Skip to content
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

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open

Conversation

shahn95
Copy link
Contributor

@shahn95 shahn95 commented May 12, 2023

AoC Day 03 ready for review

Comment on lines +12 to +13
.hgignore
.hg/
Copy link

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?

Copy link
Contributor Author

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.

@shahn95 shahn95 marked this pull request as ready for review May 23, 2023 17:09
Copy link

@tfidfwastaken tfidfwastaken left a 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
Copy link

@tfidfwastaken tfidfwastaken May 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete this file?

Copy link
Contributor Author

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

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.

Copy link
Contributor Author

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"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

likewise, here. Address the FIXMEs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filled in the FIXMEs

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

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.

Copy link
Contributor Author

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?

Comment on lines +38 to +44
(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))))

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.

Copy link
Contributor Author

@shahn95 shahn95 May 29, 2023

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

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.

Comment on lines +46 to +61
(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))))

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.

Copy link
Contributor Author

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.

Copy link
Contributor Author

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

Comment on lines +79 to +89
(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)))
Copy link

@tfidfwastaken tfidfwastaken May 25, 2023

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 for clojure.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)))

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.

Copy link
Contributor Author

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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants