我想创建一个从 1 到 的整数列表n
。我可以在 Python 中使用range(1, n+1)
,在 Haskell 中使用:take n (iterate (1+) 1)
。
什么是正确的 OCaml 习惯用法?
我想创建一个从 1 到 的整数列表n
。我可以在 Python 中使用range(1, n+1)
,在 Haskell 中使用:take n (iterate (1+) 1)
。
什么是正确的 OCaml 习惯用法?
没有我知道的成语,但这是一个使用中缀运算符的相当自然的定义:
# let (--) i j =
let rec aux n acc =
if n < i then acc else aux (n-1) (n :: acc)
in aux j [] ;;
val ( -- ) : int -> int -> int list = <fun>
# 1--2;;
- : int list = [1; 2]
# 1--5;;
- : int list = [1; 2; 3; 4; 5]
# 5--10;;
- : int list = [5; 6; 7; 8; 9; 10]
或者,理解语法扩展(提供上述语法[i .. j]
)可能会包含在OCaml 的“社区版本”的未来版本中,因此这可能会变得惯用。不过,如果您不熟悉该语言,我不建议您开始使用语法扩展。
包括电池,你可以写
let nums = List.of_enum (1--10);;
--
运算符生成从第一个值到第二个值的枚举。运算符类似,--^
但枚举一个半开区间(1--^10
将从 1 到 9 枚举)。
干得好:
let rec range i j = if i > j then [] else i :: (range (i+1) j)
请注意,这不是尾递归。现代 Python 版本甚至有一个惰性范围。
这适用于基本 OCaml:
# List.init 5 (fun x -> x + 1);;
- : int list = [1; 2; 3; 4; 5]
从上面跟随亚历克斯考文垂,但更短。
let range n = List.init n succ;;
> val range : int -> int list = <fun>
range 3;;
> - : int list = [1; 2; 3]
OCaml 具有用于范围模式匹配的特殊语法:
let () =
let my_char = 'a' in
let is_lower_case = match my_char with
| 'a'..'z' -> true (* Two dots define a range pattern *)
| _ -> false
in
printf "result: %b" is_lower_case
要创建范围,您可以使用Core
:
List.range 0 1000
如果您打算模仿 的懒惰行为range
,我实际上建议使用该Stream
模块。就像是:
let range (start: int) (step: int) (stop: int): int stream =
Stream.from (fun i -> let j = i * step + start in if j < stop then Some j else None)
如果你使用open Batteries
(这是标准库的社区版本),你可以range(1,n+1)
通过List.range 1 `To n
(注意前面的反引号To
)来做。
一种更通用的方法(也需要电池)是使用List.init n f
它返回一个包含 (f 0) (f 1) ... (f (n-1)) 的列表。
这里的游戏有点晚了,但这是我的实现:
let rec range ?(start=0) len =
if start >= len
then []
else start :: (range len ~start:(start+1))
然后你可以像使用 python 函数一样使用它:
range 10
(* equals: [0; 1; 2; 3; 4; 5; 6; 7; 8; 9] *)
range ~start:(-3) 3
(* equals: [-3; -2; -1; 0; 1; 2] *)
当然,我认为最好的答案是简单地使用 Core,但是如果您只需要一个功能并且您试图避免使用完整的框架,这可能会更好。
想出了这个:
let range a b =
List.init (b - a) ((+) a)
为了好玩,这是一个非常类似于 Python 的range
使用惰性序列的实现:
let range ?(from=0) until ?(step=1) =
let cmp = match step with
| i when i < 0 -> (>)
| i when i > 0 -> (<)
| _ -> raise (Invalid_argument "step must not be zero")
in
Seq.unfold (function
i when cmp i until -> Some (i, i + step) | _ -> None
) from
因此,您可以通过以下方式获得从 1 到的整数列表n
:
# let n = 10;;
val n : int = 10
# List.of_seq @@ range ~from:1 (n + 1);;
- : int list = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
它还提供了其他类似 Python 的行为,例如倒数:
# List.of_seq @@ range ~from:20 2 ~step:(-3);;
- : int list = [20; 17; 14; 11; 8; 5]
(* you have to use a negative step *)
# List.of_seq @@ range ~from:20 2;;
- : int list = []
# List.of_seq @@ range 10 ~step:0;;
Exception: Invalid_argument "step must not be zero".
如果您不需要“step”参数,实现此功能的一种简单方法是:
let range start stop = List.init (abs @@ stop - start) (fun i -> i + start)
Personnaly 我为此使用了 ocaml 的范围库
(* print sum of all values between 1 and 50, adding 4 to all elements and excluding 53 *) Range.(from 1 50 |> map ((+) 4) |> filter ((!=) 53) |> fold (+) 0 |> print_int);;