adventofcode

Benjamin 2024-12-19T16:23:00.153069Z

(I was cleaning my keyboard and slack opened) oops

2024-12-19T16:48:18.866919Z

No stream today. I'm done for the season. Ya'll have fun!

🙌 5
🙌🏻 1
2024-12-19T05:28:38.375289Z

Day 19 - Solutions

roklenarcic 2024-12-19T08:11:27.660449Z

unrelated, does someone have a lib I could use to fit a curve to some points?

roklenarcic 2024-12-19T08:11:59.171699Z

Like I have 4 points and I want a quadratic curve for it

Maravedis 2024-12-19T08:17:05.459029Z

Probably better to ask in #clojure.

tildedave 2024-12-19T11:17:38.503859Z

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 😅

roklenarcic 2024-12-19T11:21:09.844819Z

It’s not complicated at all, just use def

roklenarcic 2024-12-19T11:21:33.657579Z

Or rebind the variable

roklenarcic 2024-12-19T11:27:32.623669Z

This y-combinator thing can still blow up if recursion is too deep, right?

tildedave 2024-12-19T11:40:04.289519Z

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.

tildedave 2024-12-19T11:40:44.350069Z

maybe perf wise it doesn't matter for this example but I believe I've run into slowness on the "extra args checking" before

roklenarcic 2024-12-19T11:40:56.969159Z

should be fast

tildedave 2024-12-19T11:41:25.489429Z

yeah I'll futz a bit with it

roklenarcic 2024-12-19T11:41:45.259549Z

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

Maravedis 2024-12-19T11:43:39.787399Z

Oh it does? That's clever. I'm actually less concerned about partial memoization if it memoizes on a ref.

roklenarcic 2024-12-19T11:44:12.888939Z

it would be immensely stupid not to do that check

roklenarcic 2024-12-19T11:44:52.077279Z

it’s tons cheaper than running actual equality on contents, it’s basically one of the benefits of immutable objects

roklenarcic 2024-12-19T11:46:31.398879Z

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

Maravedis 2024-12-19T11:49:38.865109Z

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 🙂

roklenarcic 2024-12-19T11:50:07.583259Z

It works the same way in java

roklenarcic 2024-12-19T11:50:34.652889Z

Or C#, Kotlin, Scala, etc

Maravedis 2024-12-19T11:50:47.463139Z

I've been fortunate enough not to ever work seriously with Java. I have very superficial understanding of how it works.

roklenarcic 2024-12-19T11:50:50.456079Z

Most languages have pass-by-ref as default parameters

Maravedis 2024-12-19T11:51:17.852319Z

Yeah, I know. But the very functional approach of Clojure made me forget that.

jvuillermet 2024-12-19T11:57:21.000599Z

It's not obvious to me why I didn't need to memoize in part 1. Is it just laziness kicking in?

jvuillermet 2024-12-19T11:57:54.956589Z

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

bhauman 2024-12-19T14:47:13.272239Z

Today’s was fun, 150ms for part 2 https://github.com/bhauman/adv2024/blob/main/src/adv2024/day19/sol.clj

bhauman 2024-12-19T14:49:43.327619Z

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.

bhauman 2024-12-19T15:04:30.911819Z

@jvuillermet I think you’d want to call first instead of seq on your (keep expression to benefit from laziness

Apple 2024-12-19T19:53:51.370479Z

Just glad that it's solved.

👍 1
Felipe 2024-12-19T23:17:37.229119Z

Felipe 2024-12-19T23:19:12.934739Z

I felt really smart today. very welcome after two frustrating days at work debugging a 80 line SQL query and pulling out my hair

Felipe 2024-12-19T23:23:30.645349Z

wow, @erdos's is even cleaner. incredible

erdos 2024-12-19T05:33:50.278249Z

I was looking forward to using memoize this year. https://github.com/erdos/advent-of-code/blob/master/2024/day19.clj

2024-12-19T05:34:03.869729Z

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

Maravedis 2024-12-19T06:00:43.783239Z

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

roklenarcic 2024-12-19T06:32:02.622799Z

Well this sucks. My alarm didn’t go off.

2
roklenarcic 2024-12-19T06:32:21.914919Z

Solved it in 13 minutes, now I am at 4000-6000th place

roklenarcic 2024-12-19T06:35:06.099049Z

Nested memo? what?

Maravedis 2024-12-19T06:42:13.410699Z

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.

roklenarcic 2024-12-19T06:47:52.641889Z

clojure core memoize will let you do that

roklenarcic 2024-12-19T06:48:15.770319Z

or https://github.com/RokLenarcic/memento

Maravedis 2024-12-19T06:49:08.526479Z

Oh this is pretty cool, but way overkill for my needs. Bookmarkign it though. Thank you.

roklenarcic 2024-12-19T06:49:44.428999Z

But my library is not focused on speed, because it has advanced features and it assumes each invocation takes 1 ms at least.

roklenarcic 2024-12-19T06:50:59.789499Z

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

roklenarcic 2024-12-19T06:51:30.233749Z

As I needed to introduce additional locks

roklenarcic 2024-12-19T06:53:57.017659Z

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

Maravedis 2024-12-19T06:55:06.676119Z

Cute. I'm stealing it 😛

roklenarcic 2024-12-19T06:55:43.584169Z

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

roklenarcic 2024-12-19T06:56:23.171949Z

oof

roklenarcic 2024-12-19T06:56:25.476729Z

sorry a bug here

Maravedis 2024-12-19T06:56:44.862889Z

That's basically what I did, yeah. Yours is more idiomatic though (I just did a memoize-n which stores on take n args).

roklenarcic 2024-12-19T06:56:50.332119Z

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

1
roklenarcic 2024-12-19T06:57:20.950949Z

fixed