Haskell:尝试使用两个参数定义函数时出错

时间:2017-07-31 14:22:30

标签: haskell

我试图定义一个简单的函数,将单个整数列表转换为一个更大的整数。例如,给定列表[1,5,2,0]它将返回1520。 为了在我使用的基数10中做到这一点:

calc_nr [] = 0
calc_nr (a:y) = a * 10^(length y) + (calc_nr y)

现在,我想将它扩展到不同的基础,这可以通过将表达式中的基数10幂更改为所需的基数来完成。为此,我考虑接受另一个论点b,并用基数为10的幂代替基数为b的幂 但是,当我尝试这样做时,我遇到了一些错误。写作:

calc_nr b [] = 0
calc_nr b (a:y) = a * b^(length y) + (calc_nr y)

给我错误:

* Occurs check: cannot construct the infinite type: t ~ [t]
  Expected type: [t] -> t
    Actual type: t -> [t] -> t
* Relevant bindings include
    calc_nr :: [t] -> t (bound at calc_nr.hs:39:1)

我是Haskell的新手,所以也许这是一个非常愚蠢的错误,但任何帮助都会非常感激!

1 个答案:

答案 0 :(得分:6)

首先,一些一般建议:

  • 始终为顶级函数编写类型签名。这有很多优点(稍后会详细介绍),但也许最重要的是,阅读代码的人会理解它应该做的事情。您的旧固定基础10功能将是

    fromBase10rep :: [Int] -> Int
    

    (除了Int之外,您还可以使其与其他数字类型一起使用,但我不会为了简单起见而进行此操作。)

  • 避免使用不必要的括号。

    fromBase10Rep (a:y) = a * 10 ^ length y + calc_nr y
    
  • 避免使用length并将其编入索引列表。这是低效的(每次执行时都需要遍历整个列表。)

如果你只是按照第一点,你可能会自己回答这个问题......

fromBaseRep :: Int -> [Int] -> Int
fromBaseRep b [] = 0
fromBaseRep b (a:y) = a * b ^ length y + fromBaseRep y

因为,由于类型签名,编译器现在能够提供更清晰的错误消息:

/tmp/wtmpf-file21653.hs:3:42: error:
    • Couldn't match expected type ‘Int’
                  with actual type ‘[Int] -> Int’
    • Probable cause: ‘fromBaseRep’ is applied to too few arguments
      In the second argument of ‘(+)’, namely ‘fromBaseRep y’
      In the expression: a * b ^ length y + fromBaseRep y
      In an equation for ‘fromBaseRep’:
          fromBaseRep b (a : y) = a * b ^ length y + fromBaseRep y
  |
3 | fromBaseRep b (a:y) = a * b ^ length y + fromBaseRep y
  |                                          ^^^^^^^^^^^^^

基本上它会告诉您问题的确切原因:您在递归调用中将fromBaseRep应用于过少的参数。它仍然需要知道在哪个基础上重新组合其余的数字!

所以再次传递b,你就没事了。

fromBaseRep b (a:y) = a * b ^ length y + fromBaseRep b y

正如我所说,由于length调用,这仍然是非常低效的。解决这个问题的一个好方法是将左边的数字乘以,然后再深入到列表中:

fromBaseRep b = go 0
 where go acc [] = acc
       go acc (a:y) = go (b*acc + a) y

请注意,将递归委托给本地“循环函数”go也允许我省略明确传递b - 它只是从fromBaseRep b = ...重新使用结合。

这也可以优雅地写成折叠:

fromBaseRep b = foldl' ((+) . (b*)) 0