Reputation: 500
How can I use one of the fold functions to generate a list of integers from 0 to a value n-1? I'm confused about how to get fold_right to return a list rather than returning just an accumulated value.
This is for a helper function that I'm trying to define to solve a larger problem. Here is my attempt:
-I know the base case has to be a list containing only zero, because I do not want to add anything less than zero.
-I know that I need to decrement the value n so that I can put numbers from n-1 to 0 in the list.
let buildList n =
let ibuildList elem list =
list@[n-1]
in List.fold_right ibuildList n [0];;
But I get an error underscoring "n" in the last line saying that the expression has type int but an expression was expected of type 'a list. Isn't n an integer that I'm turning into a list via [n-1]? Where did I go wrong?
Upvotes: 1
Views: 3763
Reputation: 15404
An alternative version, using the standard Stream module:
(* an infinite stream of natural numbers, starting from 0 *)
let nats =
let rec nats_from n = [< 'n; nats_from (n + 1) >] (* extra syntax *)
in nats_from 0
(* the first n natural numbers: [0; n-1] *)
let range n = Stream.npeek n nats
The piece [< 'n; nats_from (n + 1) >]
represents a lazy list with n
as its head and the next natural numbers as its tail. Stream.npeek n stream
consumes the first n
elements of stream
and returns them as a list.
Tests with utop:
utop # #load "dynlink.cma";; (* you need these to work with *)
utop # #load "camlp4o.cma";; (* the Stream's syntactic extension *)
utop # range 1;;
- : int list = [0]
utop # range 5;;
- : int list = [0; 1; 2; 3; 4]
utop # range 10;;
- : int list = [0; 1; 2; 3; 4; 5; 6; 7; 8; 9]
If you'd like to compile it, use the following commands (you need to use the camplp4o preprocessor):
$ ocamlc -pp camlp4o <filename>.ml
or
$ ocamlopt -pp camlp4o <filename>.ml
Upvotes: 1
Reputation: 66818
Very sorry, I missed at least one step of the reasoning.
A fold is for traversing a collection. Since you want to generate a list and you just have n
, not a collection, you can't really use fold in any reasonable way. In fact, what you want to do is more like an unfold. I.e., you want to unfold your n
into a list.
It's easy to write this function, but not easy to write it using a fold.
Here's an implementation of unfold in OCaml:
let rec unfold_right f init =
match f init with
| None -> []
| Some (x, next) -> x :: unfold_right f next
Here's how to use unfold_right
to generate a list of ints:
let range n =
let irange x = if x > n then None else Some (x, x + 1) in
unfold_right irange 1
Here's how it looks when you run range
:
# range 0;;
- : int list = []
# range 8;;
- : int list = [1; 2; 3; 4; 5; 6; 7; 8]
# range 5;;
- : int list = [1; 2; 3; 4; 5]
Upvotes: 3