这是个问题:
“编写一个计算列表均值的函数,即列表中所有元素的总和除以其长度 . (您可能需要使用fromIntegral函数将列表的长度从整数转换为浮点数数 . )”
首先我试过这个:
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
它's the same as the above one (with the '在哪里'关键字',但它只在这里成功,而不是在上面的那个 .
为什么?
非常感谢 .
附:
我正在从这本书中学习 - 真实世界Haskell和练习来自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
不是吗?
我不明白的是什么?
谢谢 :-) .
7 回答
您第一次尝试得到的答案错误,因为您使用的公式不正确 . 垃圾进垃圾出 . (其他人已经介绍过这个 . )
您可能会收到解析错误,因为某些行正在使用空格而其他行正在使用制表符 . 或者您正在使用所有选项卡,但使用非标准选项卡宽度 .
此处未使用或不需要缩进,因此不会出现空格-v-制表符问题 .
您的第一次尝试评估如下:
这不是你想要的 .
第二次尝试没问题 .
正确:
不正确:
说我们定义
它有一些严重的局限性 . 首先,该定义不包括
Int
的列表:其次,它为大型列表打击了堆栈:
此外,当单次传球时,它会在列表上进行两次传递,一次用于
sum
,一次用于length
.我们可以纠正所有这三个问题
Bang patterns强制严格评估标记的参数 . 如果没有刘海,上面的代码也会在给出一个长列表的情况下打击堆栈,但出于不同的原因:例如
l
的懒惰评估会生成一个长期未评估的表单链在这种情况下,延迟评估以分配所有暂停计算的形式创建更多工作,而不是在每次迭代时简单地递增计数器 .
作为初学者,
fromRational
和toRational
也可能令人困惑 . 考虑除法函数的类型:这意味着为同一类型的任何两个值定义了除法,这两个值是Fractional的实例 .
Int
不是以下类型之一:mean
的一个定义应该涵盖[Int]
,[Float]
和[Double]
的所有内容,但如果没有有理位(并且没有类型注释),mean
的推断类型是因为按列表的长度划分 .
事实证明,
Int
,Float
和Double
是类型类Real的实例,任何Real
都可以转换为Rational
和
Rational
可能会转换为Fractional
:最后,对于大型列表,我们也有机会溢出机器的双倍,但
Rational
给我们任意精度 .如果您具有C或Java等语言的背景,可以自动提升类型来处理这些情况,那么您会发现Haskell类型系统的这种特殊缺陷令人困惑和令人沮丧 . 我担心你只需要学会处理它 .
完成所有这些,我们现在可以
要么
警告:未经测试的代码 . 在
mean
的定义中,你需要准确地携带运行总和和运行长度,正如其他人所指出的那样,你不是那样做的 . 像下面这样的东西应该工作:此定义传递两个累加器,每个累加器用于运行总计和运行计数,在您沿列表递归时更新 . 当函数最终耗尽要处理的列表(基本情况)时,它就会完成所需的操作计算 .
要使用
mean0
,您只需编写正如您所看到的,为累加器提供初始值有点烦人,因此提供类似包装器是很常见的 .
现在,你就像你想要的那样写
mean [2, 4, 6]
.哈斯克尔:最美丽的命令式语言
设意味着x = sum(x)/ fromIntegral(length(x))
平均值[2.0,4.0,6.0]
当然,这必须改进(空列表,确实适用于双打......) .