在OCaml中总结一个列表最常用(或最快)的方法是什么?

时间:2013-10-17 15:27:02

标签: ocaml

以下是我要实施的内容

功能方式

let sum l = List.fold_left (fun s x -> s+x) 0 l

势在必行

let sum l =
  let sum = ref 0 in
  List.iter (fun x -> sum := !sum +x) l;
  !sum

还有更好/更快的方法吗?

我之所以这样问是因为书Real World OCaml说:

# let sum list =
    let sum = ref 0 in
    List.iter list ~f:(fun x -> sum := !sum + x);
    !sum
  ;;
val sum : int list -> int = <fun>

This isn't the most idiomatic (or the fastest) way to sum up a list, but it shows how you can use a ref in place of a mutable variable.

3 个答案:

答案 0 :(得分:10)

这稍微凉爽一点;)

let sum l = List.fold_left (+) 0 l;;

要查看效果:

open Printf

let sum1 l = List.fold_left (fun s x -> s+x) 0 l;;
let sum2 l = List.fold_left (+) 0 l;;
let sum3 = List.fold_left (+) 0;;

let rec make_list x acc = function
| 0 -> acc
| n -> make_list x (x :: acc) (n-1)

let l = make_list 1 [] 50000000;;

let _ = match Sys.argv.(1) with
| "1" -> printf "%d\n" (sum1 l)
| "2" -> printf "%d\n" (sum2 l)
| "3" -> printf "%d\n" (sum3 l)
| _ -> printf "Bad arg\n"
;;

给予

$ ocamlc foo.ml
$ time ./a.out 1
50000000

real    0m8.204s
user    0m7.211s
sys 0m0.848s
$ time ./a.out 2
time ./a.out 3
50000000

real    0m8.226s
user    0m7.325s
sys 0m0.818s
$ 50000000

real    0m8.472s
user    0m7.561s
sys 0m0.837s

sum1和sum2具有完全相同的字节码:

    branch L2
    restart
L3: grab 1
    acc 1
    push
    acc 1
    addint
    return 2
L1: acc 0
    push
    const 0
    push
    closure L3, 0
    push
    getglobal List!
    getfield 14
    appterm 3, 4
L2: closure L1, 0
    push
    acc 0
    makeblock 1, 0
    pop 1
    setglobal Foo1!

sum3字节码较小但速度较慢

    branch L2
    restart
L1: grab 1
    acc 1
    push
    acc 1
    addint
    return 2
L2: const 0
    push
    closure L1, 0
    push
    getglobal List!
    getfield 14
    apply 2
    push
    acc 0
    makeblock 1, 0
    pop 1
    setglobal Foo3!

任何人都知道为什么?

答案 1 :(得分:3)

使用电池:

let sum = BatList.reduce (+)

(实际上,电池已经具有BatList.sum功能,它可以完全按照您的要求进行操作 - 因此无需编写它:)

答案 2 :(得分:1)

作者想到的替代方案可能是手动编写折叠:

let low_level_sum list =
  let rec loop sum = function
    | [] -> sum
    | x::xs -> loop (sum + x) xs in
  loop 0 list

这是丑陋且低级别的,除非您有具体的性能问题,否则您应该更喜欢List.fold_left (+) 0。 (甚至可能在那时 - OCaml编译器的递归函数内联正在进行中,并且可能没有性能优势。)