列表OCaml的第一个和最后一个元素

时间:2016-03-03 12:43:04

标签: functional-programming ocaml

我想在OCaml中获取列表的第一个和最后一个元素。我希望我的功能就像

'a list -> 'a * 'a

我想做的是

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

当然因为我将列表作为整数,所以我正在使用

这样的语法
*int list -> int * 'a

重点是我不知道如何为'a。

执行此功能

方向是什么?

6 个答案:

答案 0 :(得分:8)

方向是编写两个不同的函数firstlast,并将first_and_last函数实现为:

let first_and_last xs = first xs, last xs

答案 1 :(得分:3)

只有一个功能的另一种可能性:

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

您可能更喜欢这样:

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)

答案 2 :(得分:1)

您可以创建两个单独的函数来返回第一个元素和最后一个元素,然后在first_and_last函数中返回一个元组(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

答案 3 :(得分:1)

您可以创建一个辅助函数,该函数具有空列表的基本情况 - 为其返回自身,否则检查下一个递归调用是否将返回空列表。如果是,则返回当前元素(根据定义,它是列表中的最后一个元素),如果不是,则返回递归调用返回的内容。

对于常规(非帮助)方法,如果列表至少有一个元素长(即hd::tl = hd::[]),那么您可以将从last函数获得的列表连接到头上来自ls

可以按照以下方式实施:

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

答案 4 :(得分:0)

还有另外一个问题。

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)

此实现的一些属性: (1)它符合规范'a list -> 'a * 'a

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

(2)它适用于单身人士名单:first_last [x] = (x,x)

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

(3)它的尾递归(因此它不会导致堆栈溢出足够大的列表):

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

(4)它只遍历输入列表一次; (5)它避免了在递归阶梯下创建新列表; (6)它避免了污染命名空间(以不允许重用last之类的函数为代价。

<小时/> 另一个相当简单的变体,从第一个原则(我试图说明&#34;一厢情愿的想法&#34;在SICP的精神中):

(* 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))

答案 5 :(得分:0)

您可以这样写:

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

或者,如果您使用的是Base库,则可以这样编写:

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

基本思想是List.fold_left (fun _ y -> y) x xs将计算x :: xs的最后一个元素。您可以通过对xs的归纳证明:如果xs = [],则List.fold_left (fun _ y -> y) x [] = x,这是x :: []的最后一个元素;此外,如果xs = x' :: xs'可以将List.fold_left (fun _ y -> y) x (x' :: xs')改写为List.fold_left (fun _ y -> y) x' xs',因为List.fold_left f acc (x :: xs) = List.fold_left (f acc x) xs因此完成了,因为这是x' :: xs'的最后一个元素归纳假设。