Синтаксическая ошибка OCaml в конце вложенных циклов

#ocaml #caml

Вопрос:

Я пытаюсь создать функцию, которая вычисляет шансы команды на победу в турнире на основе приведенных здесь правил. У меня уже есть реализация python здесь, но я хотел попробовать сделать это на OCaml, языке, который для меня очень нов. У меня возникают проблемы с синтаксическими ошибками, и мне непонятно, почему я получаю ошибки. Я также знаю, что, поскольку я «перевожу» код с python, он не является оптимальным для OCaml, поэтому, если есть лучшие «способы OCaml» делать то, что я пытаюсь сделать, я бы тоже хотел услышать эту обратную связь.

 open Format
open List

let tourney = [1;16;8;9;5;12;4;13;6;11;3;14;7;10;2;15];;

let odds x =
  function y -> if x > y then 1.0-.(x/.x .y) else y/.(x .y);;



let replace l pos a  = List.mapi (fun i x -> if i = pos then a else x) l;;

let rec index l a =
  match l with
  | [] -> -1
  | x::xs -> if x != a then 1   index xs a else 0;;

    
let seed_odds L seed =
  let team_ind = index tourney seed
  and rounds = [2;4;8;16]
  and round_odds = [] ;

  for i = 1 to length rounds do
    let temp = [] in
    for j = 1 to length tourney do
      0.0 :: temp;
    done;
    temp :: round_odds;
  done;
  
  for r = 0 to (length rounds)-1 do
    let groups = (length tourney) / (nth rounds r);

    for i = 0 to groups do
      let teams = slice tourney i*(nth rounds r) (i 1)*(nth rounds r);
      for t = 0 to (length teams) do
        let odds_to_advance = ref 0.0;

        
        let teams_ =
          if t < ((length teams) / 2) then slice teams ((length teams)/2) (length teams)-1 else slice teams 0 ((length teams)/2)-1 ;

        for t_ = 0 to length teams_ do
          if nth teams t != nth teams_ t_ then
            begin
              if (nth rounds r) = 2 then
                begin
                  odds_to_advance := odds_to_advance  . odds (nth teams t) (nth teams_ t_);
                end
              else
                begin
                  odds_to_advance := odds_to_advance  . (odds (nth teams t) (nth teams_ t_)) *. (nth (nth round_odds r-1 ) (index tourney (nth teams_ t_) )) ;
                end
            end
          else ()
        done;
        if nths rounds r > 2 then
          begin
            odds_to_advance := odds_to_advance *. (nth (nth round_odds r-1 ) (index tourney (nth teams t) )) ;
          end
        else ()
        (*replace (nth round_odds r) (i * (nth rounds r)   t) odds_to_advance ;*)
      done;
    done;
  done;
 

Хорошо, ответы на мои предыдущие вопросы имеют смысл, спасибо! Я обновил код и все почистил:

 open Format
open Array
open List

let tourney = [1.;16.;8.;9.;5.;12.;4.;13.;6.;11.;3.;14.;7.;10.;2.;15.];;

let odds x =
  function y -> if x > y then 1.0-.(x/.x .y) else y/.(x .y);;

printf "The odds are %f" (odds 2. 15.);;

let replace l pos a  = List.mapi (fun i x -> if i = pos then a else x) l;;

let rec index l a =
  match l with
  | [] -> -1
  | x::xs -> if x != a then 1   index xs a else 0;;

let rec fold_until f acc n = function
  | [] -> (acc, [])
  | h :: t as l -> if n = 0 then (acc, l)
              else fold_until f (f acc h) (n - 1) t

let slice list i k =
  let _, list = fold_until (fun _ _ -> []) [] i list in
  let taken, _ = fold_until (fun acc h -> h :: acc) [] (k - i   1) list in
  List.rev taken;;


