(I was cleaning my keyboard and slack opened) oops
No stream today. I'm done for the season. Ya'll have fun!
Day 19 - Solutions
unrelated, does someone have a lib I could use to fit a curve to some points?
Like I have 4 points and I want a quadratic curve for it
Probably better to ask in #clojure.
memoized recursive function. this always feels kind of awkward in clojure and I do the y combinator thing - interested to see how other people did this. https://github.com/tildedave/advent-of-code/blob/main/src/advent2024/day19.clj funny enough I programmed it to be memoized for part 1 but forgot to actually memoize it 😅
It’s not complicated at all, just use def
Or rebind the variable
This y-combinator thing can still blow up if recursion is too deep, right?
yeah it's pretty janky. I know memoize can work with 2 args but I don't like re-checking equality on the contxt variables.
maybe perf wise it doesn't matter for this example but I believe I've run into slowness on the "extra args checking" before
should be fast
yeah I'll futz a bit with it
because the first thing it checks when comparing maps is if they are the literal same object. And if you don’t modify anything they should be
Oh it does? That's clever. I'm actually less concerned about partial memoization if it memoizes on a ref.
it would be immensely stupid not to do that check
it’s tons cheaper than running actual equality on contents, it’s basically one of the benefits of immutable objects
so you should first check if it’s the same object, then if both are of types that are even possibly equal, then, if the object has cheap count function, you compare counts, and only then you actually look at the elements
That makes a lot of sense. I always forget that objects are passed by ref in Clojure if not modified, since functionally, we can always think that copies are passed around. Thank you for your lights 🙂
It works the same way in java
Or C#, Kotlin, Scala, etc
I've been fortunate enough not to ever work seriously with Java. I have very superficial understanding of how it works.
Most languages have pass-by-ref as default parameters
Yeah, I know. But the very functional approach of Clojure made me forget that.
It's not obvious to me why I didn't need to memoize in part 1. Is it just laziness kicking in?
(defn doable? [design]
(if (empty? design)
:valid
(seq (keep
(fn [p]
(when (str/starts-with? design p)
(doable? (subs design (count p)))))
patterns))))
(->> designs (keep doable?) count) Today’s was fun, 150ms for part 2 https://github.com/bhauman/adv2024/blob/main/src/adv2024/day19/sol.clj
I took a regular expression approach. I compiled the patterns into a tree and then matched the designs against them recursively. Used memoization for part 2. The benefit of the reg-exp approach is that the algorithm doesn’t have to backtrack as far to try the next design. Memoization is more effective as well.
@jvuillermet I think you’d want to call first instead of seq on your (keep expression to benefit from laziness
Just glad that it's solved.
I felt really smart today. very welcome after two frustrating days at work debugging a 80 line SQL query and pulling out my hair
wow, @erdos's is even cleaner. incredible
I was looking forward to using memoize this year. https://github.com/erdos/advent-of-code/blob/master/2024/day19.clj
short and easy. I tried to nest my memo and it was a bit uglier than I hoped.. https://gitlab.com/maximoburrito/advent2024/-/blob/main/src/day19/main.clj
For some reason I tried to do part2 without memoize, iterating just on the possible patterns. Lost like 20minutes because of sheer stubbornness -_- EDIT: DRY-ing
Well this sucks. My alarm didn’t go off.
Solved it in 13 minutes, now I am at 4000-6000th place
Nested memo? what?
https://gist.github.com/RokLenarcic/2949cb7345cb8c145d596b20d2166fe2
I wish memoize took some optional arguments that let you specify "memoize on only the last argument". Would make the space required way better. I know I could do it with inline declaration but that's just ugly.
clojure core memoize will let you do that
Oh this is pretty cool, but way overkill for my needs. Bookmarkign it though. Thank you.
But my library is not focused on speed, because it has advanced features and it assumes each invocation takes 1 ms at least.
E.g. it uses extra locking to purge entries from multiple caches when you are invalidating by tag, so that slows down the simple case quite a bit
As I needed to introduce additional locks
When speed is of the essence, I wrote my own memoize:
(defn memoize*
"Faster memoize not thread safe."
[f]
(let [mem (HashMap.)
guard (Object.)]
(fn [& args]
(let [v (.getOrDefault mem args guard)]
(if (= v guard)
(let [ret (apply f args)]
(.put mem args ret) ret)
v)))))
Cute. I'm stealing it 😛
Here you go, a version where you can specify a key transformation:
(defn memoize*
"Faster memoize not thread safe."
([f] (memoize* f identity))
([f key-fn]
(let [mem (HashMap.)
guard (Object.)]
(fn [& args]
(let [v (.getOrDefault mem (key-fn args) guard)]
(if (= v guard)
(let [ret (apply f args)]
(.put mem args ret) ret)
v))))))
oof
sorry a bug here
That's basically what I did, yeah. Yours is more idiomatic though (I just did a memoize-n which stores on take n args).
(defn memoize*
"Faster memoize not thread safe."
([f] (memoize* identity f))
([key-fn f]
(let [mem (HashMap.)
guard (Object.)]
(fn [& args]
(let [k (key-fn args)
v (.getOrDefault mem k guard)]
(if (= v guard)
(let [ret (apply f args)]
(.put mem k ret) ret)
v))))))fixed