'What is the OCaml idiom equivalent to Python's range function?
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?
Solution 1:[1]
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).
Solution 2:[2]
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.
Solution 3:[3]
This works in base OCaml:
? List.init 5 (fun x -> x + 1);;
- : int list = [1; 2; 3; 4; 5]
Solution 4:[4]
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]
Solution 5:[5]
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)
Solution 6:[6]
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
Solution 7:[7]
Came up with this:
let range a b =
List.init (b - a) ((+) a)
Solution 8:[8]
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)).
Solution 9:[9]
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.
Solution 10:[10]
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".
Solution 11:[11]
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)
Solution 12:[12]
Personnaly 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);;
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
