我正在和初学者Haskell一起玩,我想写一个普通的函数.这似乎是世界上最简单的事情,对吧?
错误.
似乎Haskell的类型系统禁止平均处理泛型数字类型 - 我可以使它在Integrals列表或Fractionals列表上工作,但不能同时工作.
我想要:
average :: (Num a, Fractional b) => [a] -> b
average xs = ...
Run Code Online (Sandbox Code Playgroud)
但我只能得到:
averageInt :: (Integral a, Fractional b) => [a] -> b
averageInt xs = fromIntegral (sum xs) / fromIntegral (length xs)
Run Code Online (Sandbox Code Playgroud)
要么
averageFrac :: (Fractional a) => [a] -> a
averageFrac xs = sum xs / fromIntegral (length xs)
Run Code Online (Sandbox Code Playgroud)
而第二个似乎工作.直到我尝试传递变量.
*Main> averageFrac [1,2,3]
2.0
*Main> let x = [1,2,3]
*Main> :t x
x :: [Integer]
*Main> averageFrac x
<interactive>:1:0:
No …Run Code Online (Sandbox Code Playgroud) 我有这个相当简单的函数来计算大列表元素的平均值,使用两个累加器来保存到目前为止的总和以及到目前为止的计数:
mean = go 0 0
where
go s l [] = s / fromIntegral l
go s l (x:xs) = go (s+x) (l+1) xs
main = do
putStrLn (show (mean [0..10000000]))
Run Code Online (Sandbox Code Playgroud)
现在,用严格的语言,这将是尾递归,并且没有问题.然而,由于Haskell很懒,我的谷歌搜索让我明白(s + x)和(l + 1)将作为thunk传递递归.所以整件事崩溃和烧伤:
Stack space overflow: current size 8388608 bytes.
Run Code Online (Sandbox Code Playgroud)
进一步的谷歌搜索后,我发现seq和$!.这似乎我不理解,因为我在这种情况下使用它们的所有尝试都证明是徒劳的,错误信息说的是关于无限类型的东西.
最后我发现-XBangPatterns,它通过改变递归调用来解决所有问题:
go !s !l (x:xs) = go (s+x) (l+1) xs
Run Code Online (Sandbox Code Playgroud)
但我对此并不满意,因为-XBangPatterns目前这是一个扩展.我想知道如何在不使用的情况下严格评估-XBangPatterns.(也许还可以学到一些东西!)
只是让你理解我缺乏理解,这就是我尝试过的(编译的唯一尝试,即):
go s l (x:xs) = go (seq s (s+x)) (seq l (l+1)) xs …Run Code Online (Sandbox Code Playgroud)