2017-09-19 53 views
1
fac n = if n < 2 then 1 else n * fac (n-1) 

main = do 

    putStrLn "Enter a number: " 
    number <- getLine 
    print $ number >>= fac 

我不知道如何編寫沒有if語句的遞歸階乘函數。我們的教授說了一些關於lambda微積分的話。如何在haskell中編寫遞歸階乘函數而沒有if else語句

+2

https://en.wikibooks.org/wiki/Haskell/Pattern_matching – Ryan

+0

[舌在臉頰答案](https://www.willamette.edu/~fruehr/haskell/ evolution.html) – gallais

回答

5

模式匹配和警衛是兩種特別直接的方式。警衛本質上是if-then-else的另一種語法;它們看起來像這樣:

fac n | n < 2  = 1 
     | otherwise = n * fac (n-1) 

與if-then-else不同,它們乾淨地支持多種條件;例如,人們也可以編寫,例如,

fac n | n < 0 = error "nah" 
     | n == 0 = 1 
     | n == 1 = 1 
     | n > 1 = n * fac (n-1) 

這在if-then-else形式中看起來會少得多。

隨着模式匹配,一個通常寫入多個定義方程:

fac 0 = 1 
fac 1 = 1 
fac n = n * fac (n-1) 

特別是對於數字,這也desugars基本上一個if-then-else的;但對於編譯器集成較少的數據類型,通常不能用if-then-else來模擬,而且通常也會導致非常自然的代碼。

另一個非常不錯的方法是將您的遞歸推送到現有的Prelude函數中;您在實踐中發現迭代模式的次數越多,您可以通過不重複實施相同的循環來避免更多的錯誤。對於這一個,你可以使用product和特殊語法枚舉:

fac n = product [1..n] 

更先進的(和顯著惡化)技術是定義一個新的號碼;例如教會數字允許數字的生產者驅動遞歸,消費者(這裏,fac)只提供基本情況。在這種風格,你可能會看到這樣的事情:

fac n = fst (n (1,1) (\(prod, sum) -> (prod*sum, sum+1))) 

(但注意哦,這需要一種非常特殊的號碼 - 肯定的fac類型是不是可以接受的功能之一IntInteger !)這個笑話在The Evolution of a Haskell Programmer被採取了它的邏輯和令人震驚的結論。

+0

不要忘記'(==)'中的'Bool'上的模式匹配,這就是守衛和if/then/else都是糖的原因。(Haskell可能有太多特殊的方法來處理布爾) – Carl

1

如果/ then/else和守衛實際上只是模式匹配的語法糖。

if b 
    then c 
    else d 

desugars到

case b of 
    True -> c 
    False -> d 

同樣,

f x 
    | b = c 
    | d = e 
f x = g 

desugars到

f x = case b of 
     True -> c 
     False -> case d of 
      True -> e 
      False = g 

所以,你總是可以直接使用case。但是,您可以手動執行相當簡單的優化。如果你看到

case x == p of 
    True -> a 
    False -> b 

其中p由構造和文字,你可以用

case x of 
    p -> a 
    _ -> b 
1

更換整個事情試試這個:

factorial 0 = 1 
factorial n = n * factorial (n - 1) 

使用尾遞歸:

factorial n = f n 1 

f 0 acc = acc 
f n acc = f (n-1) (acc*n) 

main = print $ factorial 5 

輸出:

120