标准 ML:计算给定集合的平均值
Standard ML : Calculating the average of a given set
我最近有一项任务是计算标准 ML 中一组(由输入给出)的平均值。
我们的想法是拥有如下所示的函数,您可以在其中输入实数列表并接收这些数字的平均值(也是实数),以便终端将其作为 return输入函数时回答:
average = fn : real list -> real
我们也在教程中讨论过这个问题,但我想知道在标准 ML 中创建此类函数时是否有某种技巧。
提前致谢!
将数字相加并除以长度。一个简单的递归 sum
通常是您在任何 SML 教程中看到的第一个示例。您需要将 sum
的空列表基础案例计算为 0.0
而不是 0
以确保 return 类型为 real
。定义 sum
函数后,您可以使用 sum
和内置的 length
函数在 1 行中定义 average
。一个微妙之处是 SML 不允许将实数除以整数。在将总和除以它之前,您可以对长度使用转换函数 Real.fromInt
。将同一个列表传递两次,一次求和,一次计算长度,效率低下,但是当你第一次学习这门语言时,没有理由担心这些事情。
关于编辑:由于您已经找到了一个自然的解决方案并在评论中分享了它,这里有一个更惯用的版本,它计算一次遍历列表的平均值:
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
,因为它们通常会累积增长列表、运行ning 总和、运行ning 乘积等。这里 s
和 n
是累加器,s
元素的总和和 n
列表的长度。在 (s,n,[])
的基础情况下,没有更多的东西可以积累,所以最终答案是 returned。在非基础情况下,(s,n,x::xs)
、s
和 n
被适当修改并与列表的尾部一起传递给辅助函数。 av
的定义是 tail-recursive 因此将 运行 以循环的速度而不增加堆栈。整个 average
函数唯一需要做的就是使用适当的初始值调用辅助函数。 let ... helper def ... in ... helper called with start-up values ...end
是一个常用的惯用语,用于防止程序的顶层被辅助函数弄得乱七八糟。
因为只有非空列表才能有平均值,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;
计算平均值的函数是否应考虑非空列表取决于您是打算导出它还是仅在您保证其他地方输入列表非空的范围内使用它。
我最近有一项任务是计算标准 ML 中一组(由输入给出)的平均值。
我们的想法是拥有如下所示的函数,您可以在其中输入实数列表并接收这些数字的平均值(也是实数),以便终端将其作为 return输入函数时回答:
average = fn : real list -> real
我们也在教程中讨论过这个问题,但我想知道在标准 ML 中创建此类函数时是否有某种技巧。
提前致谢!
将数字相加并除以长度。一个简单的递归 sum
通常是您在任何 SML 教程中看到的第一个示例。您需要将 sum
的空列表基础案例计算为 0.0
而不是 0
以确保 return 类型为 real
。定义 sum
函数后,您可以使用 sum
和内置的 length
函数在 1 行中定义 average
。一个微妙之处是 SML 不允许将实数除以整数。在将总和除以它之前,您可以对长度使用转换函数 Real.fromInt
。将同一个列表传递两次,一次求和,一次计算长度,效率低下,但是当你第一次学习这门语言时,没有理由担心这些事情。
关于编辑:由于您已经找到了一个自然的解决方案并在评论中分享了它,这里有一个更惯用的版本,它计算一次遍历列表的平均值:
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
,因为它们通常会累积增长列表、运行ning 总和、运行ning 乘积等。这里 s
和 n
是累加器,s
元素的总和和 n
列表的长度。在 (s,n,[])
的基础情况下,没有更多的东西可以积累,所以最终答案是 returned。在非基础情况下,(s,n,x::xs)
、s
和 n
被适当修改并与列表的尾部一起传递给辅助函数。 av
的定义是 tail-recursive 因此将 运行 以循环的速度而不增加堆栈。整个 average
函数唯一需要做的就是使用适当的初始值调用辅助函数。 let ... helper def ... in ... helper called with start-up values ...end
是一个常用的惯用语,用于防止程序的顶层被辅助函数弄得乱七八糟。
因为只有非空列表才能有平均值,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;
计算平均值的函数是否应考虑非空列表取决于您是打算导出它还是仅在您保证其他地方输入列表非空的范围内使用它。