我最近有一项任务来计算标准ML中的一组(由输入给出)的平均值。
这个想法是有一个像下面这样的函数,你输入一个实数列表并接收这些数字的平均值(也是一个真实数字),这样终端会在你输入函数时将它作为一个返回答案:
average = fn : real list -> real
我们在教程中也讨论了这个问题,但我想知道在Standard ML中创建这样的函数时是否存在某种技巧。
提前致谢!
答案 0 :(得分:4)
将数字相加并除以长度。简单的递归sum
通常是您在任何SML教程中看到的第一个示例之一。您需要将sum
的空列表基础案例评估为0.0
而不是0
,以确保返回类型为real
。定义sum
函数后,您可以使用average
和内置sum
函数在一行中定义length
。一个小说是SML不允许将实数除以int。您可以在将总和除以之前使用转换函数Real.fromInt
。两次通过相同的列表有一些效率低下,一次总结和一次计算它的长度,但是当你第一次学习语言时没有理由担心这些事情。
On Edit:由于您找到了一个自然的解决方案并在评论中分享了它,因此这里有一个更惯用的版本,可以计算列表中一次传递的平均值:
fun average nums =
let
fun av (s,n,[]) = s/Real.fromInt(n)
| av (s,n,x::xs) = av (s+x,n+1,xs)
in
av (0.0, 0, nums)
end;
它的工作原理是定义一个帮助函数来完成繁重的工作。它们广泛用于函数式编程。在没有可变状态的情况下,常见的技巧是明确地传递作为参数的量,这些量将由命令式语言中的相应循环连续修改。此类参数通常称为accumulators
,因为它们通常会累积增长列表,运行总和,运行产品等。s
和n
是累加器,s
总和为n
元素和(s,n,[])
列表的长度。在(s,n,x::xs)
的基础情况下,没有什么可以累积,所以返回最终答案。在非基础情况下,s
,n
和av
被适当修改,并与列表的尾部一起传递给辅助函数。 average
的定义是tail-recursive因此将以循环的速度运行而不会增加堆栈。整个let ... helper def ... in ... helper called with start-up values ...end
函数唯一需要做的就是使用适当的初始值调用辅助函数。 A = [16, 'disk', 11, 10.29, 4.63, 30.22,
79, 'table', 11, 20.49, 60.60, 20.22,
17, 'disk', 11, 22.17, 0.71, 10.37 ]
[item for item in A if not isinstance(item,str)]
是一种常见的习惯用法,用于防止程序的顶层被辅助函数混乱。
答案 1 :(得分:1)
由于只有非空列表可以有平均值,因此John Coleman的答案是:
fun average [] = NONE
| average nums =
let
fun av (s,n,[]) = s/Real.fromInt(n)
| av (s,n,x::xs) = av (s+x,n+1,xs)
in
SOME (av (0.0, 0, nums))
end;
计算平均值的函数是否应该考虑非空列表取决于您是打算导出它还是仅在您保证输入列表非空的其他地方的范围内使用它。