OCaml函数帮助:涉及解析列表的列表

5
我正在尝试创建一个OCaml函数rv,它将遍历一个嵌套列表并重新组织元素到另一个嵌套列表中,使得第一个列表由每个原始列表的第一个元素组成,第二个列表由每个列表的第二个元素组成,以此类推。
例如:[["a"; "b"; "c"]; ["d"; "e"; "f"]]会变成[["a"; "d"]; ["b"; "e"]; ["c"; "f"]]
我已经有了一个名为gather的函数,它将获取一个嵌套列表并从每个列表中提取特定索引的元素。所以基本上使用前面的例子,我可以说gather(list, 1)并得到["b"; "e"]返回值。我需要一种方法来通过List.length次操作,并对每个元素索引使用gather。
造成这么多麻烦的原因是因为受到了限制(是的,这是为学校而设计的)。我不允许使用循环或局部 / 全局变量。我也不能在函数体内使用'let'。是否有另一种递归使用gather的方法?是否应该创建另一个函数递归使用gather,然后在rv中重复使用该函数?如果是,您会如何处理?
1个回答

8
为了克服循环的限制,可以使用递归函数。
对于使用“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)

现在,显然,我们需要在某个点终止。假设所有的列表长度相同,我们需要在列表为空时停止,这可以通过查看第一个列表来测试。我会将此留作练习。
为了理解这段代码在做什么,建议您在 Ocaml toplevel 中输入它,并使用 #trace 指令观察一些示例的操作。为此,如果您使 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"]]

非常感谢。有了您的帮助,我成功创建了该函数。非常感激! - user1040854
@Gilles,如果没有使用递归,你会怎么做呢? - jonnyd42
@jonnyd42 递归是这里的惯用方式。如果要求您在没有递归的情况下完成此操作,则可能是使用 fold_leftfold_right 的练习。 - Gilles 'SO- stop being evil'
@Gilles 这是我即将参加的考试的练习题。您能否给我一些提示,如何使用fold函数来解决它? - jonnyd42

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接