Recursion using OCaml and unzip

耗尽温柔 提交于 2019-12-12 00:53:42

问题


I did pattern matching, and it's working just fine:

let rec unzip  l = 
    match l with
    |[] -> ([], [])
    |(x,y)::tail -> 
    let (first, second) = unzip tail in 
      (x::first, y::second)

but how would I do this using map or fold right (tips only please don't tell me how to actually do it)

I was thinking something along the lines of:

let unzip : ('a * 'b) list -> 'a list * 'b list = let (first,second) = map (fun (x,y) -> (x::first, y::second) );;

but getting syntax error


回答1:


If you look at the type of List.map:

# List.map;;
- : ('a -> 'b) -> 'a list -> 'b list = <fun>

You'll see that it always returns a list. Since you want to return a pair of lists, you can't use List.map. You can use a fold. Or you could call List.map once for each list in the pair.

Update

Let's just work with List.fold_left, which I think is a little easier.

The essence of fold_left is to figure out a function that does one step of your computation. The function takes the accumulated answer so far, and one new element of the list. The return value is the new accumulated answer. If you can define such a function, you can fold it over the input list to get your full answer.

One way to look at this is that your function is exactly what would appear as the body of a for statement. The body of a for also does one step of a computation.

Let's say I want to add a list of integers. So my desired function takes the accumulated answer so far (an int), the next value from the list (an int), and it returns the new accumulated answer (the sum of the two ints). So the function to be folded looks like this:

let foldme a b = a + b

If you fold this over a list of ints you get the sum:

# let foldme a b = a + b;;
val foldme : int -> int -> int = <fun>
# List.fold_left foldme 0 [3; 5; 7; 11];;
- : int = 26

If you wanted to reverse a list, you want a function that takes the answer so far (reverse of the first part of the list), and a new element of the list, and returns the reverse with the new value added at the beginning. So the function you want looks like this:

let foldme2 a b = b :: a

If you fold this over a list, you get the reverse of the list:

# let foldme2 a b = b :: a;;
val foldme2 : 'a list -> 'a -> 'a list = <fun>
# List.fold_left foldme2 [] [4; 5; 6; 7];;
- : int list = [7; 6; 5; 4]

If you can figure out a function that does one step of your computation, you can figure out how to do folds. It does take a while to get good at it.




回答2:


let f (l,s) (x,y) = (x::l,y::s);;
let unzip l = List.fold_left f ([],[]) (List.rev l);;



回答3:


You can use List.map twice: once with fst and once with snd. However, I suggest you take the time to understand List.fold_right and List.fold_left because they're useful in many contexts.



来源:https://stackoverflow.com/questions/32792844/recursion-using-ocaml-and-unzip

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!