【问题标题】:Search Recursive Record Type OCaml搜索递归记录类型 OCaml
【发布时间】:2017-09-09 22:48:13
【问题描述】:

我正在尝试在递归记录类型的记录中递归搜索字段值。

我的记录类型是

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 字段是否与我正在查找的内容匹配,或者它是否是一个空列表。

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

任何建议都将不胜感激,如果可能的话,简要说明我哪里出错了,或者至少突出我哪里出错(我觉得我正在学习很多函数式编程概念,但仍然有一些正在躲避我)。

【问题讨论】:

  • Lhooq 的回答是对的,但我还想指出,您寻求的功能确实是单行的:let rec check name tree = tree.name = name || List.exists (check name) tree.branches(名称缩短以适合评论)
  • @AndreasRossberg 谢谢你,很高兴看到更多惯用的做事方式

标签: list recursion ocaml record


【解决方案1】:

好吧,在最后一个分支中,如果我理解得很好,你想知道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 应该有相同的类型,所以你的第二次尝试和第一次一样错误。 ;-)

【讨论】:

  • 另外,值得注意的是List.exists 是一个快捷操作符:它会在遇到“有效”元素时立即返回。如果你想亲眼看看:List.exists (fun x -> print_int x; x = 3) [1;2;3;4;5];;
  • 谢谢你,对我来说这很明显,但值得一提。 ;-)
  • 是的,那是供 OP 和其他人阅读的旁注。 :)
  • Lhooq 谢谢你的解释!这就说得通了。我在想程序流会进入 List.iter 调用的函数,然后退出。我没想到它会回来。但现在我还在 List 文档中看到它返回单位。感谢@RichouHunter 的澄清,这很有帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-06-10
  • 1970-01-01
  • 2015-05-03
  • 1970-01-01
  • 1970-01-01
  • 2016-09-06
  • 2013-06-17
相关资源
最近更新 更多