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