我试图定义一个简单的函数,将单个整数列表转换为一个更大的整数。例如,给定列表[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的新手,所以也许这是一个非常愚蠢的错误,但任何帮助都会非常感激!
答案 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