我正在尝试创建一个OCaml函数rv
,它将遍历列表列表并将元素重新组织到另一个列表列表中,这样第一个列表就包含每个原始列表的第一个元素,第二个列表由每个列表的第二个元素组成。
一个例子:
[["a"; "b"; "c"]; ["d"; "e"; "f"]]
变成了
[["a"; "d"]; ["b"; "e"]; ["c"; "f"]]
我已经有了一个函数集合,它将获取列表列表并从每个列表中提取某个索引的元素。所以基本上使用前面的例子,我可以说gather(list, 1)
并获得["b"; "e"]
。我需要一种方法来完成List.length
次,并为每个元素索引使用聚集。
这导致我如此麻烦的原因是因为适用的限制(是的,这是针对学校的)。我不允许使用循环或局部/全局变量。我也不允许在函数体内使用'let'。是否有另一种方法可以递归地为每个元素索引使用gather
?是否还有其他函数我将以递归方式使用gather
,然后在rv
中重用该函数?如果是这样,你会怎么做?
答案 0 :(得分:7)
要克服对循环的限制,请使用递归函数。
使用let
的限制有点愚蠢。您可以通过将参数传递给函数来解决它,在某种程度上,编写(function x -> e) v
而不是let x = v in e
。但请将其视为暗示您应该利用List.map
和List.fold_left
等功能。
一般来说,列表不是数组;它们并不意味着使用元素的索引进行访问。当然,你可以做到,但它既麻烦又低效。访问列表的自然方式是使用模式匹配或List.hd
和List.tl
函数来拆分第一个元素和列表的其余部分,或者使用List.map
等函数遍历整个清单。
例如,如果您需要的只是每个列表的第一个元素,那么您可以使用
List.map List.hd lists
List.map List.tl lists
给出了每个列表的其余部分。这表明了一种递归方法:结果的头部是List.map List.hd lists
,结果的尾部是List.map List.tl lists
的转置。
let rec transpose lists =
List.map List.hd lists :: transpose (List.map List.tl lists)
现在,显然,我们需要在某个时候终止。假设所有列表都具有相同的长度,我们需要在列表为空时停止,我们可以通过查看第一个列表来测试。我将把它留作练习。
为了理解这段代码的作用,我建议使用#trace
指令在Ocaml toplevel中键入它,并在几个示例中观察操作。为此,如果您使transpose
函数单态化,将会有所帮助。
# let rec tranpose (lists : string list list) = …;;
val transpose : string list list -> string list list = <fun>
# #trace transpose;;
transpose is now traced.
# transpose [["a"; "b"; "c"]; ["d"; "e"; "f"]];;
transpose <-- [["a"; "b"; "c"]; ["d"; "e"; "f"]]
transpose <-- [["b"; "c"]; ["e"; "f"]]
transpose <-- [["c"]; ["f"]]
transpose <-- [[]; []]
transpose --> []
transpose --> [["c"; "f"]]
transpose --> [["b"; "e"]; ["c"; "f"]]
transpose --> [["a"; "d"]; ["b"; "e"]; ["c"; "f"]]
- : string list list = [["a"; "d"]; ["b"; "e"]; ["c"; "f"]]