Skip to content

Commit ec84021

Browse files
authored
stripped overhead functions/calls
- removed YAGNI code (`FSO` class) - improved input parsing/filesystem building fn - removed unneeded overhead function definitions (Filesystem.tail, Directory.add, Directory.dir_sizes) In theory, the `# type: ignore` hints (for `mypy` typechecking) should be able to be removed once python/mypy#14041 is integrated into the next release.
1 parent 80c080c commit ec84021

File tree

1 file changed

+46
-72
lines changed

1 file changed

+46
-72
lines changed

day7.py

Lines changed: 46 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,102 +1,76 @@
11
from textwrap import indent
2-
from typing import Generator
2+
from itertools import chain
3+
from typing import Iterable, Generator, Self
34

4-
# really just here for the type hinting
5-
class FSO: # FSO: FileSystemObject
6-
@property
7-
def name(self) -> str:
8-
return self._name
9-
10-
@property
11-
def size(self) -> int:
12-
return self._size
135

14-
class File(FSO):
6+
class File:
157
def __init__(self, size: str, name: str) -> None:
16-
self._size = int(size)
17-
self._name = name
18-
8+
self.size = int(size)
9+
self.name = name
10+
1911
def __repr__(self) -> str:
2012
return f"- {self.name} (file, size={self.size})"
2113

22-
class Directory(FSO):
14+
15+
class Directory:
2316
def __init__(self, name: str) -> None:
24-
self._name = name
25-
self.contents: dict[str, FSO] = {}
26-
27-
def add(self, child: FSO) -> None:
28-
self.contents[child.name] = child
29-
30-
# couldn't figure out how to get the type hinting correct on this one
31-
# for some reason can't type hint Generator[Directory, None, None]
17+
self.name = name
18+
self.contents: dict[str, File | Directory] = {}
19+
3220
@property
33-
def dirs(self) -> Generator[FSO, None, None]:
21+
def dirs(self) -> Generator[Self, None, None]: # type: ignore
3422
yield self
35-
for gen in [child.dirs for child in self.contents.values() if type(child)==Directory]:
36-
yield from gen
37-
38-
@property
39-
def dir_sizes(self):
40-
return (i.size for i in self.dirs)
41-
23+
yield from chain.from_iterable(
24+
child.dirs for child in self.contents.values() if type(child) == Directory
25+
)
26+
4227
@property
43-
def _size(self) -> int:
28+
def size(self) -> int:
4429
return sum(child.size for child in self.contents.values())
45-
30+
4631
def __repr__(self) -> str:
47-
children = '\n'.join(indent(str(child), ' '*2)
48-
for child in self.contents.values())
32+
children = "\n".join(
33+
indent(str(child), " " * 2) for child in self.contents.values()
34+
)
4935
return f"- {self.name} (dir)\n{children}"
5036

37+
5138
class Filesystem:
5239
def __init__(self) -> None:
53-
self.root: Directory = Directory("/")
54-
self.head: list[Directory] = [self.root] # (c/sh)ould be a doubly-linked list
55-
56-
@property
57-
def tail(self) -> Directory:
58-
assert len(self.head)>0 # sanity check
59-
return self.head[-1]
40+
self.root = Directory("/")
41+
self.head: list[Directory] = [self.root]
6042

6143
def cd(self, name: str) -> None:
6244
match name:
63-
case "/": # jump to root
45+
case "/": # jump to root
6446
self.head = [self.root]
65-
case "..": # up one
66-
assert len(self.head)>1 # sanity check
47+
case "..": # up one
6748
self.head.pop()
68-
case _: # everything else is a cd-into-child
69-
assert name in self.tail.contents.keys() # sanity check
70-
self.head.append(self.tail.contents[name])
71-
49+
case _: # everything else is a cd-into-child
50+
child = self.head[-1].contents[name]
51+
assert type(child) == Directory
52+
self.head.append(child)
53+
7254
def __repr__(self) -> str:
7355
return str(self.root)
7456

75-
def parse(lines: list[str]) -> Filesystem:
76-
fs = Filesystem()
57+
58+
def build(lines: Iterable[str], fs: Filesystem) -> Filesystem:
7759
for line in lines:
78-
if line[0]=="$": # the line is a command, either `ls` or `cd`
79-
match line[2:4]:
80-
case "cd":
81-
fs.cd(line.split(" ")[-1])
82-
case "ls":
83-
pass # falls through to the else block on next line
84-
case _: # sanity check
85-
raise ValueError(f"command {line[2:]} not in (`ls`, `cd`)")
86-
else: # the line is a result from an `ls` command
87-
item = line.split(" ")
88-
assert len(item)==2 # sanity check
89-
left, right = item
90-
match left:
91-
case "dir":
92-
fs.tail.add(Directory(right))
93-
case _:
94-
fs.tail.add(File(left, right))
60+
if line[0:4] == "$ cd":
61+
fs.cd(line[5:])
62+
elif line[0] != "$":
63+
child: File | Directory = (
64+
Directory(data[1])
65+
if (data := line.split(" "))[0] == "dir"
66+
else File(*data)
67+
)
68+
fs.head[-1].contents[child.name] = child
9569
return fs
9670

71+
9772
with open("input7.txt") as f:
98-
lines = [line.strip() for line in f]
99-
fs = parse(lines)
73+
fs = build((line.strip() for line in f), Filesystem())
10074

101-
print(sum(filter(lambda x: x <= 1e5, fs.root.dir_sizes))) # part 1
102-
print(min(filter(lambda x: x > fs.root.size - int(4e7), fs.root.dir_sizes))) # part 2
75+
print(sum(x.size for x in fs.root.dirs if x.size <= 1e5)) # type: ignore
76+
print(min(x.size for x in fs.root.dirs if x.size > fs.root.size - 4e7)) # type: ignore

0 commit comments

Comments
 (0)