可能的OCaml代码生成错误 [英] Possible OCaml code generation bug

查看:133
本文介绍了可能的OCaml代码生成错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面的自包含代码突出了OCaml中的一个问题,可能与代码生成有关. 数组x在[0..9]中具有节点的连接信息.函数init_graph最初为每个节点构造了传入节点的显式数组.下面显示的简化版本仅打印两个连接的节点.

The following self contained code highlights a problem in OCaml, possibly with the code generation. Array x has connectivity information for nodes in [0..9]. Function init_graph originally constructed explicit arrays of incoming nodes for every node. The reduced version shown below just prints the two connected nodes.

函数init_graph2与init_graph相同,除了无用"的else分支.但是,这两个函数所产生的输出是完全不同的.您可以运行它,并看到init_graph在某些情况下会跳过第二个if-then-else!

Function init_graph2 is identical to init_graph except for a "useless" else branch. But outputs produced by these two functions are quite different. You can run it and see that init_graph skips over the second if-then-else in some cases!

我们已经在3.12.1版(适当替换了make_matrix),4.03.0和4.03.0 + flambda上运行了该程序.他们都有同样的问题.

We have run this program on version 3.12.1 (with make_matrix substituted appropriately), 4.03.0 and 4.03.0+flambda. All of them have the same problem.

我一直在处理OCaml神秘地跳过分支或在某些情况下同时占用两个分支的问题和相关问题.多亏了合作者,我们才能够将实际代码缩减为一个很小的自包含示例.

I have been dealing with this and related problems where OCaml mysteriously skips branches or in some cases takes both branches. Thanks to a collaborator we were able to pare down the real code to a small self contained example.

关于这里发生的事情有什么想法吗?有办法避免这种情况和相关问题吗?

Any ideas on what's going on here? And is there a way to avoid this and related problems?

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分支标记为无用",这显然是错误的.

Update: Ocamllint flags the else branch in init_graph2 as "useless" which is clearly wrong.

第二,在这种情况下,camlspotter建议的缩进方法可能会产生误导.我们遵循Ocamllint的建议,并注释掉else分支.除非明确要求让我们相信一切都很好,否则带有taureg模式的Emacs不会重新缩进该代码.

Second, the indentation method suggested by camlspotter can be misleading in precisely this scenario. We follow Ocamllint advice and comment out the else branch. Emacs with taureg-mode doesn't re-indent this code unless explicitly asked leading us to believe everything is fine.

需要的是一种类似皮棉的工具,在这种情况下会发出警告.我正在等待关于此的好的建议.

What is needed is a lint like tool that raises warning in these situations. I am waiting for good suggestions for this one.

谢谢.

推荐答案

您的问题似乎出在let ... in的处理上.此构造引入了一系列用分号分隔的表达式,而不是单个表达式.所以这段代码:

Your problem appears to be with the handling of let ... in. This construct introduces a series of semicolon-separated expressions, not a single expression. So this code:

   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控制.

In other words, both the first begin/end and the second if/then are controlled by the first if/then.

另一种说法是,;的优先级高于let ... in.因此let x = y in a ; b被解析为let x = y in (a; b),而不是(let x = y in a); b.

Another way to say that is that ; has higher precedence than let ... in. So let x = y in a ; b is parsed as let x = y in (a; b), not as (let x = y in a); b.

当您包含无用的" else时,事物会像您认为的那样进行解析.

When you included the "useless" else, things parse like you think they should.

是的,在OCaml中将if/thenlet混合时必须非常小心.我遇到了这样的问题. if/thenelse控制单个表达式的一般直觉虽然为true,但当其中一个表达式为let时很容易出错.

It's true, you have to be pretty careful when mixing if/then with let in OCaml. I have had problems like this. The general intuition that if/then and else control a single expression, while true, is easy to get wrong when one of the expressions is a let.

这篇关于可能的OCaml代码生成错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