我想对函数式编程更加熟悉,我为自己设定的第一个教育任务是将计算音频频率的程序从 C# 转换为 F#。原始应用程序的核心是一个大的“for”循环,它在一个大数组中选择一个值的子集;采用哪些值取决于最后接受的值和此后看到的值的排序列表。有一些变量在迭代之间持续存在,以跟踪确定下一个值的进度。
我第一次尝试使这个循环更“功能化”涉及一个尾递归函数,它的参数包括数组、到目前为止的结果集、最近看到的值的排名列表,以及需要在执行之间持续存在的一些其他项目。这看起来很笨拙,而且我不觉得通过将曾经是变量的所有内容都变成这个递归函数的参数,我没有得到任何好处。
函数式编程大师将如何处理此类任务?这是一种特殊情况,其中“纯”函数方法不太适合,我是否因为我觉得它们降低了我的函数的“纯度”而回避可变变量是错误的?也许它们不会降低纯度,因为它们只存在于该函数的范围内。我还没有那种感觉。
这是代码的尝试蒸馏,删除了一些“let”语句和状态的实际组件(“temp”是需要处理的中间结果数组):
let fif (_,_,_,_,fif) = fif
temp
|> Array.fold (fun (a, b, c, tentativeNextVals, acc) curVal ->
if (hasProperty curVal c) then
// do not consider current value
(a, b, c, Seq.empty, acc)
else
if (hasOtherProperty curVal b) then
// add current value to tentative list
(a, b, c, tentativeNextVals.Concat [curVal], acc)
else
// accept a new value
let newAcceptedVal = chooseNextVal (tentativeNextVals.Concat [curVal])
(newC, newB, newC, Seq.empty, acc.Concat [newAcceptedVal])
) (0,0,0,Seq.empty,Seq.empty)
|> fif