Unk*_*own 16 f# ocaml haskell functional-programming code-snippets
有时我仍然试图将过程代码转换为功能代码.
是否有一个功能成语/片段列表映射到程序习语/片段?
编辑
由于似乎没有这些片段的集中式网站,我将其转变为社区维基.请在此粘贴任何程序 - >功能片段.
我第一次去OCaml/F#进行休息/继续,它让我投入了一个(无限的)循环,可以这么说,因为没有这样的东西存在!在OCaml中,可以使用异常来断开循环,因为它们非常便宜,但在F#中(在.NET中)开销很高,对"正常"流控制没有用.
这种情况在使用排序算法一段时间后出现(为了消磨一段时间),这会大量使用repeat/until和break.它让我觉得递归尾调用函数可以实现完全相同的结果,只有轻微的可读性.所以,我抛弃了'可变的bDone'和'而不是bDone',并尝试在没有这些命令式构造的情况下编写代码.下面只提炼出循环部分,并展示了如何使用tailcalls编写repeat/until,do/while,while/do,break/continue和test-in-middle-style代码.这些都看起来与传统的F#'while'语句完全相同,但你的里程可能会有所不同(某些平台可能无法正确实现尾调,因此可能会在修补之前叠加故障).最后是用两种样式编写的(坏)排序算法,用于比较.
让我们从一个'do/while'循环开始,用传统的F#命令式样式编写,然后查看功能变体,它们提供相同类型的循环,以及不同的语义,如while/do,repeat/until,test in the middle ,甚至打破/继续(没有monads ..嗯,工作流程!).
#light
(* something to work on... *)
let v = ref 0
let f() = incr v;
let g() = !v;
let N = 100000000
let imperDoWhile() =
let mutable x = 0
let mutable bDone = false
while not bDone do
f()
x <- x + 1
if x >= N then bDone <- true
Run Code Online (Sandbox Code Playgroud)
好的,这很容易.请记住,f()总是被调用至少一次(do/while).
这是相同的代码,但是在功能样式中.请注意,我们不需要在此声明可变性.
let funDoWhile() =
let rec loop x =
f() (*Do*)
if x < N then (*While*)
loop (x+1)
loop 0
Run Code Online (Sandbox Code Playgroud)
我们可以通过将函数调用放在if块中来将其旋转到传统的do/while.
let funWhileDo() =
let rec loop x =
if x < N then (*While*)
f() (*Do*)
loop (x+1)
loop 0
Run Code Online (Sandbox Code Playgroud)
在某些条件为真之前重复一个块怎么样(重复/直到)?够容易......
let funRepeatUntil() =
let rec loop x =
f() (*Repeat*)
if x >= N then () (*Until*)
else loop (x+1)
loop 0
Run Code Online (Sandbox Code Playgroud)
关于无monad休息的那个是什么?好吧,只需引入一个返回'unit'的条件表达式,如:
let funBreak() =
let rec loop() =
let x = g()
if x > N then () (*break*)
else
f()
loop()
loop()
Run Code Online (Sandbox Code Playgroud)
怎么样继续?好吧,这只是另一个循环调用!首先,用语法拐杖:
let funBreakContinue() =
let break' () = ()
let rec continue' () =
let x = g()
if x > N then break'()
elif x % 2 = 0 then
f(); f(); f();
continue'()
else
f()
continue'()
continue'()
Run Code Online (Sandbox Code Playgroud)
然后再没有(丑陋的)语法拐杖:
let funBreakContinue'() =
let rec loop () =
let x = g()
if x > N then ()
elif x % 2 = 0 then
f(); f(); f();
loop()
else
f()
loop ()
loop()
Run Code Online (Sandbox Code Playgroud)
非常简单!
这些循环形式的一个很好的结果是它可以更容易地在循环中发现和实现状态.例如,冒泡排序会不断循环遍历整个数组,并在找到它们时交换不合适的值.它跟踪阵列上的传递是否产生了任何交换.如果没有,则每个值必须在正确的位置,因此排序可以终止.作为优化,在每次通过数组时,数组中的最后一个值最终被分类到正确的位置.因此,每次循环可以缩短一次.大多数算法检查交换并在每次有一个"bModified"标志时更新.但是,一旦完成第一次交换,就不需要进行该分配; 它已经设置为真!
这是实现冒泡排序的F#代码(是的,冒泡排序是可怕的算法;快速排序的岩石).最后是必要的实施,不改变状态; 它为每次交换更新bModified标志.有趣的是,在小型阵列上,必要的解决方案速度更快,在大型阵列上只需要慢一两个百分点.(虽然是一个很好的例子).
let inline sort2 f i j (a:'a array) =
let i' = a.[ i ]
let j' = a.[ j ]
if f i' j' > 0 then
a.[ i ] <- j'
a.[ j ] <- i'
let bubble f (xs:'a array) =
if xs.Length = 0
then ()
let rec modified i endix =
if i = endix then
unmodified 0 (endix-1)
else
let j = i+1
sort2 f i j xs
modified j endix
and unmodified i endix =
if i = endix then
()
else
let j = i+1
let i' = xs.[ i ]
let j' = xs.[ j ]
if f i' j' > 0 then
xs.[ i ] <- j'
xs.[ j ] <- i'
modified j endix
else
unmodified j endix
in unmodified 0 (xs.Length-1)
let bubble_imperitive f (xs:'a array) =
let mutable bModified = true
let mutable endix = xs.Length - 1
while bModified do
bModified <- false
endix <- endix - 1
for i in 0..endix do
let j = i+1
let i' = xs.[ i ]
let j' = xs.[ j ]
if f i' j' > 0 then
xs.[ i ] <- j'
xs.[ j ] <- i'
bModified <- true
done
done
Run Code Online (Sandbox Code Playgroud)