Sizin ikinci, belirli, sorun fonksiyonların türleriyle olduğunu. Ancak, ilk sayınız (gerçekten bir şey değil), getFileNameAndSize
numaralı telefondan do
ifadesidir. Monolar ile do
kullanılırken, monadik bir panacea değildir; aslında some simple translation rules olarak uygulanmaktadır. (tam sağ, hata işleme ilgili bazı detaylar sayesinde değil ama yeterince yakın) Cliff'in Notlar sürümü:
do a
≡ a
do a ; b ; c ...
≡ a >> do b ; c ...
do x <- a ; b ; c ...
≡ a >>= \x -> do b ; c ...
başka bir deyişle
, getFileNameAndSize
olmadan sürümü eşdeğerdirbloğu ve böylece do
'dan kurtulabilirsiniz. Bu Bunun için türünü bulabilirsiniz
getFileNameAndSize fname = (fname, withFile fname ReadMode hFileSize)
ile yaprakları: fname
withFile
ilk argüman olduğundan, bu FilePath
tip vardır; ve hFileSize
, bir IO Integer
döndürür, bu nedenle withFile ...
türüdür. Böylece, getFileNameAndSize :: FilePath -> (FilePath, IO Integer)
var. İstediğiniz bu olabilir veya olmayabilir; Bunun yerine FilePath -> IO (FilePath,Integer)
'u isteyebilirsiniz.Bunu değiştirmek için,
getFileNameAndSize_do fname = do size <- withFile fname ReadMode hFileSize
return (fname, size)
getFileNameAndSize_fmap fname = fmap ((,) fname) $
withFile fname ReadMode hFileSize
-- With `import Control.Applicative ((<$>))`, which is a synonym for fmap.
getFileNameAndSize_fmap2 fname = ((,) fname)
<$> withFile fname ReadMode hFileSize
-- With {-# LANGUAGE TupleSections #-} at the top of the file
getFileNameAndSize_ts fname = (fname,) <$> withFile fname ReadMode hFileSize
Sonraki herhangi yazabilir, KennyTM belirttiği gibi, sen fileNames <- getDirectoryContents
var; getDirectoryContents
, FilePath -> IO FilePath
türüne sahip olduğundan, bir argüman vermeniz gerekir. (, ör.getFilesWithSizes dir = do fileNames <- getDirectoryContents dir ...
). Bu muhtemelen sadece basit bir gözetimdir.
Mext, hatanın kalbine geldik: files <- (mapM getFileNameAndSize fileNames)
. Size neden kesin bir hata verdiğini bilmiyorum, ama neyin yanlış olduğunu söyleyebilirim. getFileNameAndSize
ile ilgili bildiklerimizi hatırlayın. Kodunuzda (FilePath, IO Integer)
döndürür. Ancak, mapM
, Monad m => (a -> m b) -> [a] -> m [b]
tipindedir ve mapM getFileNameAndSize
yanlış yazılmıştır. Yukarıda uygulamış olduğum gibi getFileNameAndSize :: FilePath -> IO (FilePath,Integer)
. Son satırınızı düzeltmemiz gerekiyor. Son olarak, son satırınızı düzeltmemiz gerekiyor. Her şeyden önce, bize vermese de, cmpFilesBySize
, muhtemelen ikinci elemanla kıyaslandığında, (FilePath, Integer) -> (FilePath, Integer) -> Ordering
tipinin bir fonksiyonudur. Bununla birlikte, bu gerçekten basit: Data.Ord.comparing :: Ord a => (b -> a) -> b -> b -> Ordering
kullanarak, Ord b => (a, b) -> (a, b) -> Ordering
türünde bu comparing snd
yazabilirsiniz. İkincisi, sonuçlarınızı IO monadına sarılmış bir sonuç olarak değil, basit bir liste olarak geri göndermeniz gerekir; return :: Monad m => a -> m a
işlevi hile yapacaktır.
Böylece hep birlikte bu koyarak, sen Bunların hepsi iyi ve güzel
import System.IO (FilePath, withFile, IOMode(ReadMode), hFileSize)
import System.Directory (getDirectoryContents)
import Control.Applicative ((<$>))
import Data.List (sortBy)
import Data.Ord (comparing)
getFileNameAndSize :: FilePath -> IO (FilePath, Integer)
getFileNameAndSize fname = ((,) fname) <$> withFile fname ReadMode hFileSize
getFilesWithSizes :: FilePath -> IO [(FilePath,Integer)]
getFilesWithSizes dir = do fileNames <- getDirectoryContents dir
files <- mapM getFileNameAndSize fileNames
return $ sortBy (comparing snd) files
alırsınız ve iyi çalışır. Ancak, biraz farklı yazabilirim. Benim versiyonu muhtemelen bu şekilde görünecektir:
{-# LANGUAGE TupleSections #-}
import System.IO (FilePath, withFile, IOMode(ReadMode), hFileSize)
import System.Directory (getDirectoryContents)
import Control.Applicative ((<$>))
import Control.Monad ((<=<))
import Data.List (sortBy)
import Data.Ord (comparing)
preservingF :: Functor f => (a -> f b) -> a -> f (a,b)
preservingF f x = (x,) <$> f x
-- Or liftM2 (<$>) (,), but I am not entirely sure why.
fileSize :: FilePath -> IO Integer
fileSize fname = withFile fname ReadMode hFileSize
getFilesWithSizes :: FilePath -> IO [(FilePath,Integer)]
getFilesWithSizes = return . sortBy (comparing snd)
<=< mapM (preservingF fileSize)
<=< getDirectoryContents
(<=<
.
ait monadic eşdeğer, fonksiyon kompozisyon operatörüdür.) İlk kapalı: evet, benim sürümü daha uzundur. Ancak, ben zaten preservingF
yapım yerde tanımlanmış olurdu uzunluğunda eşdeğer iki. * (Olabilir hatta inline fileSize
başka bir yerde kullanılan olmasaydı.) Bir arada daha basit saf fonksiyonları zincirleme içerir çünkü İkincisi, Bu versiyonundan daha ister zaten yazdık. Senin versiyonun benzer olsa da, benim (kendimi) hissediyorum daha akıcı ve bu şeylerin daha net olmasını sağlar.
Bu, bu şeylerin nasıl yapılandırılacağı konusundaki ilk sorunuzun bir cevabıdır. Ben şahsen doğrudan dış dünya (örneğinmain
ve bir dosya ile etkileşim şey) bir IO
olsun dokunmak gerek mümkün salt fonksiyonları olduğunca az fonksiyonlarına aşağı benim IO kilitlemek eğilimindedir. Diğer her şey sıradan bir saf işlevdir (ve preservingF
satırları boyunca genel nedenlerden dolayı monadik ise sadece monadiktir). Sonra şeyler düzenlemek böylece main
vb sadece kompozisyonlar ve saf fonksiyonların zincirleri şunlardır: main
IO
.arazi gelen bazı değerleri alır; Daha sonra, tarihi katlamak, iş mili haline getirmek ve mutlaklaştırmak için saf işlevleri çağırır; o zaman daha fazla IO
değerini alır; o zaman daha fazla çalışır; Buradaki fikir, iki alanı mümkün olduğunca ayırmaktır, böylece IO
kodunun daha fazla kompozisyonu her zaman ücretsizdir ve siyah kutu IO
sadece gerekli olduğu yerde tam olarak yapılır. onlar normal fonksiyonları üzerinde faaliyet tıpkı (örneğin IO
-Dünya gibi) monadic değerlerle etkileşim fonksiyonları üzerinde işlem izin olarak <=<
gibi
Operatörler gerçekten bu tarzda yazı kodu ile yardımcı olur.Ayrıca, herhangi bir sayıda monoya (gerçekten Applicative
) bağımsız değişkene normal işlevler uygulamanıza olanak veren Control.Applicative'sfunction <$> liftedArg1 <*> liftedArg2 <*> ...
notasyonuna bakmalısınız. Bu sahte <-
s kurtulmak için gerçekten güzel ve monadik kod üzerinde sadece saf işlevleri zincirleme.
*: preservingF
veya en azından kardeşinin preserving :: (a -> b) -> a -> (a,b)
gibi bir paket içinde olması gerektiğini hissediyorum, ancak bulamadım da.
Teşekkürler, büyük bir cevap, hala anlıyorum, çok azını anlıyorum çünkü% 40'ını anlıyorum, ama sorunu çözdü;) – Drakosha
Yardım edebilirim. Özellikle anlamadığınız bir şey var mı? Sonuna doğru bir çok şey, bilmeniz gereken bir şey olarak değil, bakmak/öğrenmek istediğinizi düşündüğünüz bir şey. –