遇到问题时遇到困难:
定义一个名为 zip 的函数,它将一对等长列表(元组)作为单个参数并返回一个对列表。第一对应包含每个列表的第一个元素,第二对包含每个列表的第二个元素,依此类推。
我一直被困住,正在寻求建议,看看我是否朝着正确的方向前进,或者应该尝试另一种方法。
它需要是单个函数定义,没有任何嵌套函数,并且不能使用内置函数!
我所做的是:
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)
Run Code Online (Sandbox Code Playgroud)
什么时候
> zip (["a"; "b"; "c"; "d"; "e"], [1; 2; 3; 4; 5]);;
Run Code Online (Sandbox Code Playgroud)
已输入
val it : string * int = ("e", 5)
Run Code Online (Sandbox Code Playgroud)
被返回。预期结果应该是
val it : (string * int) list = [("a", 1); ("b", 2); ("c", 3); ("d", 4); ("e", 5)]
Run Code Online (Sandbox Code Playgroud)
让我们从最初的实现开始:
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)
Run Code Online (Sandbox Code Playgroud)
首先,类型是错误的 - 这返回一个值的元组,而不是元组的列表。它的作用是迭代列表(在尾部使用List.tail),当到达末尾时,它返回每个列表的唯一元素,即"e"和5。
解决此问题的第一步可能是添加类型注释。这将迫使您返回分支中的列表then。如果你有两个单例列表["e"]和[5],你想返回["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)
Run Code Online (Sandbox Code Playgroud)
这仍然是不对的——在这种else情况下,你只看到了尾巴,却忽略了头部。您需要访问head并将其连接到递归调用返回的列表:
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)
Run Code Online (Sandbox Code Playgroud)
这是可行的,但if .. then .. else在这种情况下使用是不优雅的。Filipe 的答案展示了如何通过模式匹配更好地做到这一点。