2009-10-20 70 views
4

我編寫了一個Haskell函數,用於計算給定列表中每個數字的階乘並將其打印到屏幕。Haskell的「do」問題

factPrint list = 
if null list 
    then putStrLn "" 
    else do putStrLn ((show.fact.head) list) 
     factPrint (tail list) 

該函數的工作原理,但我發現第三行有點混淆。 由於在「putStrLn」(準?)函數之前沒有「do」,編譯器(GHC)爲什麼沒有報告錯誤? 如果我從第4行省略「do」,則會按預期彈出錯誤消息。

我對Haskell及其方式相當陌生,所以請原諒我,如果我說了一些過於愚蠢的東西。

回答

11
do putStrLn ((show.fact.head) list) 
    factPrint (tail list) 

實際上是寫

putStrLn ((show.fact.head) list) >> factPrint (tail list) 

的另一種方式,這反過來意味着

putStrLn ((show.fact.head) list) >>= \_ -> factPrint (tail list) 

do符號是穿線這些單子在一起的一種方便的方式,如果沒有這種其他難看句法。

如果您在do中只有一條語句,那麼您沒有串起任何東西,而do是多餘的。

3

do關鍵字用於排序,如果每個分支是單個語句,則Haskell中的if-then-else根本不必包含do

if a 
    then b 
    else c 

您需要do你的榜樣,你是在你的else分支測序兩個操作。如果省略do則認爲factPrint(tail list)語句不是該函數的一部分,因此編譯器會在遇到意外語句時發出抱怨。

+0

'做'這裏是_不是一個函數,它是一個關鍵字。 – 2009-10-20 14:00:51

+0

同樣,如果一個then b else c和factPrint(尾部列表)不是語句,它們就是表達式。 – 2009-10-20 14:02:00

4

do用於將多個monadic表達式連接在一起。只有一個表達式後面纔有效果。

對於if是格式正確的,只需要then-clause和else-clause具有相同的類型。由於這兩個子句都有IO()這種情況。

5

如果你是新來的Haskell,認爲do類似於牙套需要在類似C語言if後:

if (condition) 
    printf("a"); // braces not required 
else { 
    printf("b"); // braces required 
    finish(); 
} 

do工作在Haskell同樣的方式。


也許這將有助於看factPrint的類型,然後重構,以使用模式匹配:

factPrint :: [Int] -> IO() 
factPrint [] = putStrLn "" 
factPrint list = do 
    putStrLn (show.fact.head) list 
    factPrint (tail list) 

所以,如果factPrint返回IO(),並且putStrLn ""類型爲IO(),然後factPrint []等於putStrLn ""是完全合法的。不需要do - 事實上,如果你不想要結尾的換行符,你可以只說factPrint [] = return()