简体   繁体   中英

In F# define zip function for two lists

Having trouble with a problem:

Define a function called zip that takes a pair (tuple) of equal length lists as a single parameter and returns a list of pairs. The first pair should contain the first element of each list, the second pair contains the second element of each list, and so on.

I have been stuck and am looking for advice on if I'm headed in the right direction or should try another approach.

It needs to be a single function definition without any nested functions and can not use build in functions!

What I have done is:

    let rec zip (a , b) =
       if List.length a = 1 then List.head a , List.head b
       else zip (List.tail a , List.tail b) 

when

     > zip (["a"; "b"; "c"; "d"; "e"], [1; 2; 3; 4; 5]);;

is entered

      val it : string * int = ("e", 5) 

is returned. The expected result should be

     val it : (string * int) list = [("a", 1); ("b", 2); ("c", 3); ("d", 4); ("e", 5)]

Let's start with your original implementation:

let rec zip (a , b) =
    if List.length a = 1 then List.head a , List.head b
    else zip (List.tail a , List.tail b) 

First of all, the type is wrong - this returns a tuple of values, not a list of tuples. What this does is that it iterates over the list (following the tails using List.tail ) and when it reaches the end, it returns the only element of each of the lists, which is "e" and 5 .

The first step to fixing this could be to add type annotations. This will force you to return a list in the then branch. If you have two singleton lists ["e"] and [5] , you want to return ["e", 5] :

let rec zip (a:'a list , b:'b list) : list<'a * 'b> =
    if List.length a = 1 then [List.head a , List.head b]
    else zip (List.tail a , List.tail b) 

This is still not right - in the else case, you are just looking at the tails, but you are ignoring the heads. You need to access the head and concatenate it to the list returned from your recursive call:

let rec zip (a:'a list , b:'b list) : list<'a * 'b> =
    if List.length a = 1 then [List.head a , List.head b]
    else (List.head a, List.head b) :: zip (List.tail a , List.tail b) 

This works, but using if .. then .. else in this case is inelegant. The answer from Filipe shows how to do this better with pattern matching.

let rec zip (a, b) =
    match (a, b) with
    | ha :: ta, hb :: tb -> (ha, hb) :: zip (ta, tb)
    | _, _ -> []

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM