0

다음 자체 코드는 OCaml의 문제 (코드 생성 가능성이 있음)를 강조 표시합니다. 어레이 x는 [0..9]에있는 노드에 대한 연결 정보를 가지고 있습니다. init_graph 함수는 원래 모든 노드에 대해 들어오는 노드의 명시 적 배열을 구성했습니다. 아래에 표시된 축소 버전은 연결된 두 개의 노드를 인쇄합니다.가능한 OCaml 코드 생성 버그

함수 init_graph2는 "쓸모없는"else 분기를 제외하고는 init_graph와 동일합니다. 그러나이 두 함수에 의해 산출 된 결과는 상당히 다릅니다. 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() 

업데이트 : 분명히 잘못이다 "쓸모없는"등의 init_graph2에서 Ocamllint의에 플래그 다른 지점입니다.

둘째로, camlspotter가 제안한 들여 쓰기 방법은이 시나리오에서 오도 될 수 있습니다. Ocamllint의 조언에 따라 else 브랜치를 주석 처리합니다. taureg-mode를 사용하는 Emacs는 명시 적으로 모든 것이 정상이라고 믿도록 지시하지 않는 한이 코드를 다시 들여 쓰지 않습니다.

이러한 상황에서 경고를 발생시키는 보풀 같은 도구가 필요합니다. 나는 이것에 대한 좋은 제안을 기다리고있다.

감사합니다.

답변

3

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/thenif/then 의해 제어되고있다.

다른 말로하면 ;let ... in보다 우선합니다. 그래서 let x = y in a ; blet x = y in (a; b)으로, (let x = y in a); b으로 해석되지 않습니다.

"쓸데없는"else을 포함 시켰을 때 생각했던 것처럼 구문이 분석됩니다.

OCaml에서 if/thenlet과 혼합 할 때 매우 조심해야합니다. 이런 문제가 생겼어. if/thenelse이 하나의 표현식을 제어한다는 일반적인 직감은 사실이지만 표현식 중 하나가 let 일 때 잘못되기 쉽습니다.

+0

작은 부록 : let .. in과 begin .. 행을 교환하면 원하는 그룹이 만들어집니다. – user3240588

+0

설명합니다. 나는 taureg-mode를 사용하고 있지만, 너무 미묘하고 예기치 않기 때문에 들여 쓰기를 놓쳤습니다. ocamllint와 같은 자동 도구가 잠재적 인 문제 발생 지점으로 강조 표시합니까? 내 코드 기반의 여러 위치에 비슷한 코드가 있습니다. 또한, 비슷한 두 가지를 복용 ocaml에 대한 내 다른 질문을 설명 할 수 있습니까? 감사! – user5754881

1

Jeffrey가 대답 한대로 코드 들여 쓰기에서 읽을 수있는 의도는 코드가 실제로 구문 분석되는 것과 매우 다릅니다.

OCaml 용 caml-mode, tuareg-mode, ocp-indent 및 vim 플러그인과 같은 자동 들여 쓰기 도구를 사용하면 이러한 종류의 실수를 피할 수 있습니다. 자동 들여 쓰기 init_graph의 두 번째 if을, 당신이 바로 첫 번째 ifthen clasuse에서 찾을 수 있습니다함으로써

.