let seed_odds l seed =
  let team_ind = index tourney seed
  and rounds = [2;4;8;16]
  
  and round_odds = make_matrix 4 16 0.0 in
  
  for r = 0 to (length rounds)-1 do
    let groups = (length tourney) / (nth rounds r) in

    for i = 0 to groups-1 do
      let teams = slice tourney (i*(nth rounds r)) ((i 1)*(nth rounds r)) in
      for t = 0 to (length teams)-1 do
        let odds_to_advance = ref 0.0 in

        
        let teams_ =
          if t < ((length teams) / 2) then slice teams ((length teams)/2) ((length teams)-1) else slice teams 0 (((length teams)/2)-1) in

        for t_ = 0 to (length teams_)-1 do
          if nth teams t != nth teams_ t_ then
            begin
              if (nth rounds r) = 2 then
                begin
                  let od = odds (nth teams t) (nth teams_ t_) in
                  odds_to_advance := !odds_to_advance  . od
                end
              else
                begin
                  let od = odds (nth teams t) (nth teams_ t_)
                  and prev = round_odds.(r-1).(index tourney (nth teams_ t_) ) in
                  odds_to_advance := !odds_to_advance  . od *. prev
                end
            end
          else ()
        done

        if (nth rounds r) > 2 then
          begin
            let prev = round_odds.(r-1).(index tourney (nth teams t)) in
            odds_to_advance := !odds_to_advance *. prev
          end
        else()
        
        round_odds.(r).((i*(nth rounds r)) t) <- !odds_to_advance

      done
    done
  done
  round_odds.(3).(team_ind);;
 


printf "The odds of two winning right now are %f" (seed_odds tourney 2.);;


 

Единственная ошибка, которую я сейчас получаю, — это:

 66 |         if (nth rounds r) > 2 then
             ^^
Error: Syntax error
 

Не уверен, в чем сейчас проблема, потому что я проверил операторы let, убедился, что начало/конец были закрыты и т. Д.

Комментарии:

1. Вам нужно показать конкретное сообщение об ошибке, которое вы видите, и местоположение в коде, на которое оно ссылается.

Ответ №1:

Первое, что я вижу, это то, что у вас есть довольно много примеров let без совпадения in .

На верхнем уровне модуля у вас может быть let name = value . Это объявляет значение, которое будет экспортировано из модуля (грубо говоря).

Везде в остальном (в частности, внутри определений функций) у каждого let должно быть соответствие in . let Выражение выглядит так:

 let v = expr1 in expr2
 

Он объявляет локальную переменную v со значением expr1 . Область действия этой локальной переменной такова expr2 . Эта in expr2 деталь не является необязательной.

Вот строки, в которых у вас let нет совпадения in :

 let team_ind = index tourney seed

let groups = (length tourney) / (nth rounds r);

let teams = slice tourney i*(nth rounds r) (i 1)*(nth rounds r);

let odds_to_advance = ref 0.0;

let teams_ =
 

Все это синтаксические ошибки. В общем, вы можете исправить их, добавив in и удалив точку с запятой, если она у вас есть.

В качестве дополнительного комментария в OCaml точки с запятой используются для разделения выражений, которые должны оцениваться последовательно. Точка с запятой не является терминатором оператора (как в языках, на которые влияет C). Это разделитель, во многом похожий на оператор.

Обновить

В качестве еще одного комментария, как только вы заставите свой код работать, вам, возможно, захочется поискать способы сделать его более идиоматичным OCaml. Например, использование List.nth почти всегда является плохой практикой, потому что для достижения n-го элемента списка требуется линейное время. OCaml не похож на некоторые другие языки, где то, что они называют списками, на самом деле является массивами (случайным образом доступными в постоянное время). Списки OCaml на самом деле являются списками (как в Lisp и других языках FP).

Комментарии:

1. Спасибо за ответ, Джеффри. Я оставил исходный код для справки, но изменил его, чтобы отразить предложенные вами изменения. Сейчас у меня проблема на линии 66, но я не вижу ни одной из проблем, о которых вы упомянули выше.

2. Просто быстро взглянув, кажется, что вам нужна точка с запятой между циклом и if . Точки с запятой являются разделителями больше, чем терминаторами, но они вам нужны, когда нужно разделить два выражения! 🙂

3. Ладно, в этом есть смысл. Означает ли это, что после операторов if в цикле также должны быть точки с запятой?

4. Я не вижу, следуют ли утверждения друг за другом. Я вижу только оператор if, вложенный в другой оператор if. Поэтому я не думаю, что где-то еще вам нужна точка с запятой. Но компилятор является авторитетом в этом вопросе.

5. Поэтому после долгих проб и ошибок разработчик захотел поставить точку с запятой после else() над строкой, начинающейся с round_odds. (r), а также после сделанных заявлений. Спасибо за помощь, Джеффри!