相当于 Python 范围函数的 OCaml 习惯用法是什么?

发布于 2024-07-07 16:01:21 字数 166 浏览 8 评论 0原文

我想创建一个从 1 到 n 的整数列表。 我可以在 Python 中使用 range(1, n+1) 来完成此操作,在 Haskell 中使用:take n (iterate (1+) 1) 来完成此操作。

正确的 OCaml 习惯用法是什么?

I want to create a list of integers from 1 to n. I can do this in Python using range(1, n+1), and in Haskell using: take n (iterate (1+) 1).

What is the right OCaml idiom for this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(15

野心澎湃 2024-07-14 16:01:21

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

# 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 的“社区版本”,因此这可能会成为惯用语。 不过,如果您是该语言的新手,我不建议您开始使用语法扩展。

There is no idiom that I know of, but here is a fairly natural definition using an infix operator:

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

Alternatively, the comprehensions syntax extension (which gives the syntax [i .. j] for the above) is likely to be included in a future release of the "community version" of OCaml, so that may become idiomatic. I don't recommend you start playing with syntax extensions if you are new to the language, though.

为人所爱 2024-07-14 16:01:21

这适用于基础 OCaml:

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

This works in base OCaml:

# List.init 5 (fun x -> x + 1);;
- : int list = [1; 2; 3; 4; 5]
非要怀念 2024-07-14 16:01:21

使用 Batteries Included,您可以编写

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

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

With Batteries Included, you can write

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

The -- operator generates an enumeration from the first value to the second. The --^ operator is similar, but enumerates a half-open interval (1--^10 will enumerate from 1 through 9).

初与友歌 2024-07-14 16:01:21

给你:

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

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

Here you go:

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

Note that this is not tail-recursive. Modern Python versions even have a lazy range.

简美 2024-07-14 16:01:21

从上面跟随亚历克斯·考文垂,但更短。

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

Following on Alex Coventry from above, but even shorter.

let range n = List.init n succ;;    
> val range : int -> int list = <fun>   
range 3;;                           
> - : int list = [1; 2; 3]              
最单纯的乌龟 2024-07-14 16:01:21

如果您打算模拟 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)

If you intend to emulate the lazy behavior of range, I would actually recommend using the Stream module. Something like:

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)
安静被遗忘 2024-07-14 16:01:21

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< /a>:

List.range 0 1000

OCaml has special syntax for pattern matching on ranges:

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

To create a range, you can use Core:

List.range 0 1000
黯然 2024-07-14 16:01:21

想出了这个:

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

Came up with this:

let range a b =
  List.init (b - a) ((+) a)
人生百味 2024-07-14 16:01:21

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

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,但如果您只需要一个函数并且您'我们试图避免使用完整的框架。

A little late to the game here but here's my implementation:

let rec range ?(start=0) len =
    if start >= len
    then []
    else start :: (range len ~start:(start+1))

You can then use it very much like the python function:

range 10 
     (* equals: [0; 1; 2; 3; 4; 5; 6; 7; 8; 9] *)

range ~start:(-3) 3 
     (* equals: [-3; -2; -1; 0; 1; 2] *)

naturally I think the best answer is to simply use Core, but this might be better if you only need one function and you're trying to avoid the full framework.

薄荷梦 2024-07-14 16:01:21

为了好玩,这里有一个非常类似于 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".

For fun, here's a very Python-like implementation of range using a lazy sequence:

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

So you can get a list of integers from 1 to n by:

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

It also gives other Python-like behaviours, such as concisely counting from 0 by default:

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

...or counting backwards:

# 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".
生生漫 2024-07-14 16:01:21

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

更通用的方法(也需要电池)是使用 List.init n f ,它返回一个包含 (f 0) (f 1) ... (f (n-1)) 的列表。

If you use open Batteries (which is a community version of the standard library), you can do range(1,n+1) by List.range 1 `To n (notice the backquote before To).

A more general way (also need batteries) is to use List.init n f which returns a list containing (f 0) (f 1) ... (f (n-1)).

凑诗 2024-07-14 16:01:21

这是我使用 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]

Here is my version using 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 

Example usages:

# 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]
挽手叙旧 2024-07-14 16:01:21

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

let range start stop = 
  List.init (abs @@ stop - start) (fun i -> i + start)

If you don't need a "step" parameter, one easy way to implement this function would be:

let range start stop = 
  List.init (abs @@ stop - start) (fun i -> i + start)
來不及說愛妳 2024-07-14 16:01:21

就我个人而言,我使用 OCaml 的 range 库来实现此目的。

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

Personally I use the range library of OCaml for that.

(* 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
);;
捎一片雪花 2024-07-14 16:01:21

这里的很多答案都描述了从 A 到 B 获取列表的严格评估或惰性方法,有时是通过步骤 C。然而,Python 的 range 有一个关键方面尚未涵盖:在 Python 3.x 中,检查范围中的成员资格是 O(1) 操作,而检查列表(或惰性序列)中的成员资格是 O(n) 操作。

如果我们将范围实现为抽象类型,则可以实现一个 mem 函数,该函数以 O(1) 的方式检查范围中的成员资格。

module type RANGE_TYPE = sig
  type t
  val make : int -> int -> int -> t
  val mem : int -> t -> bool
  val to_seq : t -> int Seq.t
  val to_list : t -> int list
end

module Range : RANGE_TYPE = struct
  type t = { start: int; stop: int; by: int }
  
  let make start stop by =
    {start; stop; by}
    
  let mem x {start; stop; by} =
    if by > 0 then
      x >= start && x < stop && (x - start) mod by = 0 
    else
      x <= start && x > stop && (start - x) mod abs by = 0
      
  let to_seq {start; stop; by} =
    let rec aux n () =
      if (by > 0 && n >= stop) || (by < 0 && n <= stop) then 
        Seq.Nil  
      else 
        Seq.Cons (n, aux (n + by))      
    in
    aux start    
    
  let to_list t = 
    t |> to_seq |> List.of_seq  
end

A lot of answers here describe either strictly evaluated or lazy ways to get a list from A to B, and sometimes by a step C. However, there is one critical aspect of Python's range that has not been covered: in Python 3.x checking for membership in a range is an O(1) operation, while checking for membership in a list (or a lazy sequence) is an O(n) operation.

If we implement ranges as an abstract type, we can implement a mem function which checks for membership in a range in an O(1) fashion.

module type RANGE_TYPE = sig
  type t
  val make : int -> int -> int -> t
  val mem : int -> t -> bool
  val to_seq : t -> int Seq.t
  val to_list : t -> int list
end

module Range : RANGE_TYPE = struct
  type t = { start: int; stop: int; by: int }
  
  let make start stop by =
    {start; stop; by}
    
  let mem x {start; stop; by} =
    if by > 0 then
      x >= start && x < stop && (x - start) mod by = 0 
    else
      x <= start && x > stop && (start - x) mod abs by = 0
      
  let to_seq {start; stop; by} =
    let rec aux n () =
      if (by > 0 && n >= stop) || (by < 0 && n <= stop) then 
        Seq.Nil  
      else 
        Seq.Cons (n, aux (n + by))      
    in
    aux start    
    
  let to_list t = 
    t |> to_seq |> List.of_seq  
end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文