问题
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