以下自包含代码突出显示了OCaml中的问题,可能代码生成。 数组x具有[0..9]中节点的连接信息。函数init_graph最初为每个节点构造了显式的传入节点数组。下面显示的简化版本只打印两个连接的节点。
函数init_graph2与init_graph相同,除了“无用的”else分支。但是这两个功能产生的输出是完全不同的。你可以运行它,看看在某些情况下init_graph会跳过第二个if-then-else!
我们已经在3.12.1版本上运行了这个程序(make_matrix被适当替换),4.03.0和4.03.0 + flambda。所有这些都有同样的问题。
我一直在处理这个和相关的问题,其中OCaml神秘地跳过分支或在某些情况下需要两个分支。感谢合作者,我们能够将真实代码削减为一个小的自包含示例。
关于这里发生了什么的任何想法?有没有办法避免这个和相关的问题?
let x =
let arr = Array.make_matrix 10 10 false in
begin
arr.( 6).( 4) <- true;
arr.( 2).( 9) <- true;
end;
arr
let init_graph () =
for i = 0 to 9 do
for j = 0 to (i-1) do
begin
if x.(i).(j) then
let (i_inarr, _) = ([||],[||]) in
begin
Format.printf "updateA: %d %d \n" i j;
end
(* else () *)
;
if x.(j).(i) then
let (j_inarr, _) = ([||],[||]) in
begin
Format.printf "updateB: %d %d \n" i j;
end
end
done
done;
Format.printf "init_graph: num nodes is %i\n" 10
let init_graph2 () =
for i = 0 to 9 do
for j = 0 to (i-1) do
begin
if x.(i).(j) then
let (i_inarr, _) = ([||],[||]) in
begin
Format.printf "updateA: %d %d \n" i j;
end
else ()
;
if x.(j).(i) then
let (j_inarr, _) = ([||],[||]) in
begin
Format.printf "updateB: %d %d \n" i j;
end
end
done
done;
Format.printf "init_graph: num nodes is %i\n" 10
let test1 = init_graph ()
let test2 = init_graph2 ()
更新: Ocamllint将init_graph2中的else分支标记为“无用”,这显然是错误的。
其次,camlspotter建议的缩进方法在这种情况下可能会产生误导。我们遵循Ocamllint建议并注释掉其他分支。具有taureg模式的Emacs不会重新缩进此代码,除非明确要求我们相信一切都很好。
需要的是一种类似工具的lint工具,可以在这些情况下发出警告。我正在等待这个好建议。
感谢。
答案 0 :(得分:4)
您的问题似乎与let
... in
的处理有关。此构造引入了一系列以分号分隔的表达式,而不是单个表达式。所以这段代码:
if x.(i).(j) then
let (i_inarr, _) = ([||],[||]) in
begin
Format.printf "updateA: %d %d \n" i j;
end
(* else () *)
;
if x.(j).(i) then
let (j_inarr, _) = ([||],[||]) in
begin
Format.printf "updateB: %d %d \n" i j;
end
实际上这样解析:
if x.(i).(j) then
let (i_inarr, _) = ([||],[||]) in
begin
Format.printf "updateA: %d %d \n" i j;
end
(* else () *)
;
if x.(j).(i) then
let (j_inarr, _) = ([||],[||]) in
begin
Format.printf "updateB: %d %d \n" i j;
end
换句话说,第一个begin/end
和第二个if/then
都由第一个if/then
控制。
另一种说法是;
的优先级高于let ... in
。因此let x = y in a ; b
被解析为let x = y in (a; b)
,而不是(let x = y in a); b
。
当你包含“无用的”else
时,事情会像您认为的那样解析。
确实,在OCaml中将if/then
与let
混合时必须非常小心。我有这样的问题。 if/then
和else
控制单个表达式的一般直觉虽然为真,但当其中一个表达式为let
时很容易出错。
答案 1 :(得分:1)
正如杰弗里所回答的那样,你的代码缩进中的可读性与实际解析代码的方式截然不同。
使用适当的自动缩进工具可以避免这种错误,例如caml-mode,tuareg-mode,ocp-indent和OCaml的vim插件。
通过自动缩进if
的第二个init_graph
,您可以立即发现它位于第一个if
then
条款下。