-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add AOC Day 03 solution #5
base: main
Are you sure you want to change the base?
Conversation
advent-of-code/rucksack-problem/.calva/output-window/output.calva-repl
Outdated
Show resolved
Hide resolved
.hgignore | ||
.hg/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we using mercurial for code versioning anywhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These were auto-generated, haven't looked into this yet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good:
- Stateless pure functions
- I/O happens at the boundaries
- Threading used to good effect
- Overall, nice solution!
Comments about what to improve are below.
@@ -0,0 +1,24 @@ | |||
# Change Log |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Delete this file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, file deleted.
@@ -0,0 +1,44 @@ | |||
# rucksack-problem |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fill in some short instructions here, or remove them if not relevant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I added a brief description.
@@ -0,0 +1,10 @@ | |||
(defproject rucksack-problem "0.1.0-SNAPSHOT" | |||
:description "FIXME: write description" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
likewise, here. Address the FIXME
s
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Filled in the FIXME
s
;; 2. find priority of common item | ||
;; Repeat the above for each group of 3 rucksacks, and sum the priorities of common items. | ||
|
||
(ns rucksack-problem.core |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general, try to avoid calling your namespace core
. It doesn't say anything about what this module does.
In this case, it's not a big deal, since we are having only a single module, but this is something to keep in mind when you move to multi-namespace projects.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, I've been going with the default core
. Would solution
be a good name for this namespace?
(defn compartmentalize | ||
"Divide rucksack contents into 2 compartments | ||
1 string -> 2 lists of chars" | ||
[rucksack-contents] | ||
(let [half-length (/ (count rucksack-contents) 2)] | ||
(->> rucksack-contents | ||
(partition half-length)))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[suggestion] Consider a more precise name, like partition-half
.
A good heuristic for checking if the name is good: Can you guess what this function does if you were only given its name (and no access to the docstring or implementation)?
Applying this heuristic here:
compartmentalize
tells us that I can give it a rucksack and I get something that arranges its items into compartments. But it doesn't tell us how many compartments and criteria for which it makes them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, partition-half
is very clear.
I've been having trouble deciding when to use words from the problem set (compartmentalize) vs. words that more clearly describe what the function does. Will now tend towards the latter, that is, more descriptive function names.
(let [c1 (set compartment-1) | ||
c2 (set compartment-2)] | ||
(some #(get c2 %) c1))) | ||
;; can also use set intersection here |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use a set intersection, then? It's a well-known operation, and you are using sets here after all.
(defn find-common-item | ||
"Find the common item between 2 compartments" | ||
[compartment-1, compartment-2] | ||
(let [c1 (set compartment-1) | ||
c2 (set compartment-2)] | ||
(some #(get c2 %) c1))) | ||
;; can also use set intersection here | ||
|
||
(defn find-badge | ||
"Find the common item (badge) between a group of 3 rucksacks. | ||
Used in Part 2 of problem." | ||
[rucksack-1, rucksack-2, rucksack-3] | ||
(let [r1 (set rucksack-1) | ||
r2 (set rucksack-2) | ||
r3 (set rucksack-3)] | ||
(first (set/intersection r1 r2 r3)))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These two functions have some duplicated logic. They are both finding a common item in sequences of items. Consider having these delegate to a generic utility function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the logic is duplicated.
So find-common-item
and find-badge
will remain, and they would both call a new utility function within themselves. I'll give this a go.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new utility function will take minimum 2 + any number of collections as a rest argument. It will convert these to sets, and then return the first common element between the collections. (We only expect one common item as per the problem statement).
(let [m1 (frequencies compartment-1) | ||
m2 (frequencies compartment-2)] | ||
(reduce | ||
(fn [m [item frequency]] | ||
(if (contains? m2 item) | ||
(->> item | ||
(get m2) | ||
(min frequency) | ||
(assoc m item)) | ||
m)) | ||
{} m1))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some thoughts regarding this bit (I believe I shared some of these from our pairing session, repeating them here):
- Try to refactor this without using
reduce
. Reach forclojure.core
utilities. - If it can't be done with clojure core, then try it with reduce.
- Another source of complexity in this reduce is the dependence on state in the enclosing function, ie,
m2
. You could clump it in the accumulator, which would let you extract the reducing function. It would still not be easy to read, though. - Consider more descriptive names,
m
,m1
,m2
make this harder to reason about. Off the top of my head:m
->common-item-frequency
m1
->item-freq-c1
,m2
->item-freq-c2
- Sometimes reduce is not a good fit because of readability, in which case use
loop-recur
as a last resort.
I'd encourage you to actually write this function in all the ways discussed above and compare them.
(println "Part 2 answer: \n" | ||
(->> "input.txt" | ||
input-file->string-seq | ||
sum-badge-priorities))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[style nitpick] Add a newline at the end of the files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, will do. Looked it up as well, didn't know about this!
AoC Day 03 ready for review