我想在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。
执行此功能方向是什么?
答案 0 :(得分:8)
方向是编写两个不同的函数first
和last
,并将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'
的最后一个元素归纳假设。