Haskell执行序列

时间:2017-04-22 17:13:14

标签: haskell

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奇数?

3 个答案:

答案 0 :(得分:4)

此处有两种不同的功能:isOddisEven。它们是相互定义的:数字是&#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强制执行。

您的定义也不正确,但至少会以负数终止。