相当于 Python 的 range 函数的 OCaml 成语是什么?

问题描述 投票:0回答:14

我想创建一个从 1 到

n
的整数列表。我可以在 Python 中使用
range(1, n+1)
来做到这一点,在 Haskell 中使用:
take n (iterate (1+) 1)
.

正确的 OCaml 习语是什么?

ocaml
14个回答
27
投票

我不知道有什么成语,但这里有一个使用中缀运算符的相当自然的定义:

# 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]

或者,comprehensions 语法扩展(它给出了上面的语法

[i .. j]
)可能会包含在 OCaml“社区版本”的未来版本中,因此这可能会成为惯用的。不过,如果您是该语言的新手,我不建议您开始使用语法扩展。


14
投票

使用 随附电池,您可以书写

let nums = List.of_enum (1--10);;

--
运算符生成从第一个值到第二个值的枚举。
--^
运算符类似,但枚举半开区间(
1--^10
将从 1 到 9 枚举)。


14
投票

这适用于基础 OCaml:

# List.init 5 (fun x -> x + 1);;
- : int list = [1; 2; 3; 4; 5]

12
投票

给你:

let rec range i j = 
  if i > j then [] 
  else i :: range (i+1) j

注意这不是尾递归。现代 Python 版本甚至有一个惰性范围。


10
投票

从上面跟随 Alex Coventry,但更短。

let range n = List.init n succ;;    
> val range : int -> int list = <fun>   
range 3;;                           
> - : int list = [1; 2; 3]              

6
投票

如果你打算模仿

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)

4
投票

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

4
投票

想到这个:

let range a b =
  List.init (b - a) ((+) a)

3
投票

这里的游戏有点晚了,但这是我的实现:

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,但如果您只需要一个功能并且您试图避免完整的框架,这可能会更好。


3
投票

为了好玩,这里有一个非常类似于 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 的行为,例如默认从 0 简洁地计数:

# List.of_seq @@ range 5;;
- : int list = [0; 1; 2; 3; 4]

...或倒数:

# 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".

2
投票

如果您使用

open Batteries
(这是标准库的社区版本),您可以通过
range(1,n+1)
执行
List.range 1 `To n
(注意
To
之前的反引号)。

更通用的方法(也需要电池)是使用

List.init n f
返回包含 (f 0) (f 1) ... (f (n-1)) 的列表。


0
投票

这是我使用 Base.Sequence 的版本

open Base

let pylike_range ?(from=0) ?(step=1) (until: int) : int Sequence.t = 
  Sequence.range ~stride:step ~start:`inclusive ~stop:`exclusive from until

let range_list ?(from=0) ?(step=1) (until: int) : int list = 
  pylike_range ~from:from ~step:step until 
  |> Sequence.to_list 

用法示例:

# range_list 10;;
- : int list = [0; 1; 2; 3; 4; 5; 6; 7; 8; 9]

# range_list 10 ~from:3;;
- : int list = [3; 4; 5; 6; 7; 8; 9]

# range_list 10 ~from:3 ~step:2;;
- : int list = [3; 5; 7; 9]

0
投票

我个人使用 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
);;

0
投票

如果您不需要“step”参数,实现此功能的一种简单方法是:

let range start stop = 
  List.init (abs @@ stop - start) (fun i -> i + start)
© www.soinside.com 2019 - 2024. All rights reserved.