列表 OCaml 的第一个和最后一个元素
First and last element of list 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 执行此功能。
方向是什么?
方向是编写两个不同的函数first
和last
并实现first_and_last
函数为:
let first_and_last xs = first xs, last xs
另一种只有一个功能的可能性:
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)
您可以为 return 第一个元素和最后一个元素创建两个单独的函数,然后在您的 first_and_last
函数中 return 一个元组(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
您可以创建一个辅助函数,它具有空列表的基本情况 - 它 return 自身,否则检查下一个递归调用是否 return 一个空列表列表。如果是,return 当前元素(根据定义是列表中的最后一个元素),如果不是,return 递归调用 return 的内容。
对于常规(非辅助)方法,如果列表至少有一个元素长(即 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
;;
另一个解决这个问题的方法。
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
这样的函数)。
另一个相当简单的变体,来自第一原则(我试图本着 SICP 书的精神来说明 "wishful thinking"):
(* 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))
你可以这样写:
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
的归纳来证明这一点: if xs = []
then 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'
的最后一个元素归纳假设。
我试图在 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 执行此功能。
方向是什么?
方向是编写两个不同的函数first
和last
并实现first_and_last
函数为:
let first_and_last xs = first xs, last xs
另一种只有一个功能的可能性:
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)
您可以为 return 第一个元素和最后一个元素创建两个单独的函数,然后在您的 first_and_last
函数中 return 一个元组(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
您可以创建一个辅助函数,它具有空列表的基本情况 - 它 return 自身,否则检查下一个递归调用是否 return 一个空列表列表。如果是,return 当前元素(根据定义是列表中的最后一个元素),如果不是,return 递归调用 return 的内容。
对于常规(非辅助)方法,如果列表至少有一个元素长(即 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
;;
另一个解决这个问题的方法。
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
这样的函数)。
另一个相当简单的变体,来自第一原则(我试图本着 SICP 书的精神来说明 "wishful thinking"):
(* 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))
你可以这样写:
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
的归纳来证明这一点: if xs = []
then 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'
的最后一个元素归纳假设。