Fork me on GitHub
#adventofcode
<
2018-12-04
>
lady3janepl08:12:07

Day 4 discussion, with spoilers

lady3janepl08:12:25

[1518-09-19 23:52] Guard #2083 begins shift
[1518-02-25 00:51] wakes up
[1518-02-28 00:34] wakes up
[1518-04-29 00:54] wakes up

lady3janepl08:12:57

Rather sneakily, the example data isn’t in the same format the actual input is.

lady3janepl08:12:16

They don’t give you any hint of that apart from > consider the following records, which have already been organized into chronological order

pesterhazy08:12:11

yeah I found the description less than clear

pesterhazy08:12:43

but I guess vague specifications is a real-world programming scenario 🙂

athos09:12:03

In the description they say: > While this example listed the entries in chronological order, your entries are in the order you found them. You’ll need to organize them before they can be analyzed.

lady3janepl09:12:08

Yeah, you’re right. I missed it because it’s not in the place I expected to find it (task description, rather than input format description)

lady3janepl09:12:28

That also is a real-world programming scenario: don’t read specs before first coffee :woman-facepalming:

pesterhazy09:12:54

Reminds me

regen20:12:22

I almost had a heart-attack when I checked out the real data and saw it had multiple "wakes up" in a row, after having written my solution. Then I realized that it just wasn't chronologically ordered. Then sort came to the rescue 🌸

pesterhazy08:12:14

Spent around half the time on parsing/understanding the file format

fellshard09:12:28

It was a key realization that there's only one useful chunk of info per line. Also, the specific line format they give means you can sort it in standard lexicographic order to get the correct sequence of events right off the bat, no interpretation of the data required. 🙂

pesterhazy09:12:51

I did sort lexicographicallz

pesterhazy09:12:29

but you still need to match guard-ids, interval starts and ends no?

fellshard09:12:55

Yep. One piece of info to parse per line: Guard ID or minute.

pesterhazy10:12:09

And event type 🙂

fellshard10:12:00

True; I attached that as a tag afterwards, though, since you need to know which type it is to parse correctly in the first place

borkdude10:12:20

In CLJ:

Testing aoc.y2018.d04.borkdude
part-2 took 40.23 msecs
part-1 took 3.80 msecs

borkdude10:12:56

@pesterhazy It seems our solutions are quite similar

borkdude10:12:45

When reading the puzzle, I thought “strategy 1” was trying to mislead me, since they wrote that there were two strategies and I thought you had to come up with another better strategy 😉

ihabunek11:12:09

took me a while to parse it, but as usual, regex to the rescue 🙂

taylor13:12:39

here’s my day 4 https://github.com/taylorwood/advent-of-code/blob/master/src/advent_of_code/2018/4.clj reached for clj-time and instaparse b/c why not 🙂

me174014:12:00

If I had time I would have refactored into something like @tsulej. If you have the proper data structure with minute-level stats for each guard, the 2 solutions can be very concise.

tsulej14:12:35

Yes, I think that proper data structure is crucial here. I spent most of my time to find one. The same applies for day 3.

borkdude14:12:15

oh yeah, I always forget you can give sort-by another argument like (sort-by val > the-things)

tsulej14:12:07

without it you can always call (last) which can be obviously slower

tsulej15:12:01

(comp - val) cool hack 🙂

borkdude15:12:06

yeah I did the comp thing

borkdude15:12:36

of course, (max-key val …)

borkdude15:12:02

our add-minutes solution is quite the same

borkdude15:12:25

or wait, no, it isn’t 🙂

borkdude15:12:43

ah you save a map of guard -> minutes

taylor15:12:37

nice to see another use of max-key; I rarely ever have a use for it

taylor15:12:10

the concision of some of these solutions is impressive 👏

potetm15:12:13

@mfikes very nice!

potetm15:12:49

yeah I was surprised at the amount of (first (sort-by ...)) I’ve been seeing.

potetm15:12:56

max-key often gets forgotten

lady3janepl15:12:51

I’m not done yet (done 1/2), but took a look at this channel, and max-key instead of sort-by in the existing solution … which slowed things down. Did you find it more performant?

taylor15:12:42

I haven't been concerned with performance for any of the problems yet

taylor15:12:02

later in the schedule performance becomes more important

fellshard16:12:13

Are you thinking (apply max-key...) has a heavier cost for, say, a map of minute tallies?

lady3janepl16:12:10

(…actually might have been the effect of something triggering in the background… I need to check again once I’ve got a charger 🙂 )

potetm15:12:37

nah, just what I want

fellshard16:12:00

I looked at it, but passed it by because I thought it wasn't quite what I wanted. I'll take another look to see what I'm missing.

borkdude16:12:15

@fellshard I had the same: max-key passed my mind but then I thought: oh this is for map keys or something

borkdude16:12:40

why isn’t it called max-by

fellshard16:12:18

