作者:手机用户2502875333 | 来源:互联网 | 2023-01-27 14:15
我正在尝试定义一个简单的函数,将单个整数列表转换为一个更大的整数.例如,给定列表[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> leftaroundab..:
首先,一些一般建议:
始终为顶级函数编写类型签名.这有很多优点(稍后会详细介绍),但也许最重要的是,阅读代码的人会理解它应该做什么.你的旧固定基础功能将是
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