isOdd, isEven :: Int -> Bool
isOdd n
| n<=0 = False
| otherwise = isEven (n-1)
isEven n
| n<0 = False
| n==0 = True
| otherwise = isOdd (n-1)
我无法理解这段代码,在isOdd中,它只定义了什么是False,然后切换到isEven,那么haskell怎么知道什么时候是n奇数?
答案 0 :(得分:4)
此处有两种不同的功能:isOdd
和isEven
。它们是相互定义的:数字是&#34;不是奇数&#34;如果它是负数或零,&#34;奇数&#34;如果一个小于那个数字是&#34;偶数&#34;。一个数字是&#34;甚至不是&#34;如果它是否定的,&#34;甚至&#34;如果它是零或一个小于那个数字是&#34;奇数&#34;。
这是定义这些函数的一种相当不直观的方法,但它与&#34;执行序列&#34;无关。或&#34;评估顺序&#34;。实际上,只要Haskell编译器提供正确的值作为结果并且遵循Haskell报告中指定的延迟/严格语义,就允许Haskell编译器执行他们想要的任何计算。
这些功能的更好实现如下:(来自Prelude)
even, odd :: (Integral a) => a -> Bool
even n = n `rem` 2 == 0
odd = not . even
换句话说,即使将2除以2的余数为0,也就是整数式的东西,如果不是偶数则为奇数。
注意:上面链接中的INLINEABLE
个pragams只是一个优化,可以忽略。
答案 1 :(得分:2)
这些函数是相互递归(每个函数可以调用另一个函数),具有基本情况。让我们使用isOdd
进行示例评估。首先,我将首先将警卫更改为等效if
s,以便(希望)更清晰地回答这个问题(尽管我通常建议使用警卫)。
isOdd, isEven :: Int -> Bool
isOdd n =
if n <= 0
then False
else isEven (n-1)
isEven n =
if n < 0
then False
else if n == 0
then True
else isOdd (n-1)
现在,我们可以尝试逐步完成示例评估 [1] :
isOdd 3
==> if 3 <= 0 (Applying isOdd to 5 and expanding the body)
then False
else isEven (3-1)
==> isEven (3-1) (3 > 0)
==> if 2 < 0
then False
else if 2 == 0
then True
else isOdd (2-1)
==> isOdd (2-1) (4 > 0, so the first two branches aren't taken)
==> if 1 <= 0 (Applying isOdd to 1 and expanding the body)
then False
else isEven (1-1)
==> isEven 0
==> if 0 < 0
then False
else if 0 == 0
then True
else isOdd (0-1)
==> True (0 == 0, so the second branch is taken)
这些函数为什么起作用的直觉是这样的:如果非负整数(自然数)n
大于0
,如果它的前身(n-1
)是奇数是偶数,即使它的前身是奇怪的。这是正确的,因为偶数和奇数交替。
我建议您在使用这样一个小例子时遇到一个你不理解的函数(或者,在这种情况下,一对函数)时单步执行评估。
[1] :请注意对于这个问题并不重要的事情:当形状x-1
的表达式减少到时,我略微简化了相应的号码。
答案 2 :(得分:0)
这被称为&#34;相互递归&#34;或者&#34;相互递归的函数&#34;,就像你需要定义终端状态(或退出条件)的递归函数一样。但是,你的定义并不是最好的,这里有一个更好的选择
isEven,isOdd :: Int -> Bool
isEven 0 = True
isEven n = isOdd (n - 1)
isOdd 0 = False
isOdd n = isEven (n - 1)
这里终端条件设置为0
(对称),并且相互递归最终会在其中一个上结束。
请注意,这仅针对非负整数定义,但未使用类型Int
强制执行。
您的定义也不正确,但至少会以负数终止。