我发现这种类型
zipper
和insert
函数定义如下:
type 'a zipper = 'a list * int;;
exception Empty
let empty = ([], 0)
let insert ((l, n): 'a zipper) (a: 'a) : 'a zipper =
let rec ins ll nn =
if nn = 0 then a::ll
else
match ll with
| [] -> raise Empty
| h::q -> h::(ins q (nn-1))
in
(ins l n, n)
如何将此功能转换为仅使用一个
let
而没有那个in (ins l n, n)
编写一个在
n
值中使用 zipper
并返回列表的插入函数是微不足道的。
# let rec insert' ((l, n) : 'a zipper) v =
if n = 0 then v :: l
else
match l with
| [] -> raise Empty
| x::xs -> x :: insert' (xs, n - 1) v;;
val insert' : 'a zipper -> 'a -> 'a list = <fun>
# insert' ([1;2;3;4;5], 5) 6;;
- : int list = [1; 2; 3; 4; 5; 6]
这本质上就是你的内部功能在做什么。
诀窍是当您需要返回具有原始
zipper
值的n
值时。我上面显示的 ins
函数的 insert'
函数在每次迭代中更新该值,并且每次迭代都不知道之前的值,所以我们无法取回 n
的原始值。
这就是为什么你的内部函数的原始解决方案有效的原因。内部函数可以生成更新后的列表,然后用原值
n
包裹在元组中。
没有本地绑定有没有办法做到这一点?好吧,下面是一个,但它不是特别有效。
let insert (l, n) v =
if List.length l <= n || n < 0 then raise Empty;
(l |> List.to_seq |> Seq.take n |> List.of_seq)
@ [v]
@ (l |> List.to_seq |> Seq.drop n |> List.of_seq)
减少局部函数定义的数量在很大程度上是一个无意义的指标,但你当然可以做到
let cons x (l,n) = (x::l,n+1)
let rec insert (l, n) a =
if n = 0 then a::l,n
else
match l with
| [] -> raise Empty
| h::q -> cons h (insert (q,n-1) a)