OCaml - 递归列表列表“if 表达式”

时间:2021-03-10 17:01:58

标签: if-statement recursion ocaml nested-lists

我想创建一个 rec 函数,比如 oddleninLdbl,它将列表 "L" 作为参数,并返回 "L" 的副本strong> 其中奇数长度的列表已“加倍” - 这里加倍意味着列表将与自身连接。

为了说明上述内容,我们的想法是:

oddleninLdbl [[]; [5]; [23;5]; [45;65;2]; []; [34;85;7;22;1]];;

返回以下内容:

[[]; [5;5]; [23;5]; [45;65;2;45;65;2]; []; [34;85;7;22;1;34;85;7;22;1]]

它也应该适用于任何列表类型,例如这里的字符串:

oddleninLdbl [[]; ["mi";"lo"]; ["la"]; ["lo";"co";"mo"]]

应该返回:

[[]; ["mi";"lo"]; ["la";"la"]; ["lo";"co";"mo";"lo";"co";"mo"]]

我一直在绕着这个转一圈。如果我没记错的话,我知道我的“if 表达式”应该围绕与列表中列表长度相关的内容,当它是 List.length mod 2 = 1 时,子列表应该与自身连接。

但是,我无法应用它,因为我从未使用过列表列表!这个函数应该怎么写?非常感谢您的帮助。

编辑 1:

我已尝试执行以下操作

let double_list l = l@l;;
let rec oddleninLdbl l =
if l = [] 
then []
else (
let t = List.tl l in
  if ((List.length l) mod 2 = 1) 
  then double_list l
  else oddleninLdbl t
) ;;

但我得到这个作为答案:

# oddleninLdbl [[]; [5]; [23;5]; [45;65;2]; []; [34;85;7;22;1]] ;;
- : int list list =
[[5]; [23; 5]; [45; 65; 2]; []; [34; 85; 7; 22; 1]; [5]; [23; 5]; [45; 65; 2]; []; [34; 85; 7; 22; 1]]

# oddleninLdbl [[]; ["mi";"lo"]; ["la"]; ["lo";"co";"mo"]] ;;
- : string list list =
[["mi"; "lo"]; ["la"]; ["lo"; "co"; "mo"]; ["mi"; "lo"]; ["la"];["lo"; "co"; "mo"]]

# oddleninLdbl [5;6;3;4;6] ;;
- : int list = [5; 6; 3; 4; 6; 5; 6; 3; 4; 6]
# oddleninLdbl [4;5] ;;
- : int list = [5; 5]
# oddleninLdbl [4;5;8] ;;
- : int list = [4; 5; 8; 4; 5; 8]
# oddleninLdbl [4;5;8;9] ;;
- : int list = [5; 8; 9; 5; 8; 9]
# oddleninLdbl ["mi";"lo"] ;;
- : string list = ["lo"; "lo"]
# oddleninLdbl ["mi";"lo";"co"] ;;
- : string list = ["mi"; "lo"; "co"; "mi"; "lo"; "co"]
# oddleninLdbl ["mi"] ;;
- : string list = ["mi"; "mi"]

显然不是这个函数所期望的。我的代码中的罪魁祸首在哪里?有人可以给我他们的诊断吗?

编辑 2:

let double_list l = (List.hd l)@(List.hd l);;
let p l = (List.length (List.hd l)) mod 2 = 1 ;;
let apply p double_list l = 
  let rec aux acc l = match l with
  | [] -> List.rev acc
  | hd :: tl -> 
    let hd = if p hd then double_list hd else hd in
    aux (hd :: acc) tl ;;

我在这个问题上遇到了语法错误,我在这里跳过了一些东西。你怎么看?

1 个答案:

答案 0 :(得分:1)

处理不易理解的结构时,一个好的经验法则是分而治之。

您的问题在这里可以分解为多个部分:

  1. 获取列表列表并返回列表列表
  2. 当列表为奇数时,加倍

所以,你应该首先创建一个简单的函数来将列表加倍

let double_list l = (* double the elements in a list of elements *)

然后你应该创建一个函数来遍历元素列表并在当前元素对应于谓词p时做一些事情

let apply p f l = 
  let rec aux acc l = match l with
  | [] -> List.rev acc
  | hd :: tl -> 
    let hd = if p hd then f hd else hd in
    aux (hd :: acc) tl
  in aux [] l

现在您唯一需要的是正确的谓词 p 并且您已经实现了您的函数 f,它应该可以完美运行。

总而言之,当您可以通过将问题分解为更小的问题来简化问题时,永远不要害怕表面上的复杂性。

编辑:

这是您的代码:

let double_list l = l@l;;

let rec oddleninLdbl l =
  if l = [] 
  then []
  else (
    let t = List.tl l in
    if ((List.length l) mod 2 = 1) 
    then double_list l
    else oddleninLdbl t
) ;;

这不是惯用的 OCaml,请尝试使用模式匹配:

let double_list l = l@l;;

let rec oddleninLdbl l =
  match l with
  | [] -> [] 
  | hd :: tl ->
    if ((List.length l) mod 2 = 1) 
    then double_list l
    else oddleninLdbl tl;;

如您所见,您正在检查 l 是否为奇数,而不是其元素,这不是您想要做的。您想检查 hd 是否为奇数并将其加倍,然后继续遍历列表的其余部分并将 hd(加倍或不加倍)附加到递归调用的结果中。在这里,当 l 为奇数时,您将其加倍并停止,如果不是,您只需忘记列表的头部并将其余部分加倍(因为其余部分在逻辑上将是一个奇数列表)。

相关问题