Debugging OCaml,引用和树的意外行为
我试图给树的每个元素分配一个数字。我认为使用Debugging OCaml,引用和树的意外行为,debugging,tree,ocaml,ref,Debugging,Tree,Ocaml,Ref,我试图给树的每个元素分配一个数字。我认为使用refs会使任务更容易,但我遇到了一个奇怪的行为:分配的数字不是唯一的,也没有清晰的模式出现。我设法修复了这个bug(在行中添加了let unbox=!second_ref),但我不明白发生了什么 输出控制台中的第一个树只是确保print_tree函数输出它应该输出的内容 但是,第二次打印的预期输出应该与第三棵树的输出完全相同。我错过了什么 type ('a, 'b) tree = | Node of 'a * ('a, 'b) tree * ('
refs
会使任务更容易,但我遇到了一个奇怪的行为:分配的数字不是唯一的,也没有清晰的模式出现。我设法修复了这个bug(在行中添加了let unbox=!second_ref),但我不明白发生了什么
输出控制台中的第一个树只是确保print_tree
函数输出它应该输出的内容
但是,第二次打印的预期输出应该与第三棵树的输出完全相同。我错过了什么
type ('a, 'b) tree =
| Node of 'a * ('a, 'b) tree * ('a, 'b) tree
| Leaf of 'b
let print_tree tree string_of_node string_of_leaf =
let rec print indent tree =
match tree with
| Leaf (l) -> print_string (indent^" -> "^string_of_leaf(l)^"\n")
| Node (n, left, right) ->
Printf.printf "%s-----------\n" indent;
print (indent ^ "| ") left;
Printf.printf "%s%s\n" indent (string_of_node(n));
print (indent ^ "| ") right;
Printf.printf "%s-----------\n" indent
in print "" tree
let myTree = Node(1,Node(2,Leaf(3),Leaf(4)),Node(5,Leaf(6),Leaf(7))) ;;
let first_ref = ref 0 ;;
let rec bug tree =
first_ref := !first_ref+ 1;
match tree with
|Leaf(a) -> Leaf(!first_ref)
|Node(n,l,r) -> Node(!first_ref, bug l, bug r) ;;
let second_ref = ref 0 ;;
let rec bug_fixed tree =
second_ref := !second_ref + 1;
let unboxed = !second_ref in
match tree with
|Leaf(a) -> Leaf(unboxed)
|Node(n,l,r) -> Node(unboxed, bug_fixed l, bug_fixed r) ;;
let bug_tree = bug myTree ;;
let bug_fixed_tree = bug_fixed myTree ;;
print_tree myTree string_of_int string_of_int ;
print_tree bug_tree string_of_int string_of_int ;
print_tree bug_fixed_tree string_of_int string_of_int ;
输出如下:
-----------
| -----------
| | -> 3
| 2
| | -> 4
| -----------
1
| -----------
| | -> 6
| 5
| | -> 7
| -----------
-----------
-----------
| -----------
| | -> 7
| 7
| | -> 6
| -----------
7
| -----------
| | -> 4
| 4
| | -> 3
| -----------
-----------
-----------
| -----------
| | -> 7
| 5
| | -> 6
| -----------
1
| -----------
| | -> 4
| 2
| | -> 3
| -----------
-----------
在您的bug
函数中,有一个有问题的表达式:
Node(!first_ref, bug l, bug r)
它的行为取决于参数的求值顺序:bug l
和bug r
增量first\u ref
,因此传递的值可能不是您想要的值
您可以通过执行以下操作强制执行订单:
let v = !first ref in
let new_l = bug l in
let new_r = bug r in
Node (v, new_l, new_r)
在您的bug
函数中,有一个有问题的表达式:
Node(!first_ref, bug l, bug r)
它的行为取决于参数的求值顺序:bug l
和bug r
增量first\u ref
,因此传递的值可能不是您想要的值
您可以通过执行以下操作强制执行订单:
let v = !first ref in
let new_l = bug l in
let new_r = bug r in
Node (v, new_l, new_r)
只是为了给这个答案增加一点背景。理论上,由于没有副作用,在纯函数式语言中,评估顺序并不重要。当然,使用引用打破了这种情况,因此使用了局部绑定技巧。值得注意的是,OCaml中没有指定求值顺序,这符合语言的功能性质。@RichouHunter,OCaml远不是纯粹的,除了可变状态之外还有很多其他影响,例如异常、非终止、I/O等。因此,我没有理由不指定求值顺序。“这是它最令人讨厌的陷阱之一。我完全同意,”安德烈亚斯罗斯伯格说。我想知道现在指定它会对现有的实现和代码库产生什么影响。@RichouHunter,理论上,它只会影响当前有缺陷的程序。只需为这个答案添加一点上下文即可。理论上,由于没有副作用,在纯函数式语言中,评估顺序并不重要。当然,使用引用打破了这种情况,因此使用了局部绑定技巧。值得注意的是,OCaml中没有指定求值顺序,这符合语言的功能性质。@RichouHunter,OCaml远不是纯粹的,除了可变状态之外还有很多其他影响,例如异常、非终止、I/O等。因此,我没有理由不指定求值顺序。“这是它最令人讨厌的陷阱之一。我完全同意,”安德烈亚斯罗斯伯格说。我想知道现在指定它会对现有的实现和代码库产生什么影响。@RichouHunter,理论上,它只会影响当前有缺陷的程序。这可能是离题了,但您对类型树的定义让我困惑。叶子的类型可能与节点的类型不同?这可能与本文的主题无关,但您对类型树的定义让我感到困惑。叶的类型可能与节点的类型不同?