miechooy
miechooy

Reputation: 3422

First and last element of list OCaml

I am trying to get first and last element of the list in OCaml. I expect that my function will be like

'a list -> 'a * 'a

What I am trying to do is

let lista = [1;2;3;4;6;0];;

let rec first_last myList =
        match myList with
        [x] -> (List.hd lista,x)
        | head::tail ->     
                  first_last tail;;

first_last lista;;

Of course because of I made list as integer then I am doing this syntax like

*int list -> int * 'a

The point is that I dont have idea how to do this function for 'a.

Whats the direction?

Upvotes: 4

Views: 24018

Answers (6)

de Vilhena
de Vilhena

Reputation: 11

You could write it like this:

let first_last = function
  | [] -> assert false
  | x :: xs -> (x, List.fold_left (fun _ y -> y) x xs)

Or, if you are using the Base library, you could write in this way:

let first_last xs = (List.hd_exn xs, List.reduce_exn ~f:(fun _ y -> y) xs)

The basic idea is that List.fold_left (fun _ y -> y) x xs will compute the last element of x :: xs. You can prove this by induction on xs: if xs = [] then List.fold_left (fun _ y -> y) x [] = x, which is the last element of x :: []; moreover, if xs = x' :: xs' then List.fold_left (fun _ y -> y) x (x' :: xs') can be rewritten as List.fold_left (fun _ y -> y) x' xs', because List.fold_left f acc (x :: xs) = List.fold_left (f acc x) xs, hence we are finished, because this is the last element of x' :: xs' by our induction hypothesis.

Upvotes: 0

Anton Trunov
Anton Trunov

Reputation: 15404

Yet another take on this problem.

let first_last xs =
  let rec last_non_empty = function
    | [x]     -> x
    | _ :: xs' -> last_non_empty xs'
    | []      -> failwith "first_last: impossible case!"
  in
    match xs with
      | []   -> failwith "first_last"
      | x::_ -> (x, last_non_empty xs)

Some properties of this implementation: (1) it meets the specification 'a list -> 'a * 'a:

utop > #typeof "first_last";;
val first_last : 'a list -> 'a * 'a 

(2) it works for singleton lists: first_last [x] = (x,x):

utop> first_last [1];;
- : int * int = (1, 1)                                                                                                                                  utop> first_last ["str"];;
- : bytes * bytes = ("str", "str")

(3) it's tail-recursive (hence it won't cause stack overflow for sufficiently big lists):

utop > first_last (Array.to_list (Array.init 1000000 (fun x -> x+1)));;
- : int * int = (1, 1000000)

(4) it traverses the input list one time only; (5) it avoids creating new lists as it goes down the recursive ladder; (6) it avoids polluting the namespace (with the price of not allowing the reuse of a function like last).


And another rather simple variant, from the first principles (I was trying to illustrate "wishful thinking" in the spirit of the SICP book):

(* Not tail-recursive, might result in stack overflow *)
let rec first_last = function
  | [] -> failwith "first_last"
  | [x] -> (x,x)
  | x :: xs -> (x, snd (first_last xs))

Upvotes: 0

Ephraim
Ephraim

Reputation: 8391

You can create a helper function that has a base-case of the empty-list - for which it returns itself, and otherwise checks if the next recursive call will return an empty list. If it does, return the current element (which is by definition the last element in the list), and if it doesn't, return what was returned by the recursive call.

For the regular (non-helper) method, if the list is at least one element long (i.e. hd::tl = hd::[]) then you can just concatenate the list you got from the last function onto the head from ls.

It can be implemented as follow:

let rec last ls = 
    match ls with
    | [] -> []
    | hd::tl -> let next = last tl in
        if next = [] then [hd]
    else next
;;
let first_last ls =
    match ls with
    | [] -> failwith "Oh no!!!!! Empty list!"
    | hd::tl -> hd::last tl
;;

Upvotes: 0

Akash Magoon
Akash Magoon

Reputation: 899

You can create two separate functions to return first element and last element, and then in your first_and_last function return a tuple (first_element, last_element).

let rec first_element list = 
    match list with 
       | [] -> failwith "List is empty"
       | first_el::rest_of_list -> first_el


let rec last_element list = 
    match list with 
       | [] -> failwith "List is empty"
       | [x] -> x
       | first_el::rest_of_list -> last_element rest_of_list

Upvotes: 1

ivg
ivg

Reputation: 35210

The direction is to write two different functions first and last and implement the first_and_last function as:

let first_and_last xs = first xs, last xs

Upvotes: 8

Julien C.
Julien C.

Reputation: 329

Another possibility with only one function:

let rec first_last = function
    | [] -> failwith "too bad"
    | [e] -> failwith "too bad"
    | [e1;e2] -> (e1,e2) 
    | e1 :: _ :: r -> first_last (e1::r)

You may prefer it like that:

let rec first_last myList = match myList with
    | [] -> failwith "too bad"
    | [e] -> failwith "too bad"
    | [e1;e2] -> (e1,e2) 
    | e1 :: _ :: r -> first_last (e1::r)

Upvotes: 5

Related Questions