【发布时间】:2019-02-03 06:40:07
【问题描述】:
我正在学习使用 F# 进行函数式编程,我想编写一个为我生成序列的函数。
有一个预先确定的函数来转换一个值,在我需要编写的函数中应该有两个输入——起始值和序列的长度。序列从初始值开始,后面的每一项都是对序列中的前一个值应用变换函数的结果。
在 C# 中,我通常会这样写:
public static IEnumerable<double> GenerateSequence(double startingValue, int n)
{
double TransformValue(double x) => x * 0.9 + 2;
yield return startingValue;
var returnValue = startingValue;
for (var i = 1; i < n; i++)
{
returnValue = TransformValue(returnValue);
yield return returnValue;
}
}
当我试图将这个函数翻译成 F# 时,我做了这个:
let GenerateSequence startingValue n =
let transformValue x =
x * 0.9 + 2.0
seq {
let rec repeatableFunction value n =
if n = 1 then
transformValue value
else
repeatableFunction (transformValue value) (n-1)
yield startingValue
for i in [1..n-1] do
yield repeatableFunction startingValue i
}
这个实现有两个明显的问题。
首先是因为我试图避免创建一个可变值(类似于 C# 实现中的 returnValue 变量),所以在生成序列时我没有重用以前计算的值。这意味着对于序列的第 100 个元素,我必须另外调用 99 次 transformValue 函数,而不是只调用一次(就像我在 C# 实现中所做的那样)。这表现得非常糟糕。
第二是整个函数好像不是按照函数式编程来写的。我很确定会有更优雅和紧凑的实现。我怀疑这里应该使用Seq.fold 或List.fold 或类似的东西,但我仍然无法掌握如何有效地使用它们。
所以问题是:如何在 F# 中重写 GenerateSequence 函数,使其具有函数式编程风格并具有更好的性能?
也欢迎任何其他建议。
【问题讨论】: