Anyone using OCaml for 2024 AOC?
I have not done any coding for months – so using this opportunity to brush up on my novice OCaml skills.
Anyone using OCaml for 2024 AOC?
I have not done any coding for months – so using this opportunity to brush up on my novice OCaml skills.
I had tried… my version differs… GitHub - F-Loyer/AoC2024
I guess
List.find_all (fun x -> x = v) lst |> List.length
is more clever than my
List.fold_left (fun acc m -> if n=m then acc+n else acc) 0
.
I’ve been doing it for a few years now, trying to catch up to past editions whenever I have the time (meaning, never). It’s a neat sandbox to play with some syntax tricks (abusing .%{}
and the like until this does not feel like OCaml anymore ) and it’s great inspiration for coding exercises for students.
A word of advice though, during AOC season, it is best to put a warning or spoiler tags in discussions about particular problems. The AOC author also asks to not publish the full text of puzzles or one’s particular input (so I add mine to a private repo, which is a submodule of my main one).
Now, back to meaningful work until the next treat.
My Reason solutions are here: ezrast / advent2024 · GitLab
I’m also participating this year: GitHub - vshender/advent-of-code: My solutions to the Advent of Code problems.
how can i rewrite my day 3 to make it more readable? it just looks really really bad
Your part1 is already pretty readable, for that kind of code. I think you can only improve it by using a regex engine or a tool like ocamllex or sedlex.
For part 2, maybe you could simplify a bit by dropping the text that is between don't()
and do()
(reconstructing a new string) and apply part1 on that. Maybe insert a phony #
so that mul(1,2don't()do())
won’t be considered valid.
My two cents.
An easy thing to do would be to swap the order of the tuple you are matching on such that the previous character appears before the current one.
A more involved one would be to allow your solve
functions to consume more than one character per iteration. If you see a m
you already know what you want the next characters to be. So you can try to consume them eagerly before calling solve
again. This decreases the number of cases in your match
statements and allows you to drop the num1
and num2
arguments.
A small PSA that it is requested not to publish one’s inputs (see author’s comment on Twitter, for some value of X)
(I’m seeing how long I manage to work in OCaml, as opposed to on OCaml, this year at dra27/AOC2024, although my solutions tend to be coded to amuse me, which occasionally includes adopting style which would never fly on a PR )
See also @sabine’s List on Bluesky
I’ve doing it in OCaml again this year as well (I’ve tried Rust last year, and OCaml in 2022), pretty straightforward for now: GitHub - dlesbre/advent-of-code: Puzzle solutions for advent of code 2022 and 2023
About swapping rest.[0]
and prev
, I tried this when I was solving the puzzle
For case 'm'
, prev
can anything for the match to be valid. Swapping rest.[0]
and prev
means I’ll have to move _
in front of 'm'
, but the type checker seems very unhappy about this
As mentioned above: I like doing AOC to get a chance to write OCaml for myself (i.e my solutions involve a bunch of let (>>>) f g x = f x |> g
and a series of >>>
(i.e forward function compositions) that I don’t think is at all maintainable lol).
Since we are sharing, I’ve this guilty pleasure of mine:
val ( .%{} ) : ('a, 'b) Hashtbl.t -> 'a -> 'b
(** [h.%{k}] is [Hashtbl.find h k]. *)
val ( .%?{} ) : ('a, 'b) Hashtbl.t -> 'a -> 'b option
(** [h.%?{k}] is [Hashtbl.find_opt h k]. *)
val ( .%{}<-) : ('a, 'b) Hashtbl.t -> 'a -> 'b -> unit
(** [h.%{k}<- v] is [Hashtbl.replace h k v]. *)
val ( %? ) : ('a, 'b) Hashtbl.t -> 'a -> bool
(** [h %? k] is [Hashtbl.mem h k]. *)
val ( %- ) : ('a, 'b) Hashtbl.t -> 'a -> unit
(** [h %- k] is [Hashtbl.remove h k]. *)
val ( ~% ) : ('a* 'b) list -> ('a, 'b) Hashtbl.t
(** [ ~% l] creates a fresh Hashtbl.t from the bindings listed in [l]. *)
val ( or ) : 'a option -> 'a -> 'a
(** [ a or b ] is [v] if [a] is [Some v] and [b] if [a] is [None]. *)
and then stuff like that:
let table = ~%[ "FR", 0; "IT", 0; "VN",0 ]
let f key =
if table %? "FR" then Printf.printf "Bonjour\n";
table.%{key} <- 1 + (table.?%{key} or 0);
table %- "FR" (* au revoir *)
of course with generic Hashtbl.t
using generic equality and hash.
Huh, I did not at all know about using {}
in infix operators; wild!
Taken straight from the manual (ok, the language extension part, but still, this one has been there since 4.06).
I find that to be very readable. Why is it ‘guilty’, i.e. what would considered questionable style about this?
To give some context, OCaml code that I write for my day job is either:
In both case, I aim at writing idiomatic OCaml code. Since using these indexing operators is not a well established convention, I don’t want to introduce any difficulties for people reading the code.
Plus there are some dangers. For instance, although writing ( e1 or e2 )
is cute in recreational code, I only do it because I know that in my case, e2 will always be a constant (a default value). If people where to abuse it with arbitrary expressions that would be bad (evaluation order, side effects, …).
Rather, I would say that I use personal projects such as the AoC to experiment with more experimental part of the language.
But operators are quite nice… my day4 proposition use heavily .$()
as a shorthand of String.get
. Most language have a short expression for such a thing (even Ada which is quite verbose). Why shouldn’t OCaml have it ?
Sure, it is not common to have very multiple such operators, but in many language, Strings, Arrays, Associative maps have them. (With languages that support overloading, .[]
could be use with many many things).
The standard .[]
operator (in "Abc".[0]
) is a built-in short-hand for String.get