11

I'm looking at the List documentation. It seems the library does not provide a sublist function.

I'm trying to get list of elements from i to j. Now I have to write it as:

let rec sublist list i j =
  if i > j then
    []
  else
    (List.nth list i) :: (sublist list (i+1) j)

which is quite concise but I'm questioning the efficiency of List.nth, because if it's O(n), I would rather have to write it in a less concise way.

I'm wondering why didn't they provide List.sublist func, if List.nth is not O(1), because it's such a quite common operation..

Michaël Le Barbier
  • 6,103
  • 5
  • 28
  • 57
romerun
  • 2,161
  • 3
  • 18
  • 25

5 Answers5

11
let rec sublist b e l = 
  match l with
    [] -> failwith "sublist"
  | h :: t -> 
     let tail = if e=0 then [] else sublist (b-1) (e-1) t in
     if b>0 then tail else h :: tail
;;

sublist 3 5 [1;2;3;4;5;6;7;8;9] ;;
- : int list = [4; 5; 6]

The above is more or less a deforested version of newacct's solution. newacct's solution allocates an intermediate list (drop i list), which is possible for the compiler to optimize away in Haskell but much harder in ML. Therefore his version is perfectly fine for a Haskell function and marginally sub-optimal for an ML one. The difference between the two is only a constant factor: both are O(e). zrr's version is O(length(l)) since List.filteri doesn't know that f only returns false after a while, it calls it for all elements in l.

I'm not very happy to let b go negative but the version where it doesn't is more complicated.

One reference among quite a few for deforestation if you're interested: http://homepages.inf.ed.ac.uk/wadler/topics/deforestation.html

Pascal Cuoq
  • 79,187
  • 7
  • 161
  • 281
  • 1
    Actually, I was wrong: the un-optimized call-by-value evaluation of newacct's function is O(length(l)) too, because of the intermediate list. To preserve the asymptotic complexity O(e) in ML, you would have to `take` first, then `drop`. – Pascal Cuoq Apr 27 '10 at 15:48
6

Try writing the take (first n items) and drop (everything but the first n items) functions (like in Haskell) first. Then sublist i j lst is just take (j-i) (drop i lst)

newacct
  • 119,665
  • 29
  • 163
  • 224
2

This is a bit harder than it should be with OCaml's standard library --- the standard library is a bit sparse. If you use one of the extended standard libraries, it gets easier. With Core, for example, you could write:

let sublist list low high =
   List.filteri l ~f:(fun i _ -> i >= low && pos < high)

I imagine something similar is possible with extlib/batteries.

zrr
  • 907
  • 4
  • 6
  • 3
    This will go through the entire list even if you just want something in the beginning of the list. – larsr Dec 19 '12 at 19:35
1

While the answer provided by Pascal implements a nice candidate for List.sublist the right answer is that you should probably better use an array of a list. The Array modules implements the Array.sub function that you might use.

While in many imperatives languages such as C++ or Perl there is essentially no difference between lists and arrays, this is not the same in OCaml where:

  • Lists are better suited for recursive treatments and sequential access, i.e. is usually a better candidate as an array as argument to a recursive function, and you usually want to take a look at all the elements of the list.

  • Arrays are better suited for random access, structure alteration (such as sorting), or for numerical computations.

Michaël Le Barbier
  • 6,103
  • 5
  • 28
  • 57
0
let rec sublist (lst : int list) (start : int) (stop : int) : int list =
  match lst with
  | [] -> [] 
  | x::rest when start = 0 && stop >= 0 -> x::sublist rest start (stop-1)
  | x::rest when start = 0 && stop = 0 -> sublist rest start stop
  | x::rest -> sublist rest (start-1) (stop-1)
;;

when works as if else statement