搜索递归记录类型OCaml

时间:2017-04-13 17:43:25

标签: list recursion ocaml record

我试图以递归方式搜索作为递归记录类型的记录中的字段值。

我的记录类型是

type node = {node_name:string; node_branch:node list}

首先,我试图像这种类型的变量一样遍历树:

let tree = {node_name="trunk"; node_branch=[{node_name="branch1L:1"; node_branch=[{node_name="branch2L:1"; node_branch=[]};
                                                                                  {node_name="foo_node"; node_branch=[]};];};
                                            {node_name="branch1L:2"; node_branch=[]}];}
    in
    let target = "foo_node" in
    print_newline();
    search_tree_for_name target tree;

这个想法是,它有点像这样:

                    trunk
            __________|_________
           |                    |
       branch1L:1           branch1L:2
    ______|_______
   |              |
branch2:1       foo_node

所以我正在搜索字段值为node_name = foo_node的记录。我遍历它的方式,只是为了证明我的逻辑:

let rec search_tree_for_name target_name in_tree =
  if in_tree.node_name = target_name then
    (* First check the node_name *)
      Format.printf "[%s] Target node_name found\n" in_tree.node_name
  else
    (* Then evaluate the branches *)
    begin
      match in_tree.node_branch with
      | [] ->
         Format.printf "[%s] Branches: 0; End of branch\n" in_tree.node_name
      | head :: tail ->
         Format.printf "[%s] Branches: %i\n" in_tree.node_name (List.length in_tree.node_branch);
         search_tree_for_name target_name head;
         List.iter (search_tree_for_name target_name ) tail;
    end

打印出来:

[trunk] Branches: 2
[branch1L:1] Branches: 2
[branch2L:1] Branches: 0; End of branch
[foo_node] Target node_name found
[branch1L:2] Branches: 0; End of branch

现在,我真正想要的是看看是否存在node_name正在寻找的实例,所以我只想返回一个布尔值。

我尝试的是(我为测试目的创建了一个新功能):

let rec check_tree_for_name target_name in_tree =
  if in_tree.node_name = target_name then
    begin
    (* First check the node_name *)
      Format.printf "[%s] Target node_name found\n" in_tree.node_name;
      true
    end 
  else
    (* Then evaluate the branches *)
    begin
      match in_tree.node_branch with
      | [] ->
         Format.printf "[%s] Branches: 0; End of branch\n" in_tree.node_name;
         false
      | head :: tail ->
         Format.printf "[%s] Branches: %i\n" in_tree.node_name (List.length in_tree.node_branch);
         check_tree_for_name target_name head;
         List.iter (check_tree_for_name target_name ) tail;
    end

我在传递给List.iter的函数时遇到以下错误:

Error: This expression has type node -> bool
       but an expression was expected of type node -> unit
       Type bool is not compatible with type unit 

然后我将最后一个模式匹配块更改为

  | head :: tail ->
     Format.printf "[%s] Branches: %i\n" in_tree.node_name (List.length in_tree.node_branch);
     if check_tree_for_name target_name head then true
     else List.iter (check_tree_for_name target_name ) tail;

因此if条件检查函数调用的返回和传递它的种类。现在我少了一个错误,但我仍然有同样的错误。

我的理解是List.iter会将传递列表的每个元素作为传递给List.iter的函数的最后一个参数传递。所以这应该迭代我传递给它的列表元素,唯一的出路是node_name字段匹配我要查找的内容或者它是否为空列表。

我在两个实现之间看到的主要区别在于,第一个实现持续到所有节点都被评估,第二个实现一直持续到找到我正在寻找的内容。

任何建议都会受到赞赏,如果可能的话,可以简单解释一下我的错误或至少突出我的错误(我觉得我正在学习很多功能性编程概念,但仍然有一些正在逃避我。)

1 个答案:

答案 0 :(得分:4)

好吧,在最后一个分支中,如果我理解得很好,你想知道tail中的一个节点是否有好名字。在这种情况下,只需使用List.exists

| head :: tail ->
         Format.printf "[%s] Branches: %i\n" in_tree.node_name 
            (List.length in_tree.node_branch);
         check_tree_for_name target_name head ||
         List.exists (check_tree_for_name target_name ) tail

它会按预期工作(请注意||check_tree...之间的List.exists ...。如果check_tree ...返回true,则您不需要检查列表的其余部分。

小解释:

在OCaml中,模式匹配的所有分支必须具有相同的类型。在这里,您的第一个分支有一个boolean类型,因为您使用false结束了该类型,而第二个分支的结果为unit,因为您使用List.iter ...结束了该类型。 List.iter的类型为('a -> unit) -> 'a list -> unit,并将其第一个参数应用于作为第二个参数给出的列表中的所有元素。 List.exists类型为('a -> bool) -> 'a list -> bool,如果作为第二个参数给出的列表中的一个元素满足作为第一个参数给出的谓词,则返回true false如果不存在这样的元素

同等地,if cond then e1 else e2就像一个模式匹配,因此e1e2应该具有相同的类型,因此您的第二次尝试与第一次尝试一样错误。 ; - )