将函数从使用两个 `let` 和 `in` 转换为只使用一个 `let`

问题描述 投票:0回答:2

我发现这种类型

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)

functional-programming ocaml
2个回答
1
投票

编写一个在

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)

0
投票

减少局部函数定义的数量在很大程度上是一个无意义的指标,但你当然可以做到

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)
© www.soinside.com 2019 - 2024. All rights reserved.