复杂的部分是循环。让我们开始。通常通过使用单个函数表示迭代,将循环转换为函数样式。迭代是循环变量的转换。
这是常规循环的功能实现:
loop : v -> (v -> v) -> (v -> Bool) -> v
loop init iter cond_to_cont =
if cond_to_cont init
then loop (iter init) iter cond
else init
它需要(循环变量的初始值,表示[循环变量]上的单个迭代的函数)(继续循环的条件)。
您的示例在数组上使用循环,该循环也会中断。命令式语言中的此功能已融入语言本身。在函数式编程中,通常在库级别实现这种功能。这是一个可能的实现
module Array (foldlc) where
foldlc : v -> (v -> e -> v) -> (v -> Bool) -> Array e -> v
foldlc init iter cond_to_cont arr =
loop
(init, 0)
(λ (val, next_pos) -> (iter val (at next_pos arr), next_pos + 1))
(λ (val, next_pos) -> and (cond_to_cont val) (next_pos < size arr))
在里面 :
我使用((val,next_pos))对,其中包含在外部可见的循环变量和该函数隐藏在数组中的位置。
迭代函数比常规循环中的函数稍微复杂一点,该版本使使用数组的当前元素成为可能。[采用咖喱形式。]
这些功能通常称为“折叠”。
我在名称中加上了“ l”,以表示数组元素的累加是以左关联的方式完成的;模仿命令式编程语言从低索引到高索引迭代数组的习惯。
我在名称中添加了“ c”,以指示此版本的fold带有控制是否及何时停止循环的条件。
当然,这些实用程序功能很可能在所使用的功能编程语言附带的基本库中很容易获得。我在这里写了它们进行演示。
现在,我们拥有了在必要情况下使用该语言提供的所有工具,接下来我们可以实现示例的特定功能。
循环中的变量是一对(“ answer”,一个编码是否继续的布尔值)。
iter : (Int, Bool) -> Int -> (Int, Bool)
iter (answer, cont) collection_element =
let new_answer = answer + collection_element
in case new_answer of
10 -> (new_answer, false)
150 -> (new_answer + 100, true)
_ -> (new_answer, true)
请注意,我使用了一个新的“变量”“ new_answer”。这是因为在函数式编程中,我无法更改已初始化的“变量”的值。我不担心性能,如果编译器认为效率更高,则可以通过生命周期分析将“ answer”的内存重新用于“ new_answer”。
将其纳入我们先前开发的循环函数中:
doSomeCalc :: Array Int -> Int
doSomeCalc arr = fst (Array.foldlc (0, true) iter snd arr)
此处的“数组”是导出函数foldlc的模块名称。
“ fist”,“ second”代表返回其对参数的第一,第二部分的函数
fst : (x, y) -> x
snd : (x, y) -> y
在这种情况下,“无点”样式可提高doSomeCalc实现的可读性:
doSomeCalc = Array.foldlc (0, true) iter snd >>> fst
(>>>)是函数组成: (>>>) : (a -> b) -> (b -> c) -> (a -> c)
与上面相同,只是在定义方程式的两侧都省略了“ arr”参数。
最后一件事:检查大小写(数组== null)。在设计更好的编程语言中,但即使是在某些基础学科设计不佳的语言中,人们也宁愿使用可选类型来表示不存在。这与函数编程没有太大关系,而函数编程最终是关于这个问题的,因此我不予理not。
break
并return answer
可以通过更换return
内循环。在FP中,您可以使用延期