这是一个问题:
“写一个计算的函数 列表的平均值,即所有的总和 列表中的元素除以其 长度。 (您可能需要使用 fromInteral功能转换 整数列表的长度 到浮点数。)“
首先我尝试了这个:
mean :: [Double] -> Double
mean [] = 0
mean (x:xs) = (x + mean xs) / (1 + mean xs)
但它给了我奇怪的结果,例如,当我这样使用它时:
mean [2,4,6]
它给我的结果:1.41176
它应该在哪里:4
为什么?
我尝试了另一件事:
mean :: [Double] -> Double
mean list = (summ list) / (count list)
where count [] = 0
count (x:xs) = 1 + count xs
summ [] = 0
summ (x:xs) = x + summ xs
但是当我尝试将文件加载到GHC时出现错误 错误是:
parse error on input 'count'
Failed, modules loaded: none
再次,我做错了什么?
最后,我尝试了这个(成功):
mean :: [Double] -> Double
count [] = 0
count (x:xs) = 1 + count xs
summ [] = 0
summ (x:xs) = x + summ xs
mean list = summ list / count list
它与上面的那个相同(使用'where'关键字),但它只在这里成功,而不是在上面的那个。
为什么呢?
非常感谢。
P.S。
我正在从这本书中学习 - 真实世界哈斯克尔
练习来自here - (向下滚动: - ))
但我仍然不明白为什么第一个不起作用。 我认为它应该是那样的
(2 + mean [4,6]) / (1 + mean [4,6])
(4 + mean [6 ]) / (1 + mean [ 6])
(6 + mean [ ]) / (1 + mean [ ])
所以现在就像那样
(6 + 0 ) / (1 + 0 ) -- last recursion
(4 + (6 + 0) ) / (1 + (1 + 0) )
(2 + (4 + (6 + 0))) / (1 + (1 + (1 + 0))
现在应该是:12/3
不是吗?
我不明白的是什么?
谢谢你: - )。
答案 0 :(得分:5)
您第一次尝试时得到的答案错误,因为您使用的公式不正确。垃圾进垃圾出。 (其他人已经介绍了这一点。)
您可能会收到一个解析错误,因为有些行正在使用空格而其他行正在使用制表符。或者您正在使用所有选项卡,但使用非标准选项卡宽度。
此处未使用或不需要缩进,因此不会出现空格-v-制表符问题。
答案 1 :(得分:2)
您的第一次尝试评估如下:
6 / 1
4 + 6 / 1 + 6
2 + (10/7) / 1 + (10/7)
这不是你想要的。
第二次尝试没问题。
答案 2 :(得分:2)
正确:
import Data.List (foldl')
mean :: Fractional a => [a] -> a
mean = uncurry (/) . foldl' (\(s, n) x -> (s + x, n + 1)) (0, 0)
mean [2,4,6] = uncurry (/) $ foldl' (...) (0, 0) [2,4,6]
= uncurry (/) $ foldl' (...) (2, 1) [4,6]
= uncurry (/) $ foldl' (...) (6, 2) [6]
= uncurry (/) $ foldl' (...) (12, 3) []
= uncurry (/) (12, 3)
= 12 / 3
= 4
不正确:
mean [] = 0
mean (x:xs) = (x + mean xs) / (1 + mean xs)
mean [6] = mean (6 : [])
= (6 + mean []) / (1 + mean [])
= (6 + 0) / (1 + 0)
= 6
mean [4,6] = mean (4 : [6])
= (4 + mean [6]) / (1 + mean [6])
= (4 + 6) / (1 + 6)
= 10/7
mean [2,4,6] = mean (2 : [4,6])
= (2 + mean [4,6]) + (1 + mean [4,6])
= (2 + 10/7) / (1 + 10/7)
= 24/17
答案 3 :(得分:2)
说我们定义
naivemean l = sum l / fromIntegral (length l)
它有一些严重的限制。首先,该定义不包括Int
:
*Main> naivemean ([1] :: [Int])
<interactive>:1:0:
No instance for (Fractional Int)
arising from a use of `naivemean' at <interactive>:1:0-21
Possible fix: add an instance declaration for (Fractional Int)
In the expression: naivemean ([1] :: [Int])
In the definition of `it': it = naivemean ([1] :: [Int])
其次,它为大型列表打击了堆栈:
*Main> naivemean [1..1000000]
*** Exception: stack overflow
此外,当单次传递时,它会在列表中进行两次传递,一次用于sum
,另一次用于length
。
我们可以用
纠正所有这三个问题{-# LANGUAGE BangPatterns #-}
mean :: (Real a, Fractional b) => [a] -> b
mean = go (toRational 0) 0
where
go !s !l [] = fromRational s / fromIntegral l
go s l (x:xs) = go (s+.x) (l+1) xs
s +. x = s + toRational x
Bang patterns强制严格评估标记的参数。如果没有刘海,上面的代码也会在给出一个长列表时吹掉堆栈,但出于不同的原因:例如l
的惰性求值会生成一个长的未评估的表单链
0 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + ...
在这种情况下,延迟评估以分配所有暂停计算的形式创建更多工作,而不是在每次迭代时简单地递增计数器。
作为初学者,fromRational
和toRational
也可能令人困惑。考虑除法函数的类型:
*Main> :t (/)
(/) :: (Fractional a) => a -> a -> a
这意味着为同一类型的Fractional实例的任何两个值定义了除法。 Int
不是以下类型之一:
*Main> (1::Int) / (2::Int)
<interactive>:1:0:
No instance for (Fractional Int)
arising from a use of `/' at <interactive>:1:0-18
Possible fix: add an instance declaration for (Fractional Int)
In the expression: (1 :: Int) / (2 :: Int)
In the definition of `it': it = (1 :: Int) / (2 :: Int)
mean
的一个定义应涵盖[Int]
,[Float]
和[Double]
的所有内容,但如果没有有理位(且没有类型注释),则推断类型对于mean
是
*Main> :t mean
mean :: [Double] -> Double
因为除以列表的长度。
事实证明Int
,Float
和Double
是类型类Real
的实例,任何Real
都可以转换为{{1} }}
Rational
和*Main> :t toRational
toRational :: (Real a) => a -> Rational
可能会转换为Rational
:
Fractional
最后,对于大型列表,我们也有可能溢出机器的双倍,但*Main> :t fromRational
fromRational :: (Fractional a) => Rational -> a
给我们任意精度。
如果您具有C或Java等语言背景,可以自动提升类型以处理这些情况,那么您会发现Haskell类型系统的这种特殊不灵活性令人困惑和令人沮丧。我担心你只需要学会处理它。
完成所有这些后,我们现在可以
Rational
或
*Main> mean ([1..1000000] :: [Int])
500000.5
答案 4 :(得分:1)
警告:未经测试的代码。在你对mean
的定义中,你需要准确地携带运行总和和运行长度,正如其他人所指出的那样,你不是那样做的。以下内容应该有效:
mean0 sum len [] = sum / len
mean0 sum len (x:xs) = mean0 (sum+x) (len+1) xs
此定义传递两个 accumulators ,分别用于运行总计和运行计数,当您沿列表递归时更新。当函数最终耗尽要处理的列表(基本情况)时,它只进行所需的计算。
要使用mean0
,您只需编写
mean0 0 0 [2, 4, 6]
正如您所看到的,为累加器提供初始值有点烦人,因此提供类似
的包装器是很常见的。mean xs = mean0 0 0 xs
现在,你就像你想要的那样写mean [2, 4, 6]
。
答案 5 :(得分:1)
Haskell:最美丽的命令式语言
import Control.Monad.ST
import Data.STRef
import Control.Monad
mean xs = s / l
where (s,l) = runST $ do{
acc <- newSTRef (0,0);
forM_ xs $ \x -> do{
modifySTRef acc $ \(a,b) -> (x+a,1+b)
};
readSTRef acc }
答案 6 :(得分:0)
表示x = sum(x)/ fromIntegral(length(x))
表示[2.0,4.0,6.0]
当然,这必须改进(空列表,适用于双打......)。