This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-12-08
Channels
- # adventofcode (283)
- # aleph (2)
- # announcements (1)
- # aws (12)
- # beginners (43)
- # calva (14)
- # cider (9)
- # cljsrn (1)
- # clojure (16)
- # clojure-kc (1)
- # clojure-nl (2)
- # clojure-uk (8)
- # clojurescript (55)
- # cursive (6)
- # datomic (6)
- # emacs (15)
- # jobs (1)
- # kaocha (5)
- # nrepl (13)
- # re-frame (2)
- # shadow-cljs (3)
- # spacemacs (6)
- # sql (10)
- # vim (13)
- # yada (5)
(sorted-set)
ftw https://github.com/thegeez/clj-advent-of-code-2018/blob/master/src/advent2018/core.clj#L747
Mine's not exactly optimally expressed - there's some duplicate calculations I'd like to weed out - but I think it reads nicely 🙂 https://github.com/armstnp/advent-of-code-2018/blob/master/clojure/src/advent_of_code_2018/day7.clj#L67
I tried passing around data this time and having each function recieve and transmit the same data structure. It could have been just as easily done with an atom. I have never tried something like this before and it was super painful. What do other people do? https://gitlab.com/randall.mason/advent-of-code/blob/master/src/advent_of_code_2018/day07.clj Also, gitlab may be having struggs?
I like the idea of ancillary functions taking a large map of all the data, then only operating on the subsets the function needs. It's very "inversion of control" style.
I think you could improve your usage of the style by having your ancillary functions return the whole map, after performing some update-in
calls to update the keys that pertain to that function.
Then where you are calling the ancillary functions you can use a threading macro:
(->> the-data
do-thing-1
do-thing-2
do-thing-3)
Ah, I see it's basically what you're doing but instead of calling update-in
and letting the other values come along for the ride by default, you are manually reconstituting the map including all the keys that don't pertain to the function.
Here are a couple examples; https://gist.github.com/bgrabow/0306387873ec31e053091eff73c50626
Yeah, that's a whole ton more readable. The way I was doing it I was losing some keys when I tried that. Do you not have to enumerate ll the keys, or just the ones you use?
All the data will be preserved if you bind the whole map in your arg list (the :as the-data
part).
And then instead of returning a map from scratch, you return the result of calling (update the-data k f)
(let [m {:a 1 :b 2 :c 3}]
(update m :b inc)) ; => {:a 1, :b 3, :c 3}
@U09NVRNG2 I think the second example in my gist was a little off. If you have a brand new value for a key, you should use assoc
. If you want to apply a function on the old value to get the new value, you should use update
. Hope that helps.
I have finally gotten the update assoc distinction, so at least I'm not learning that! 🙂
so is it just me or is day8 part 1 really unclear how you're supposed to split up child nodes?
My day 7: https://github.com/jreighley/advent-of-cljc/blob/master/src/aoc/y2018/d07/jreighley.cljc I would love some code review — I am a lonely self taught hack. 😉
My favorite part of day 7 was my fn to parse the line:
(defn parse-line [line]
(let [[[lhs] [rhs]] (->> (str/split line #"tep ")
(drop 1))]
[lhs rhs]))
Day 8 was definitely easier, day 7 was the hardest so far for me
i've never worked with zippers, and therefore I am having troubles wrapping my head about parsing day 8 😞
I didn't use zippers, just regular split by space
but I did use trampoline for the first time to avoid a stack overflow
I didn't end up with a deep enough tree to have to worry about stack overflows. Maybe I should test it against other inputs.
No stack overflow for me, raw recursion worked fine.
I think I had similar pains as you @helios, my solution of having a functional parser required using both stack-recursion and loops for state magenement which is unfamiliar for me to piece together. soln: https://github.com/rymndhng/advent-of-clojure/blob/master/src/advent_2018/08.clj
My solution for Day 8: https://github.com/benfle/advent-of-code-2018/blob/master/day8.clj
It's always painful to write a parser in a functional style so I did it with local state 🙂
I’ve actually never seen/used volatile! before. Looks like a new 1.10 thing? Time to learn something new 🙂
they've been there since 1.7 https://github.com/clojure/clojure/blob/master/changes.md#21-transducers
not sure if they're documented except for the (tautological?) docstrings
I found this, https://stackoverflow.com/questions/31288608/what-is-clojure-volatile#comment50623688_31288608 - looks like a good summary
I wonder is there a rule of thumb when volatiles are safe to use
and when atoms are required instead
My rule of thumb is to use volatile unless I have concurrency. There is also transient for IEditableCollection
. https://clojure.org/reference/transients
basically keep them contained in a single fn right?
There are two aspects: - for correctness: ensure the volatiles/transients are not accessed by multiple threads. They don't ensure atomicity. - for code organization: If I use state I prefer to keep it as local as possible so in this case inside the function
good points both
My day 8: https://github.com/pesterhazy/advent2018/blob/master/src/advent/puzzle08.clj#L37 - I took the functional route
- bad: I was stuck for half an hour because I didn't realized that children are 1-indexed (why??) - good: In the process I rediscovered clojure.tools.trace (which I'm sure will be useful later on)
It's pretty clear in the text to ignore the 0, and use the 1's as zero. Your lucky with using clojure since sometimes the index is higher then the number of children. With clojure they must are ignored, which not always is a good thing.
I'm talking about the "meta-data`, which (in my test data) includes no 0s
I agree that it's clear from the text (I was hasty once more)
I'm trying to use clojure.core.reducer/fold
with @.mfikes's day 5 solution.
https://gist.github.com/namenu/21e5d99989fddb1447ba446ca53e40a6
Started a journal to remember the problems I'm running into and maybe come up with some conclusions: https://github.com/pesterhazy/advent2018/blob/master/journal.md#L1
@namenu By default, only a vector and a hashmap are foldable https://github.com/clojure/clojure/blob/c4dc8815272382725ca3d3d06f195e57c3228109/src/clj/clojure/core/reducers.clj#L320-L334
Day 8: https://github.com/mfikes/advent-of-code/blob/master/src/advent_2018/day_08.cljc
@mfikes almost identical to mine!
differences - reduce vs loop (I think I prefer loop here) - different tree-seq params (not sure why mine works)
As a beginner I’d like to ask why you prefer loop over reduce here?
essentially because the 2nd argument in the reduce-fn is ignored
I just saw you mentioned it in the main thread. Thanks tons!
I'm getting a spec error on my local machine from time to time on the advent-of-cljc tests. Does anybody know what I'm doing wrong? This compiled fine last night...
=== Running clojure test aoc.y2018.d07.clashthebunny
Running tests in #{"src"}
Testing user
Ran 0 tests containing 0 assertions.
0 failures, 0 errors.
=== Running cljs test aoc.y2018.d07.clashthebunny
#error {
:cause Library name must be specified as a symbol in :require / :require-macros; offending spec: [] at line 1 /Users/ranmason/code/advent-of-cljc/cljs-test-runner-out/gen/cljs_test_runner/gen.cljs
:data {:file #object[java.io.File 0x70c69586 /Users/ranmason/code/advent-of-cljc/cljs-test-runner-out/gen/cljs_test_runner/gen.cljs], :line 1, :column 1, :tag :cljs/analysis-error}
:via
[{:type clojure.lang.ExceptionInfo
:message Library name must be specified as a symbol in :require / :require-macros; offending spec: [] at line 1 /Users/ranmason/code/advent-of-cljc/cljs-test-runner-out/gen/cljs_test_runner/gen.cljs
:data {:file #object[java.io.File 0x70c69586 /Users/ranmason/code/advent-of-cljc/cljs-test-runner-out/gen/cljs_test_runner/gen.cljs], :line 1, :column 1, :tag :cljs/analysis-error}
:at [cljs.analyzer$error invokeStatic analyzer.cljc 718]}]
That file is generated with the empty brakets at the top:
cat /Users/ranmason/code/advent-of-cljc/cljs-test-runner-out/gen/cljs_test_runner/gen.cljs
(ns cljs-test-runner.gen
(:require [doo.runner :refer-macros [doo-tests]] []))
I've done a git clean -fdx
, so I am pretty sure I'm not being bitten by cache.
@U09NVRNG2 What is that empty vector doing at the end?
It's an autogenerated file from cljs_test_runner.
So I assume I made either made an egregious error in the clojure side of things or I have a bug in the versions of things I use...
Mine looks like:
(ns cljs-test-runner.gen
(:require [doo.runner :refer-macros [doo-tests]] [aoc.y2018.d08.mfikes]))
Damn! I figured it out.
It's the whole uppercase wasn't allowed in the username and then I ran it again with lowercase.
Thanks for the help!
So script/test-one aoc.y2018.d07.ClashTheBunny
works for both cljs and clj, but script/test-one aoc.y2018.d07.clashthebunny
only works for clj.
Is this something that would be expected? Should cljs downcase the namespace?
Or should clj not be downcasing it?
No... this isn't really a case-specific problem. It happens if you ask it to run tests for which there is no namespace.
I don't know where the problem lies, either Michiel's script, or upstream in one of the test runners. Perhaps file a ticket against advent-of-cljc
.
@pesterhazy Yeah, both bother me a little: loop
directly says what you are doing, albeit at a very low level, and it feels like I'm shoehorning reduce
by driving it with range
and essentially ignoring the range
values.
What you really want to do in words is "iterate over this n times". I've updated my solution to try to be closer to that:
https://github.com/mfikes/advent-of-code/commit/7c60c2b1468e1863a30ba5ec9573f9a12d12736e
yeah iterate
seems like the most descriptive choice (I actually considered but discarded it - not sure why)
For some reasons it always bugs me when the functional style forces me to keep all the state around and change my function signatures.
Yeah, having to thread the remaining sequence through is a bit of a pain for this one.
damn, I’m close, but I’m having a stackoverflow. I might have to throw the towel due to time constraints 😢
funny, I used naive recursion and didn't run into any stack overflows
I had the same. The test input isn’t stacked deeply. Do you need another test input? 2 1 3 3 0 1 2 0 3 7 8 9 1 1 0 2 6 12 1 4 1 2 1 3 0 1 17 1 4 4 2
That one broke my first implementation as did the real data.
If you need the numbers for part 1 or 2, just tell me.
(They’re comfortable enough to do by hand)
Part 1: 80
Do you want Part 2 as well?
sweet
but the real file still doesn’t work? 😮
I’m sorry that the additional test input didn’t help 😞
Have you checked your iterations?
Hang on.
Have you considered that there is a newline at the end of the test file?
(That one kicked my bucket a few times already and I guess that’s not it. But as a tester I have to ask 😉 )
what was it?
After a few rounds of yakshaving, I ended up with a script to create the next puzzleNN.clj, usable as clojure -m advent.main
: https://github.com/pesterhazy/advent2018/blob/master/src/advent/main.clj#L3
Even better, scripts/dev
automatically starts rebel-readline in the latest puzzle namespace
I think I should stop now 🙂
As I was doing this I worried there would be some degenerate case where we’d need to save the score for a node to be efficient, like maybe metadata [1 1 1 …} on 5 or 6 nested nodes…
I took this into account and multiplied the node-value by it's frequency, and it is quite a bit faster, but as my full runtime is dominated by parsing the tree, it doesn't make much of a difference.
I've been thinking about this, and I really do think it's possible to do both parts as a single, linear pass that doesn't require recursion...
Yeah, it's basically just turning the recursion stack into an explicit one. But it would certainly make the parts that consume the stream easier to handle, I suspect, by breaking apart the functions that handle different read states (reading header, reading body, reading metadata)
It's possible because I'm doing it, but it's much easier with mutable data, but you can have that as well in clojure.
I still enjoy the lisp-y way of pushing environments onto an explicit stack. That said, I'm probably making life hard for myself 🙂
I can see how to do that with part 1 but with part 2 I don’t think I stand a chance to to something without a second pass. Unless I don’t understand what you mean with “second pass” :thinking_face:
Accumulating your total with a single walk through the tree's data, beginning to end, no back-tracking
Urgh. Nope. I couldn’t do that. But I will now attempt to use tree-seq for walking the tree after I built it… I also want to change how I built the tree because it’s s l o w.
Yes, the invite code is in the little bit of text next to the pin icon and at https://github.com/adventofcode-clojurians/adventofcode-clojurians#leaderboard
Thanks. And OMG I am not last
my solutions: https://gist.github.com/dmitrygusev/316119976f77368eb9356aa82a0d0b17
they’re raw and not refactored after I’ve got correct answer, which should stress the pain I had while solving them 🙂
I’m new to clojure and lisp
I’ve gone back and forth between reifying data and doing things algorithmically during AoC
Uhm… I just learned a new word. Reify. I am not sure yet if I understand it completely though I consulted a lexicon
In this case: There’s an implied data structure in the sequence of numbers for Day 8. Reifying it means to make it into clojure maps, lists, and vectors instead of requiring pre-understood knowledge.
Structuring it? Like building a tree?
Yepp. Especially when you’re not a native speaker 😄
It is a tree, but it’s represented as a list of numbers. Better is to make the relationships explicit.
How do you walk the first? Well… You the developer must be told what each number means.
How do you walk the second? The relationships are explicit in the structure. It’s immediately obvious.
:shocked_face_with_exploding_head: I just learned that a test that runs in 1s with vectors can run 40s if I use lists. I guess it’s worth deciding the right way!
I’m still finding it difficult to estimate computational complexity of an algorithm in Clojure 😕
I’m far from it, too 😄
But I am learning tons by AoC. First I try to solve it and now I am trying to optimize and to try things out.
@U04V437EA Do you have an example?
pick any from the gist I’ve posted, i.e. day1 pt 2
the problem is there are plenty of built-in functions whose complexity is unknown
I have four Revisions in this gist and especially the difference between Rev 3 and Rev 4 are mind blowing https://gist.github.com/MeikeMertsch/f1f3a1bfc157c3ed3a6cfcb1dec0ff57
@U04V437EA I’m not sure what you mean by that statement. The complexity is pretty well stated for most operations on most structures.
But it is sometimes polymorphic. So you must be aware of the type of the underlying structure.
@U0A7TVBLN Is there a particular change you’re interested in hearing about. Instead of “this whole diff,” it’s easier to talk about, “This change from concat
to conj
” or whatever.
(map-invert
(frequencies
(flatten
(vals
ids-by-minutes))))
i.e. how can you estimate complexity for the above?flatten is lazy O(n), frequencies is eager O(n), map-invert is eager O(n). The lazy will get rolled into the first eager O(n) so it doesn’t contribute.
Language is hard… what’s “O(n)“?
oh. Just check the diff from rev 3 to 4. it’s just two lines. And really just a conversion
yeah, I meant the C in front of O(n) is difficult to estimate
@U0A7TVBLN The last revision changes behavior. It’s not an isolated optimization.
knowing something is O(n) helps, for sure
I noticed. 39 seconds worth of a behavioral change
@U0A7TVBLN One of those should return an incorrect result, I would presume.
no. Both rune fine. Before trying that out I changed all peeks, pops and conjs, so they would work the same for vecs and lists
that’s why the vector version is so slow already. With peeks, pops, and conjs I gain almost half a second speed (see version 2)
Obviously I access data WAY more often from the end than from the front. And lists aren’t built for that, right??
Okay, so you’re running into the same lazy evaluation issues @U04V437EA was talking about
using vec made me 39seconds (!) faster (:shocked_face_with_exploding_head: )
vec vs list is like array with direct access vs linked list, get element from vec is O(1), from list it’s O(n)
So I think I’m misunderstanding the question 🙂 It looks like you took out calls to vec (I presume to make it faster)
But, in general, there’s a lot going on in the solution, so it’s difficult to characterize where it’s spending its time.
haha. Sorry for the confusion. My tests ran 500msec and I wanted them faster so I checked my collection operations and spotted that I did a whole lot of conversions to vectors. So I refactored them out. With the result that everything was s l o w e r
I am quite impressed with what you can read out of those docs. Most of the internals are just “blahb” to me
I can read the words but I don’t understand what they mean.
There are some information about the complexity of the Clojure data structures here: https://clojure.org/reference/data_structures#Collections
> their specific behavior is slightly different for different types of collections this ^
Once you fundamentally get your algorithm sorted, you can often tweak things to ensure optimizations like chunked sequences or directly reducible collections help.
At least I figured out the tree-seq again. I am fairly happy with that
my day 8 https://github.com/borkdude/advent-of-cljc/blob/master/src/aoc/y2018/d08/borkdude.cljc 😅
Did you start with the tree approach? Or something optimized for part 1 that was then converted to tree?
CLJ:
Testing aoc.y2018.d08.borkdude
part-2 took 7.98 msecs
part-1 took 2.81 msecs
Testing aoc.y2018.d08.iamdrowsy
part-2 took 10.25 msecs
part-1 took 14.21 msecs
Testing aoc.y2018.d08.mfikes
part-2 took 18.30 msecs
part-1 took 21.29 msecs
CLJS:
Testing aoc.y2018.d08.borkdude
part-1 took 20.00 msecs
part-2 took 2.00 msecs
Testing aoc.y2018.d08.iamdrowsy
part-1 took 58.00 msecs
part-2 took 38.00 msecs
Testing aoc.y2018.d08.mfikes
part-1 took 120.00 msecs
part-2 took 76.00 msecs
I'm not even sure if is correctly placed
Lots of solutions involving the expression
[{:children children, :metadata metadata} the-rest-of-the-seq]
🙂If we had a state monad that we were all used to using maybe we could avoid that. Othewise this aspect was troublesome.
I used an integer position instead of the-rest-of-the-seq, and left the input vector untouched.
I could probably optimize that by not going through a seq at all, but staying in a vec
I had no time yesterday to read, but how did you approach part2 of day 7?
The way I see it, you have this extra appendage you have to thread through all of your calls, which is state you need to pass around. Perhaps a monad of some sort would help hide a bit of that complexity.
Otherwise lots of [what-i-want extra-crap]
destructuring, along with passing extra-crap
around.
https://github.com/glguy/advent2018/blob/master/execs/Day08.hs (solution with State Monad)
Yeah, I don't have enough experience with state monads to know if they work in recursive situations. Well, I have nearly zero experience.
I worked through the http://HaskellBook.com this year. I have a little bit of experience with the State monad, and StateT monad transformer.. but I’m not using that a lot so just a matter of time before I forget again
Haskell: banging on the type system until it works. Clojure: banging on the REPL until it works.
does anyone know any other interesting users coming from other language to follow? The postgres guy is super interesting https://github.com/xocolatl/advent-of-code/tree/master/2018
I intended to respond to this
(this was mentioned here a few days ago, thanks for that)
I did 2017 in Prolog https://github.com/Average-user/adventofcode-pl-2017
I did 2017 in Prolog https://github.com/Average-user/adventofcode-pl-2017
I was on a private leaderboard with him last year and one PureScript user (so three different langs). it was fun to see the different solutions
I intended to respond to this
oh wow, impressive @lucaspolymeris
chapeau @lucaspolymeris!
There are some missing stuff, and others very slow . So if you know Prolog glad to accept PRs
This list looks great: https://github.com/Bogdanp/awesome-advent-of-code
These Scala solutions look more compact that I would have expected. (Perhaps the author is golfing or that's fairly idiomatic?) https://github.com/FlorianCassayre/AdventOfCode-2018
Yes that looks pretty good, better than most of the haskell/ocaml solution I've looked at
I think I’ve convinced myself that solving day 8 w/o recursion would require some gnarly stack juggling
I feel like day 8 is foreshadowing a problem where direct recursion won’t be feasible 😰
last year (2017) I saw someone write here: in 2016 the puzzles were harder, now they’re sometimes too easy. I haven’t heard that this year yet?
I think untill now, hardness has been equivalent to 2017
Nice F# solution - also has to thread through the "remainingTree"
Probably once you've done them for one year it also becomes easier? It's a pretty specific kind of problems.
Yeah it is interesting;
let meta, remainingTree = List.splitAt metadata tree'
and
val (metadata, other) = right.splitAt(nMetadata)
and
(let [[meta rest] (split-at n-meta nums)]
all look very similar
(We are all roughly employing the same algorithm, even using the language's built-in "split at" functions.) That's F#, Scala, Clojure
here’s a day 8 stack-based solution https://www.reddit.com/r/adventofcode/comments/a47ubw/2018_day_8_solutions/ebc99t8/
I adapted this to Clojure and built the tree using a zipper https://github.com/taylorwood/advent-of-code/commit/3479b928e16874fdda7469ed58e4eb1648671736
I think there's something about this particular problem that forces you down a similar path (whereas other problems admit a richer variety of approaches.) I found James Henderson's bit of code here https://github.com/jarohen/advent-of-code/blob/master/2018/src/aoc2018/day8.clj#L9-L20 way too similar to mine here https://github.com/mfikes/advent-of-code/blob/b89aff099c2dd42928e038f3d70344b685f7d889/src/advent_2018/day_08.cljc#L9-L18
both make sense to me. reduce with a counter could also have been used, but that starts to look like a normal loop
I adapted this to Clojure and built the tree using a zipper https://github.com/taylorwood/advent-of-code/commit/3479b928e16874fdda7469ed58e4eb1648671736