Input data and solutions (in Jupyter notebook form, with links to the original problem).
When I attempted my first Advent of Code in 2020 it was as a way of practising my python skills and learning by solving a different style of problem to my day-to-day work. Through lots of googling and help from Stack Overflow, this helped me:
- discover new techniques
- learn more about algorithms
- discover libraries and features of python I wasn't aware of
- identify performance bottlenecks and more code- and time-efficient solutions
Some of these are documented here for posterity:
- list/dict comprehensions
squares_list = [x**2 for x in range(10)]squares_dict = {x:x**2 for x in range(10)}ifcondition at end of comprehension v. useful for filtering/matching other lists
- sets (
squares_set = {1,4,9,16})- Unordered, unindexed, no duplicates
- Lots of set arithmetic methods, e.g.
- intersection:
set_a & set_b & set_corset_a.intersection(set_b, set_c) - union:
set_a | set_borset_a.union(set_b))
- intersection:
zipping iterators - usezip(a,b)as an iterator of tuples(a,b)through iteratorsaandbin parallel- Packing/unpacking with
*(positional arguments) and**(keyword arguments) [see here]. I've seen this often in function definitions but kind of ignored it until now. It became useful to unpack a list into multiple arguments with*(e.g. intersection of many sets withset.intersection(*list_of_sets)) or unpack a dictionary with**(e.g. combining two dictionariesd1andd2withnew_dict = {**d1, **d2}) - DIY generators - never bothered to make my own before, but less intimidated than I would have been before. Basically just a function that
yields values one at a time rather thanreturning. For example, cell 2 of my Day 9 solution definessliding_window()whichyields a chunk of the list provided and steps through the list until the end. - regex (
relibrary, or enhanced versionregex)- Coming from
stringrin R and latterly using SQL/PySpark, I still find there/regexAPI confusing and have to look up the expected arguments and the contents of the "match object" returned, as well as differences between e.g.match(find all instances of a pattern) vssearch(find the first instance of a pattern) - A new regex trick I discovered, which isn't supported in
rebut is inregexis the idea of recursion in a regex, to match balanced constructs like nested brackets. The simple version (described here before going deeper) can be applied in Part 2 of Day 19 to match any string in the formab,aabb,aaabbbetc. with the regex patterna(?R)?b. The(?R)tells the regex engine to start again looking forauntil it fails and moves ontob. However many levels of recursion reached in the first part (a), the pattern isn't completed until that manybs are detected. (aandbcan themselves be entire patterns)
- Coming from
parselibrary - kind of like a reverse-engineered f-string, as an alternative to regex for predictable formatting. For example,p = parse.compile("{field}: {a:d}-{b:d} or {c:d}-{d:d}")creates a parser that looks for strings like"age: 18-30 or 31-49". Thenp.parse(test_string)produces a dict returning{"field": "age", "a": 18, ...}(example from Day 16)collectionslibraryCounter- useful for counting instances of a value in a list of values, and storing as a dict - e.g.wins = Counter(list_of_world_cup_winners)-wins["England"]returns1whilewins["Wales"]returns0(for obvious reasons), andwins.most_common(1)returns('Brazil', 5)defaultdict- same as any other dict but if a new key is encountered, it is given a default value, depending on value type (e.g.0forintor[]forlist)deque- "double-ended queue" (which I refuse to pronounce "deck") to optimise various list operations and iteration, where indexing the list is not needed. Didn't use this when I probably could have, and then tried to use it where I probably shouldn't have
- Related to
deque, thank you to @mratford for introducing me to the idea of using a dictionary as a linked list ([a,b,c]becomes{a: b, b:c, c:a}) - Complex numbers are useful for coordinates and vector geometry (see Day 12)
- Classes - I have a lot to learn! But
functools.cached_propertycan be very useful to add the@cached_propertydecorator to class properties that are likely to be reused frequently (caching them rather than recalculating many times). numpyis a thing that is useful, and I should be far more aware of it than "I betnumpyhas a function for this..."appendvsextend- You canlist1.append(list2)to addlist2as a single item tolist1, orlist1.extend(list2)to add each element oflist1tolist2.