如何在没有 if then else 语句的情况下在 haskell 中编写递归阶乘函数
How to write recursive factorial function in haskell without if then else statment
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 演算的事情。
模式匹配和守卫是两种特别直接的方式。守卫本质上是 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)
特别是对于数字,这也基本上是一个 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
的类型不是可以接受 Int
或 Integer
的函数之一! ) 这个笑话在 The Evolution of a Haskell Programmer.
中得出了合乎逻辑且令人毛骨悚然的结论
If/then/else 守卫实际上只是模式匹配的语法糖。
if b
then c
else d
脱糖为
case b of
True -> c
False -> d
同样,
f x
| b = c
| d = e
f x = g
脱糖为
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
试试这个:
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
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 演算的事情。
模式匹配和守卫是两种特别直接的方式。守卫本质上是 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)
特别是对于数字,这也基本上是一个 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
的类型不是可以接受 Int
或 Integer
的函数之一! ) 这个笑话在 The Evolution of a Haskell Programmer.
If/then/else 守卫实际上只是模式匹配的语法糖。
if b
then c
else d
脱糖为
case b of
True -> c
False -> d
同样,
f x
| b = c
| d = e
f x = g
脱糖为
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
试试这个:
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