2010-01-06 17 views
11

Ben haskell için yeni ve yol boyunca birkaç şey denemek, Learn You A Haskell For Great Good sindirilen ve sindirilir. İlk projem için klasik denemek istedim: FizzBuzz. Yani aşağıdaki kodla geldi: i okumak zordu oldukça yoğun görünümlü listesi var haricindeHaskell - güzel baskı ile sorun bir liste

import System.IO 

fizzBuzz :: (Integral a) => a -> String 
fizzBuzz num 
    | fizz && buzz = "FizzBuzz" 
    | fizz = "Fizz" 
    | buzz = "Buzz" 
    | otherwise = show num 
    where fizz = num `mod` 3 == 0 
      buzz = num `mod` 5 == 0 

main = print $ map fizzBuzz [1..100] 

, büyük çalıştı.

main = map putStrLn $ map fizzBuzz [1..100] 

Ve bu bana hata Couldn't match expected type 'IO t' against inferred type '[IO()]' veriyor: Yani ben onun yerine bu ana işlevi çalıştı. Yarım düzine şey denedim ve hiçbiri yardım etmedi. Ne yapmaya çalıştığımı yapmanın doğru yolu nedir?

cevap

26
map :: (a -> b) -> [a] -> [b] 
putStrLn :: Show a => a -> IO() 
map putStrLn :: Show a => [a] -> [IO()] 

IO() eylemlerinizin bir listesi var.

main :: IO() 

Tek IO() eyleme onlara katılmak gerekir. Yapmak istediğiniz ne

sequence/sequence_ bu IO() işlemlerin her birini gerçekleştirmek için geçerli:

kolaylık sağlamak için
sequence :: Monad m => [m a] -> m [a] 
sequence_ :: Monad m => [m a] -> m() 

, mapM/mapM_ listesi ve sekans çıkan monadic sonuçları üzerinde bir işlev eşler.

mapM :: Monad m => (a -> m b) -> [a] -> m [b] 
mapM_ :: Monad m => (a -> m b) -> [a] -> m() 

Yani sabit kod şu şekilde görünecektir:

main = mapM_ putStrLn $ map fizzBuzz [1..100] 

Muhtemelen bu gibi yazmak isterim rağmen:

main = mapM_ (putStrLn . fizzBuzz) [1..100] 

Hatta bu:

main = putStr $ unlines $ map fizzBuzz [1..100] 

Kendi sequence ürünümüzü yazalım. Ne yapmasını istiyoruz?

sequence [] = return [] 
sequence (m:ms) = do 
    x <- m 
    xs <- sequence ms 
    return $ x:xs 
  • listede sol şey yoksa, iade (monadın içine enjekte) sonuçlarının boş listesi.
  • Aksi takdirde, monadın içinde
    • bağlama ilk sonucu (IO monadın için, bu yürütme anlamına gelir). Listenin geri kalanı için
    • sequence; sonuç listesini ekle.
    • İlk sonuca ve diğer sonuçların listesine bir kez geri dönün.

ghc kütüphanesi daha foldr (liftM2 (:)) (return []) gibi bir şey kullanır ama bu yeni gelen açıklamak zordur; Şimdilik, sadece benim sözüm ona eşdeğer olsun.Sonuçların kaydını tutmakla uğraşmadığı için,

sequence_ daha kolaydır. GHC'nin kütüphanesi, sequence_ ms = foldr (>>) (return()) ms olarak uygular. "Sonucunu atmak a yoktur; b yapmak; hellip & sonucu atmak; nihayet, () dönüş"

başka deyişle
sequence [a, b, c, d] 
= foldr (>>) (return()) [a, b, c, d] 
= a >> (b >> (c >> (d >> return()))) 

: Sadece foldr tanımını genişletmek edelim. Öte yandan


mapM f xs = sequence $ map f xs 
mapM_ f xs = sequence_ $ map f xs 

, hatta alternatif unlines çözeltisi ile hiç monads bilmek gerekmez.

unlines ne yapar? Eh, lines "a\nb\nc\nd\n" = ["a", "b", "c", "d"], tabiki unlines ["a", "b", "c", "d"] = "a\nb\nc\nd\n".

= unlines ["1", "2", "Fizz", ..] = "1\n2\nFizz\n..." = ve putStr'a gider. Haskell'in tembellik büyüsü sayesinde, tam dizinin bellekte yapılması gerekmiyor, bu yüzden bu mutlu bir şekilde [1..1000000] ya da daha yükseğine gidecek :)

+0

Teşekkür ederim, haskell'in böyle bir şey olduğunu biliyordum ama anlayamadım dışarı! şimdi anladım * neden * işe yarıyor ... :) – RCIX

+0

Açıklamaya çalıştığım gibi, “IO()” eylemlerinin bir listesini oluşturdunuz, ama * ne * ile ne yapmak istediğinizi söylemelisiniz. 'sequence', burada istediğiniz gibi olan sırayla monadların bir listesini çalıştırır. Son çözümüm onun yerine, fizik/vızıltı dizelerini “IO” nun garip ülkesine girmeden önce tek bir çok satırlı dizeye katıyor. – ephemient

+0

Evet, sadece bir monad ve IO eylemi arasındaki bağlantıyı yapmadım - bu öğreticiden bazıları hala biraz bulanık – RCIX