'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);;