【发布时间】:2015-02-23 11:43:11
【问题描述】:
似乎 Ocaml 电池具有理解语法: http://en.wikipedia.org/wiki/List_comprehension#OCaml
但是,我应该包含什么模块才能使用这种语法?我已经open Batteries,但它不起作用。还是有更惯用的方法来进行列表理解?我可以使用 List.map 和 BatList.remove_if 来实现类似的结果,但这样就不太优雅了。
【问题讨论】:
标签: ocaml
似乎 Ocaml 电池具有理解语法: http://en.wikipedia.org/wiki/List_comprehension#OCaml
但是,我应该包含什么模块才能使用这种语法?我已经open Batteries,但它不起作用。还是有更惯用的方法来进行列表理解?我可以使用 List.map 和 BatList.remove_if 来实现类似的结果,但这样就不太优雅了。
【问题讨论】:
标签: ocaml
列表推导已经包含在标准 ocaml 中
#require "camlp4.listcomprehension";;
[ x * x | x <- [ 1;2;3;4;5] ];;
- : int list = [1; 4; 9; 16; 25]
【讨论】:
目前 OCaml 中有两个库提供列表理解,一个以前是 OCaml Batteries 的一部分,另一个是随 camlp4 提供的。两者都没有被广泛使用,我个人不建议您使用任何一个。
要使列表理解起作用,您需要更改语言的语法。这可以通过使用camlp4 预处理器对您的程序进行预处理来完成,该程序以扩展语法编写。此外,列表理解不是 OCaml 社区中的一等公民,现代工具包也没有很好地支持它。虽然,您仍然可以轻松地在顶层使用它,为此您需要安装列表理解包:
opam install pa_comprehension
并使用以下指令将其加载到顶层:
# #use "topfind";;
# #camlp4o;;
# #require "pa_comprehension";;
# open Batteries;;
# [? 2 * x | x <- 0 -- max_int ; x * x > 3 ?];;
但是,我个人认为列表理解并不是构建代码的最佳方式。
您提供的示例可以使用core_kernel Sequence 模块(电池Enum 的模拟)表示
let f n =
Sequence.(range 0 n |>
filter ~f:(fun x -> x * x > 3) |>
map ~f:(fun x -> x * 2))
因此filter |> map 是一个如此常见的习语,因此存在filter_map 函数:
let f n =
Sequence.(range 0 n |>
filter_map ~f:(fun x ->
if x * x > 3 then Some (x * 2) else None))
您可能会注意到,此示例使用的代码比列表理解更多。但是,一旦您的程序开始从简单的带有整数的 hello world 应用程序成熟到更复杂的程序,您就会同意使用显式迭代器更具可读性和可理解性。
另外,由于Core 中的库非常一致,您可以使用简单的List 而不是Sequence,只需将后者替换为前者即可。但当然,List 是渴望的,不像Sequence,所以使用列表来玩max_int 并不是一个好主意。
此外,由于所有容器都是 monad,因此您可以使用 monadic 运算符进行映射,例如:
let odds n = List.(range 0 n >>| fun x -> x * 2 + 1)
【讨论】:
List.filter 用于>3 条件,然后使用List.map
|>,那就没那么糟糕了(参见ocamlpro.com/blog/2012/08/20/ocamlpro-and-4.00.0.html“管道操作符”)
[ x * 2 + 1 for x in range(n) ]相同。 Monad 可以看作是容器的泛化,实际上 monadic map 操作符与任何其他容器的 map 操作符完全相同。这是一个很好的tutorial