I think the prospect of (apply max-key... on 60 elements had me a bit worried, I forget what the rules are for slapping a bunch of arguments in there, even rest-args

pesterhazy16:12:20

@fellshard (apply + (repeat 1000000 1)) seems to work ok in CLJ and CLJS, but (apply + (repeat 10000000 1)) is fast in Clojure but very slow in CLJS (at least in Lumo 1.8.0)

pesterhazy16:12:14

In Java varargs are passed as an array AFAIK so it should be fine. Not sure why it's so slow in CLJS

pesterhazy16:12:45

Probably GC?

pesterhazy16:12:41

Math.max.apply(null, new Array(1000000).fill(0)) blows the stack...

borkdude16:12:31

(apply max (range 100000000000)) works though

mfikes17:12:02

@pesterhazy Update to Lumo 1.9.0. You'll see (apply + (repeat 1000000 1)) a few hundred times faster. Why? This is the result of optimizations made in ClojureScript after last year's Advent of Code. 🙂

borkdude17:12:26

changed my max-frequency function. I really should use max-key more often. I think I only remember it until Advent of Code comes along again https://github.com/borkdude/advent-of-cljc/commit/24fa6344c3c64a3fc242d7b2a784df7b73bace8a

mfikes17:12:57

Lumo 1.8.0:

cljs.user=> (type (repeat 1000000 1))
cljs.core/LazySeq
Lumo 1.9.0:
cljs.user=> (type (repeat 1000000 1))
cljs.core/Repeat

quoll17:12:19

I’ve browsed clojure.core, but keep forgetting about many of the functions in there. I totally forgot about max-key and rolled my own. That said, I used what I rolled in a couple of other ways, so it wasn’t a waste

pesterhazy18:12:06

@mfikes the advent of code comes full circle!

pesterhazy18:12:01

it's much faster indeed 🎉

pesterhazy18:12:17

also a good occasion to upgrade lumo

pesterhazy18:12:37

now it's 10x faster than Clojure

$ lumo
cljs.user=> (time (apply + (repeat 10000000 1)))
"Elapsed time: 65.706720 msecs"
10000000
$ clj
Clojure 1.9.0
user=> (time (apply + (repeat 10000000 1)))
"Elapsed time: 657.798409 msecs"
10000000

baritonehands04:12:33

Is (reduce + ... any faster?

baritonehands04:12:40

Looks like yes

baritonehands04:12:42

user=> (time (apply + (repeat 10000000 1)))
"Elapsed time: 526.222753 msecs"
10000000
user=> (time (apply + (repeat 10000000 1)))
"Elapsed time: 656.773543 msecs"
10000000
user=> (time (reduce + (repeat 10000000 1)))
"Elapsed time: 178.887459 msecs"
10000000
user=> (time (reduce + (repeat 10000000 1)))
"Elapsed time: 47.995412 msecs"
10000000
user=> (time (reduce + (repeat 10000000 1)))
"Elapsed time: 39.555085 msecs"
10000000

pesterhazy18:12:04

performance is weird

mfikes18:12:01

In this case, Lumo may be running at native speed. See https://www.youtube.com/watch?v=LopU-kMpe8I

adammiller18:12:59

anyone know why (read-string "01") or (read-string "02") ... all the way up to 07 work just fine but (read-string "08") and (read-string "09") throw exceptions saying it is an invalid number? Would assume all would or none would.

mfikes18:12:12

08 is octal

mfikes18:12:25

Well, malformed octal 🙂

lilactown18:12:42

I've used this almost every day so far:

(defn str->int [^String s]
  (Integer/parseInt s))

clashthebunny19:12:18

Does this work with the positive numbers, like "+5"?

potetm19:12:14

Try it in the REPL!

clashthebunny19:12:04

I thought I tried that and it didn't work. Is fixed by the ^string metadata?

lilactown19:12:31

It worked for me on day 1 ¯\(ツ)

clashthebunny19:12:03

Hmmm.... Dang. Wonder what I had problems with...

clashthebunny19:12:46

Maybe it was just the int function...

potetm19:12:40

int is a cast to int

quoll20:12:52

I’m forever wrapping Long/parseLong in a function. It’s a source of never ending frustration for me that Clojure doesn’t have to-long and to-double

ben.grabow20:12:51

The function reader literal syntax is pretty terse: #(Long/parseLong %). Much nicer than a top level (defn ...) form, if that's what you've been using.

quoll01:12:27

I disagree

quoll01:12:58

yes, it’s terse. It’s not much nicer than a top level defn (or def)

baritonehands04:12:43

I did this for day 3:

(map (comp #(Integer/parseInt %)
            (memfn trim)))

adammiller18:12:31

yeah, for cljc read-string was just faster, but better not to use it for these scenarios (in real code)

borkdude18:12:36

is the type annotation really necessary?

borkdude18:12:05

@adammiller not sure if you do advent-of-cljc, but there’s aoc.utils/parse-int for cross platform

adammiller18:12:41

yes, I saw that. Actually just moved my day 3 solution there yesterday....just locally though.

borkdude18:12:17

@adammiller welcome to submit individual puzzles for any day

adammiller18:12:50

ok, i'll see about moving my day 4 and then submit them. Thanks!

lilactown19:12:44

@borkdude it was necessary when I tried it with 1.10-RC2