程序员程序员的功能代码片段列表?

时间:2009-05-07 01:49:55

标签: haskell f# ocaml functional-programming code-snippets

有时我仍然试图将程序代码转换为功能代码。

是否有映射到程序习语/片段的功能习语/片段列表?

修改

由于似乎没有这些片段的集中式网站,我将其转变为社区维基。请粘贴任何程序 - >功能片段在这里。

5 个答案:

答案 0 :(得分:9)

(编辑自this post上的fshub

我第一次在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在中间,甚至打破/继续(没有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

好的,这很容易。请记住,f()总是被调用至少一次(do / while)。

这是相同的代码,但是在功能样式中。请注意,我们不需要在此处声明可变。

let funDoWhile() =
    let rec loop x =
        f()             (*Do*)
        if x < N then   (*While*)
            loop (x+1)
    loop 0

我们可以通过将函数调用放在if块中来将它旋转到传统的do / while。

let funWhileDo() =
    let rec loop x =
        if x < N then   (*While*)
            f()         (*Do*)
            loop (x+1)
    loop 0

在某些条件成立之前重复一个块怎么样(重复/直到)?很容易......

let funRepeatUntil() =
    let rec loop x =
        f()                 (*Repeat*)
        if x >= N then ()   (*Until*)
        else loop (x+1)
    loop 0

关于无monad休息的那是什么?好吧,只需引入一个返回'unit'的条件表达式,如:

let funBreak() =
    let rec loop() =
        let x = g()
        if x > N then ()    (*break*)
        else
            f()
            loop()
    loop()

继续怎么样?好吧,这只是另一个循环调用!首先,使用语法拐杖:

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'()

然后再没有(丑陋的)语法拐杖:

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()

简单就是馅饼!

这些循环形式的一个很好的结果是它可以更容易地在循环中发现和实现状态。例如,冒泡排序会不断循环遍历整个数组,并在找到它们时交换不合适的值。它跟踪阵列上的传递是否产生了任何交换。如果没有,则每个值必须在正确的位置,因此排序可以终止。作为优化,在每次通过数组时,数组中的最后一个值最终被分类到正确的位置。因此,每次循环可以缩短一次。大多数算法检查交换并在每次有一个“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

答案 1 :(得分:4)

哦,现在这是一个很好的问题。以下是一些code snips in python或其他内容:

  • for循环可以用迭代器替换

    stripped_list = [line.strip() for line in line_list]

  • for循环可以替换为apply或map或filter

      
        
          

    map(upper,['sentence','fragment'])       ['SENTENCE','FRAGMENT']

        
      
  • 嵌套了具有函数组合的循环

  • 尾递归代替循环

  • 生成器表达式代替for循环

    sum(x*x for x in range(10))

答案 2 :(得分:2)

旧作业问题:

  

功能

(define f-imperative (y) (x) ; x is a local variable
  (begin
    (set x e)
    (while (p x y)
       (set x (g x y)))
    (h x y)))
  

是典型的命令式样式,具有赋值和循环。编写一个等效函数f-functional,它不使用命令功能begin(排序),while(goto)和set(赋值)。您可以根据需要使用尽可能多的“辅助函数”,只要它们使用let或letrec而不是顶层定义。

一个解决方案:

; The idea is simple: 
; Use parameter passing for binding the values 
; of the variables and recursion instead of iteration. 
;
; For those who like theory this is the main argument for proving 
; that recursive functions (LISP, lambda calculus) have the same 
; computational power as any imperative programming language. 

(define f-functional (y) 
  (letrec (
     (f-helper (lambda (x y)
                  (if (p x y) 
                     (f-helper (g x y) y)
                     (h x y)))))
     (f-helper e y)))

; Notice that y in f-helper is invariant.  Therefore, we can rewrite
; f-helper without y as follows.

(define f-functional (y) 
  (letrec (
     (f-helper (lambda (x)
                  (if (p x y) 
                     (f-helper (g x y))
                     (h x y)))))
     (f-helper e)))

; This is not the only solution, though I think it is one of the 
; nicer ones.

答案 3 :(得分:2)

折叠是一个非常有趣的功能,这是许多功能算法的核心。假设我们要添加列表的所有元素。在过程代码中,通常会创建一个累加器变量并将其设置为0,然后遍历列表并按项目递增累加器。

在Ocaml中,您可以使用fold:

以功能方式执行相同的操作
List.fold_left (+) 0 [1; 2; 3];;
- : int = 6

使用fold,您可以计算列表中的单词数并同时连接它们:

List.fold_left (fun (count, concat) e -> (count + 1, concat ^ e)) (0, "") ["a"; "b"; "c"];;
- : int * string = (3, "abc")

折叠的另一个有用的用途是将矢量复制到集合中。由于Ocaml中的集合是不可变的,因此您实际上需要为列表中的每个项目创建一个包含前一个集合以及该新项目的新集合。

module Int = struct type t = int let compare = compare end;;
module IntSet = Set.Make(Int);;

let s = List.fold_left (fun set e -> IntSet.add e set) IntSet.empty [1; 2; 3];;
val s : IntSet.t = <abstr>

IntSet.elements s;;
- : IntSet.elt list = [1; 2; 3]

这里,我们的初始对象是一个空集,并且在每次调用时,都会使用IntSet.add基于前一个集和当前项创建一个新集。

自己一次递归折叠,知道它是如何在幕后完成的,然后在任何地方使用内置版本。即使在C ++中,使用std::accumulate

答案 4 :(得分:2)

PLEAC项目几乎完全以此为目标 - 以其他语言实现perl cookbook中的所有示例。这里是ocaml版本的链接(三个100%完成之一)http://pleac.sourceforge.net/pleac_ocaml/index.html