ocamlcaml

OCaml Syntax Error at the end of nested loops


I'm trying to create a function that calculates the odds of a team winning a tournament based on the rules here. I already have a python implementation here, but I wanted to try and do it in OCaml, a language that is very new to me. I'm running into issues with syntax errors, and it's not clear to me why I'm getting the errors. I also know that, because I'm "translating" the code from python, it is not optimal for OCaml, so if there better "OCaml ways" of doing the things I'm trying to do, I'd like to hear that feedback as well.

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;

Ok, the answers to my previous issues make sense, thank you! I've updated the code and cleaned things up:

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.);;


The only error I get now is:

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

Not sure what the issue is now, because I've checked over the let statements, made sure begin/ends were closed, etc.


Solution

  • The first thing I see is that you have quite a few instances of let with no matching in.

    At the top level of a module you can have let name = value. This declares a value to be exported from the module (roughly speaking).

    Everywhere else (inside function definitions in particular), every let has to have a matching in. The let expression looks like this:

    let v = expr1 in expr2
    

    It declares a local variable v with the value expr1. The scope of this local variable is expr2. The in expr2 part is not optional.

    Here are the lines where you have let with no matching 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_ =
    

    All of these are syntax errors. In general you can fix them by adding in and removing a semicolon if you have one.

    As a side comment, in OCaml semicolons are used to separate expressions that should be evaluated sequentially. The semicolon is not a statement terminator (as in languages influenced by C). It's a separator, similar in many ways to an operator.

    Update

    As another side comment, once you get your code working you might want to look for ways to make it more idiomatic OCaml. For example, using List.nth is almost always bad practice because it takes linear time to reach the nth element of a list. OCaml is not like some other languages where what they call lists are actually arrays (randomly accessible in constant time). OCaml lists are really lists (as in Lisp and other FP languages).