在Haskell中使用嵌套的`where`子句

时间:2016-08-24 08:36:48

标签: haskell where-clause

在学习Haskell时,我要做一个函数来返回给定数字的所有整数除数。所以,我使用两个嵌套的where子句创建它,但它不起作用。

错误返回exs2.hs:49:24: Parse error in pattern: negRef / 2

divisors' :: (Integral a) => a -> [a]
divisors' x = divs x (x/2) [x]
  where
    divs ref 1 list = negDiv (-ref) (-2) ((-1):1:list)
    divs ref num list = if (mod ref num == 0) then divs ref (num-1) (num:list) else divs ref (num-1) list
      where
        negDiv negRef (negRef/2) negList = (negRef:(negRef/2):negList)
        negDiv negRef negNum negList = if (mod negRef negNum == 0) then negDiv (negNum-1) (negNum:negList) else negDiv (negNum-1) negList

那有什么不对的?它似乎很好地缩进了。

3 个答案:

答案 0 :(得分:2)

您的第二个where - 子句不使用divs范围内的任何名称。您可以像这样使用一个单独的子句:

divisors' :: (Integral a) => a -> [a]
divisors' x = divs x (x/2) [x]
  where
    divs ref 1 list = negDiv (-ref) (-2) ((-1):1:list)
    divs ref num list = if (mod ref num == 0) then divs ref (num-1) (num:list) else divs ref (num-1) list
    negDiv negRef (negRef/2) negList = (negRef:(negRef/2):negList)
    negDiv negRef negNum negList = if (mod negRef negNum == 0) then negDiv (negNum-1) (negNum:negList) else negDiv (negNum-1) negList

如果您真的想用嵌套子句表达您的函数,可以使用let ... in

但是在这种情况下这没用,我建议使用where子句(通常比let ... in更受欢迎,在大多数情况下被认为不太惯用)。

它不起作用的原因是该条款附加到divs的第二个等式,而不是第一个使用negDiv的等式。

PS:由于MathematicalOrchid saidnegRef/2不是有效模式,而是您的错误来自。

答案 1 :(得分:2)

另一个问题是/运算符不能处理整数。在Haskell中,/是字段的除法运算符,因此需要Fractional类型,例如RationalDouble

对于整数除法,您应该使用div or quot

答案 2 :(得分:1)

你有几个问题:

  1. 您只能对文字和数据构造函数进行模式匹配,而不是像/这样的任意函数。
  2. /仅针对Fractional a值定义,而不是Integral。请改用div
  3. negDiv的定义在递归调用中缺少一个参数。但不清楚论点应该是什么。
  4. 大部分修正后的版本:

    divisors' :: (Integral a) => a -> [a]
    divisors' x = divs x (x `div` 2) [x]
      where
        divs ref 1 list = negDiv (-ref) (-2) ((-1):1:list)
        divs ref num list | ref `mod` num == 0 = divs ref (num-1) (num:list) 
                          | otherwise          = divs ref (num-1) list
        -- Three arguments, but only two given to each recursive call
        negDiv x y negList | x == negRef `div` 2 = x:y:negList
                           | x `mod` y == 0      = negDiv (y-1) (y:negList)
                           | otherwise           = negDiv (y-1) negList
    

    顺便说一下,使用

    可以更简单地完成
    divisors' x = ds ++ (map negate ds)               -- positive and negative ds
                  where ds = filter (divs x) [1..x]   -- d such that d divides x
                        x `divs` y = x `mod` y == 0   -- Does y divide x?
    

    甚至

    divisors' x = [d | d <- [(-x)..x], d /= 0, x `mod` d == 0]
    

    任何时候你发现自己编写递归函数来迭代列表,你可能会忽略正确的高阶函数或列表理解。