diff --git a/Chapter1/fib2.py b/Chapter1/fib2.py index cf572c8..fdc64d2 100644 --- a/Chapter1/fib2.py +++ b/Chapter1/fib2.py @@ -16,9 +16,7 @@ def fib2(n: int) -> int: - if n < 2: # base case - return n - return fib2(n - 2) + fib2(n - 1) # recursive case + return n if n < 2 else fib2(n - 2) + fib2(n - 1) if __name__ == "__main__": diff --git a/Chapter1/fib4.py b/Chapter1/fib4.py index 86446c2..a6125fd 100644 --- a/Chapter1/fib4.py +++ b/Chapter1/fib4.py @@ -18,9 +18,7 @@ @lru_cache(maxsize=None) def fib4(n: int) -> int: # same definition as fib2() - if n < 2: # base case - return n - return fib4(n - 2) + fib4(n - 1) # recursive case + return n if n < 2 else fib4(n - 2) + fib4(n - 1) if __name__ == "__main__": diff --git a/Chapter1/trivial_compression.py b/Chapter1/trivial_compression.py index 3cf2a9f..d44799d 100644 --- a/Chapter1/trivial_compression.py +++ b/Chapter1/trivial_compression.py @@ -32,22 +32,22 @@ def _compress(self, gene: str) -> None: elif nucleotide == "T": # change last two bits to 11 self.bit_string |= 0b11 else: - raise ValueError("Invalid Nucleotide:{}".format(nucleotide)) + raise ValueError(f"Invalid Nucleotide:{nucleotide}") def decompress(self) -> str: gene: str = "" for i in range(0, self.bit_string.bit_length() - 1, 2): # - 1 to exclude sentinel bits: int = self.bit_string >> i & 0b11 # get just 2 relevant bits - if bits == 0b00: # A + if bits == 0b00: gene += "A" - elif bits == 0b01: # C + elif bits == 0b01: gene += "C" - elif bits == 0b10: # G + elif bits == 0b10: gene += "G" - elif bits == 0b11: # T + elif bits == 0b11: gene += "T" else: - raise ValueError("Invalid bits:{}".format(bits)) + raise ValueError(f"Invalid bits:{bits}") return gene[::-1] # [::-1] reverses string by slicing backwards def __str__(self) -> str: # string representation for pretty printing @@ -57,8 +57,10 @@ def __str__(self) -> str: # string representation for pretty printing if __name__ == "__main__": from sys import getsizeof original: str = "TAGGGATTAACCGTTATATATATATAGCCATGGATCGATTATATAGGGATTAACCGTTATATATATATAGCCATGGATCGATTATA" * 100 - print("original is {} bytes".format(getsizeof(original))) + print(f"original is {getsizeof(original)} bytes") compressed: CompressedGene = CompressedGene(original) # compress - print("compressed is {} bytes".format(getsizeof(compressed.bit_string))) + print(f"compressed is {getsizeof(compressed.bit_string)} bytes") print(compressed) # decompress - print("original and decompressed are the same: {}".format(original == compressed.decompress())) \ No newline at end of file + print( + f"original and decompressed are the same: {original == compressed.decompress()}" + ) \ No newline at end of file diff --git a/Chapter2/dna_search.py b/Chapter2/dna_search.py index 1c3c0ce..cc650c9 100644 --- a/Chapter2/dna_search.py +++ b/Chapter2/dna_search.py @@ -38,10 +38,7 @@ def string_to_gene(s: str) -> Gene: def linear_contains(gene: Gene, key_codon: Codon) -> bool: - for codon in gene: - if codon == key_codon: - return True - return False + return key_codon in gene acg: Codon = (Nucleotide.A, Nucleotide.C, Nucleotide.G) diff --git a/Chapter2/generic_search.py b/Chapter2/generic_search.py index 6e0ceb7..c8dccd8 100644 --- a/Chapter2/generic_search.py +++ b/Chapter2/generic_search.py @@ -22,10 +22,7 @@ def linear_contains(iterable: Iterable[T], key: T) -> bool: - for item in iterable: - if item == key: - return True - return False + return key in iterable C = TypeVar("C", bound="Comparable") diff --git a/Chapter2/maze.py b/Chapter2/maze.py index 6283ba1..0005ded 100644 --- a/Chapter2/maze.py +++ b/Chapter2/maze.py @@ -41,7 +41,10 @@ def __init__(self, rows: int = 10, columns: int = 10, sparseness: float = 0.2, s self.start: MazeLocation = start self.goal: MazeLocation = goal # fill the grid with empty cells - self._grid: List[List[Cell]] = [[Cell.EMPTY for c in range(columns)] for r in range(rows)] + self._grid: List[List[Cell]] = [ + [Cell.EMPTY for _ in range(columns)] for _ in range(rows) + ] + # populate the grid with blocked cells self._randomly_fill(rows, columns, sparseness) # fill the start and goal locations in @@ -56,10 +59,7 @@ def _randomly_fill(self, rows: int, columns: int, sparseness: float): # return a nicely formatted version of the maze for printing def __str__(self) -> str: - output: str = "" - for row in self._grid: - output += "".join([c.value for c in row]) + "\n" - return output + return "".join("".join([c.value for c in row]) + "\n" for row in self._grid) def goal_test(self, ml: MazeLocation) -> bool: return ml == self.goal @@ -68,11 +68,11 @@ def successors(self, ml: MazeLocation) -> List[MazeLocation]: locations: List[MazeLocation] = [] if ml.row + 1 < self._rows and self._grid[ml.row + 1][ml.column] != Cell.BLOCKED: locations.append(MazeLocation(ml.row + 1, ml.column)) - if ml.row - 1 >= 0 and self._grid[ml.row - 1][ml.column] != Cell.BLOCKED: + if ml.row >= 1 and self._grid[ml.row - 1][ml.column] != Cell.BLOCKED: locations.append(MazeLocation(ml.row - 1, ml.column)) if ml.column + 1 < self._columns and self._grid[ml.row][ml.column + 1] != Cell.BLOCKED: locations.append(MazeLocation(ml.row, ml.column + 1)) - if ml.column - 1 >= 0 and self._grid[ml.row][ml.column - 1] != Cell.BLOCKED: + if ml.column >= 1 and self._grid[ml.row][ml.column - 1] != Cell.BLOCKED: locations.append(MazeLocation(ml.row, ml.column - 1)) return locations @@ -93,7 +93,7 @@ def euclidean_distance(goal: MazeLocation) -> Callable[[MazeLocation], float]: def distance(ml: MazeLocation) -> float: xdist: int = ml.column - goal.column ydist: int = ml.row - goal.row - return sqrt((xdist * xdist) + (ydist * ydist)) + return sqrt(xdist**2 + ydist**2) return distance diff --git a/Chapter2/missionaries.py b/Chapter2/missionaries.py index 92a5692..9b0bb3a 100644 --- a/Chapter2/missionaries.py +++ b/Chapter2/missionaries.py @@ -73,7 +73,7 @@ def successors(self) -> List[MCState]: def display_solution(path: List[MCState]): - if len(path) == 0: # sanity check + if not path: # sanity check return old_state: MCState = path[0] print(old_state) diff --git a/Chapter3/csp.py b/Chapter3/csp.py index c8f9ce3..5030dec 100644 --- a/Chapter3/csp.py +++ b/Chapter3/csp.py @@ -55,10 +55,10 @@ def add_constraint(self, constraint: Constraint[V, D]) -> None: # Check if the value assignment is consistent by checking all constraints # for the given variable against it def consistent(self, variable: V, assignment: Dict[V, D]) -> bool: - for constraint in self.constraints[variable]: - if not constraint.satisfied(assignment): - return False - return True + return all( + constraint.satisfied(assignment) + for constraint in self.constraints[variable] + ) def backtracking_search(self, assignment: Dict[V, D] = {}) -> Optional[Dict[V, D]]: # assignment is complete if every variable is assigned (our base case) diff --git a/Chapter3/map_coloring.py b/Chapter3/map_coloring.py index 4332f11..a95eb4f 100644 --- a/Chapter3/map_coloring.py +++ b/Chapter3/map_coloring.py @@ -36,9 +36,10 @@ def satisfied(self, assignment: Dict[str, str]) -> bool: if __name__ == "__main__": variables: List[str] = ["Western Australia", "Northern Territory", "South Australia", "Queensland", "New South Wales", "Victoria", "Tasmania"] - domains: Dict[str, List[str]] = {} - for variable in variables: - domains[variable] = ["red", "green", "blue"] + domains: Dict[str, List[str]] = { + variable: ["red", "green", "blue"] for variable in variables + } + csp: CSP[str, str] = CSP(variables, domains) csp.add_constraint(MapColoringConstraint("Western Australia", "Northern Territory")) csp.add_constraint(MapColoringConstraint("Western Australia", "South Australia")) diff --git a/Chapter3/queens.py b/Chapter3/queens.py index 31a82a5..a6d69d8 100644 --- a/Chapter3/queens.py +++ b/Chapter3/queens.py @@ -38,9 +38,10 @@ def satisfied(self, assignment: Dict[int, int]) -> bool: if __name__ == "__main__": columns: List[int] = [1, 2, 3, 4, 5, 6, 7, 8] - rows: Dict[int, List[int]] = {} - for column in columns: - rows[column] = [1, 2, 3, 4, 5, 6, 7, 8] + rows: Dict[int, List[int]] = { + column: [1, 2, 3, 4, 5, 6, 7, 8] for column in columns + } + csp: CSP[int, int] = CSP(columns, rows) csp.add_constraint(QueensConstraint(columns)) solution: Optional[Dict[int, int]] = csp.backtracking_search() diff --git a/Chapter3/send_more_money.py b/Chapter3/send_more_money.py index 2071c10..049a01a 100644 --- a/Chapter3/send_more_money.py +++ b/Chapter3/send_more_money.py @@ -46,9 +46,10 @@ def satisfied(self, assignment: Dict[str, int]) -> bool: if __name__ == "__main__": letters: List[str] = ["S", "E", "N", "D", "M", "O", "R", "Y"] - possible_digits: Dict[str, List[int]] = {} - for letter in letters: - possible_digits[letter] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + possible_digits: Dict[str, List[int]] = { + letter: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] for letter in letters + } + possible_digits["M"] = [1] # so we don't get answers starting with a 0 csp: CSP[str, int] = CSP(letters, possible_digits) csp.add_constraint(SendMoreMoneyConstraint(letters)) diff --git a/Chapter3/word_search.py b/Chapter3/word_search.py index c6a6085..1df8a36 100644 --- a/Chapter3/word_search.py +++ b/Chapter3/word_search.py @@ -28,7 +28,7 @@ class GridLocation(NamedTuple): def generate_grid(rows: int, columns: int) -> Grid: # initialize grid with random letters - return [[choice(ascii_uppercase) for c in range(columns)] for r in range(rows)] + return [[choice(ascii_uppercase) for _ in range(columns)] for _ in range(rows)] def display_grid(grid: Grid) -> None: @@ -74,9 +74,10 @@ def satisfied(self, assignment: Dict[str, List[GridLocation]]) -> bool: if __name__ == "__main__": grid: Grid = generate_grid(9, 9) words: List[str] = ["MATTHEW", "JOE", "MARY", "SARAH", "SALLY"] - locations: Dict[str, List[List[GridLocation]]] = {} - for word in words: - locations[word] = generate_domain(word, grid) + locations: Dict[str, List[List[GridLocation]]] = { + word: generate_domain(word, grid) for word in words + } + csp: CSP[str, List[GridLocation]] = CSP(words, locations) csp.add_constraint(WordSearchConstraint(words)) solution: Optional[Dict[str, List[GridLocation]]] = csp.backtracking_search() diff --git a/Chapter4/dijkstra.py b/Chapter4/dijkstra.py index 9bdee27..99ff954 100644 --- a/Chapter4/dijkstra.py +++ b/Chapter4/dijkstra.py @@ -66,20 +66,16 @@ def dijkstra(wg: WeightedGraph[V], root: V) -> Tuple[List[Optional[float]], Dict # Helper function to get easier access to dijkstra results def distance_array_to_vertex_dict(wg: WeightedGraph[V], distances: List[Optional[float]]) -> Dict[V, Optional[float]]: - distance_dict: Dict[V, Optional[float]] = {} - for i in range(len(distances)): - distance_dict[wg.vertex_at(i)] = distances[i] - return distance_dict + return {wg.vertex_at(i): distances[i] for i in range(len(distances))} # Takes a dictionary of edges to reach each node and returns a list of # edges that goes from `start` to `end` def path_dict_to_path(start: int, end: int, path_dict: Dict[int, WeightedEdge]) -> WeightedPath: - if len(path_dict) == 0: + if not path_dict: return [] - edge_path: WeightedPath = [] e: WeightedEdge = path_dict[end] - edge_path.append(e) + edge_path: WeightedPath = [e] while e.u != start: e = path_dict[e.u] edge_path.append(e) diff --git a/Chapter4/graph.py b/Chapter4/graph.py index f08601e..13f1b4c 100644 --- a/Chapter4/graph.py +++ b/Chapter4/graph.py @@ -82,10 +82,10 @@ def edges_for_vertex(self, vertex: V) -> List[Edge]: # Make it easy to pretty-print a Graph def __str__(self) -> str: - desc: str = "" - for i in range(self.vertex_count): - desc += f"{self.vertex_at(i)} -> {self.neighbors_for_index(i)}\n" - return desc + return "".join( + f"{self.vertex_at(i)} -> {self.neighbors_for_index(i)}\n" + for i in range(self.vertex_count) + ) if __name__ == "__main__": diff --git a/Chapter4/mst.py b/Chapter4/mst.py index d9f7ba1..7b43822 100644 --- a/Chapter4/mst.py +++ b/Chapter4/mst.py @@ -23,7 +23,7 @@ def total_weight(wp: WeightedPath) -> float: - return sum([e.weight for e in wp]) + return sum(e.weight for e in wp) def mst(wg: WeightedGraph[V], start: int = 0) -> Optional[WeightedPath]: diff --git a/Chapter4/weighted_graph.py b/Chapter4/weighted_graph.py index 0acb273..1df18bf 100644 --- a/Chapter4/weighted_graph.py +++ b/Chapter4/weighted_graph.py @@ -35,16 +35,16 @@ def add_edge_by_vertices(self, first: V, second: V, weight: float) -> None: self.add_edge_by_indices(u, v, weight) def neighbors_for_index_with_weights(self, index: int) -> List[Tuple[V, float]]: - distance_tuples: List[Tuple[V, float]] = [] - for edge in self.edges_for_index(index): - distance_tuples.append((self.vertex_at(edge.v), edge.weight)) - return distance_tuples + return [ + (self.vertex_at(edge.v), edge.weight) + for edge in self.edges_for_index(index) + ] def __str__(self) -> str: - desc: str = "" - for i in range(self.vertex_count): - desc += f"{self.vertex_at(i)} -> {self.neighbors_for_index_with_weights(i)}\n" - return desc + return "".join( + f"{self.vertex_at(i)} -> {self.neighbors_for_index_with_weights(i)}\n" + for i in range(self.vertex_count) + ) if __name__ == "__main__": diff --git a/Chapter5/simple_equation.py b/Chapter5/simple_equation.py index d18f413..8712ee3 100644 --- a/Chapter5/simple_equation.py +++ b/Chapter5/simple_equation.py @@ -46,11 +46,10 @@ def mutate(self) -> None: self.x += 1 else: self.x -= 1 - else: # otherwise mutate y - if random() > 0.5: - self.y += 1 - else: - self.y -= 1 + elif random() > 0.5: + self.y += 1 + else: + self.y -= 1 def __str__(self) -> str: return f"X: {self.x} Y: {self.y} Fitness: {self.fitness()}" diff --git a/Chapter6/data_point.py b/Chapter6/data_point.py index df5c49c..e21eeb0 100644 --- a/Chapter6/data_point.py +++ b/Chapter6/data_point.py @@ -33,9 +33,11 @@ def distance(self, other: DataPoint) -> float: return sqrt(sum(differences)) def __eq__(self, other: object) -> bool: - if not isinstance(other, DataPoint): - return NotImplemented - return self.dimensions == other.dimensions + return ( + self.dimensions == other.dimensions + if isinstance(other, DataPoint) + else NotImplemented + ) def __repr__(self) -> str: return self._originals.__repr__() diff --git a/Chapter7/layer.py b/Chapter7/layer.py index b133c4b..0b30c1f 100644 --- a/Chapter7/layer.py +++ b/Chapter7/layer.py @@ -25,11 +25,13 @@ def __init__(self, previous_layer: Optional[Layer], num_neurons: int, learning_r self.previous_layer: Optional[Layer] = previous_layer self.neurons: List[Neuron] = [] # the following could all be one large list comprehension, but gets a bit long that way - for i in range(num_neurons): - if previous_layer is None: - random_weights: List[float] = [] - else: - random_weights = [random() for _ in range(len(previous_layer.neurons))] + for _ in range(num_neurons): + random_weights = ( + [] + if previous_layer is None + else [random() for _ in range(len(previous_layer.neurons))] + ) + neuron: Neuron = Neuron(random_weights, learning_rate, activation_function, derivative_activation_function) self.neurons.append(neuron) self.output_cache: List[float] = [0.0 for _ in range(num_neurons)] diff --git a/Chapter7/util.py b/Chapter7/util.py index b83a66e..0cb5af9 100644 --- a/Chapter7/util.py +++ b/Chapter7/util.py @@ -39,6 +39,6 @@ def normalize_by_feature_scaling(dataset: List[List[float]]) -> None: column: List[float] = [row[col_num] for row in dataset] maximum = max(column) minimum = min(column) - for row_num in range(len(dataset)): - dataset[row_num][col_num] = (dataset[row_num][col_num] - minimum) / (maximum - minimum) + for item in dataset: + item[col_num] = (item[col_num] - minimum) / (maximum - minimum) diff --git a/Chapter8/connectfour.py b/Chapter8/connectfour.py index 98bfa22..6314466 100644 --- a/Chapter8/connectfour.py +++ b/Chapter8/connectfour.py @@ -42,33 +42,25 @@ def generate_segments(num_columns: int, num_rows: int, segment_length: int) -> L # generate the vertical segments for c in range(num_columns): for r in range(num_rows - segment_length + 1): - segment: List[Tuple[int, int]] = [] - for t in range(segment_length): - segment.append((c, r + t)) + segment: List[Tuple[int, int]] = [(c, r + t) for t in range(segment_length)] segments.append(segment) # generate the horizontal segments for c in range(num_columns - segment_length + 1): for r in range(num_rows): - segment = [] - for t in range(segment_length): - segment.append((c + t, r)) + segment = [(c + t, r) for t in range(segment_length)] segments.append(segment) # generate the bottom left to top right diagonal segments for c in range(num_columns - segment_length + 1): for r in range(num_rows - segment_length + 1): - segment = [] - for t in range(segment_length): - segment.append((c + t, r + t)) + segment = [(c + t, r + t) for t in range(segment_length)] segments.append(segment) # generate the top left to bottom right diagonal segments for c in range(num_columns - segment_length + 1): for r in range(segment_length - 1, num_rows): - segment = [] - for t in range(segment_length): - segment.append((c + t, r - t)) + segment = [(c + t, r - t) for t in range(segment_length)] segments.append(segment) return segments @@ -166,17 +158,16 @@ def _evaluate_segment(self, segment: List[Tuple[int, int]], player: Piece) -> fl return score def evaluate(self, player: Piece) -> float: - total: float = 0 - for segment in C4Board.SEGMENTS: - total += self._evaluate_segment(segment, player) - return total + return sum( + self._evaluate_segment(segment, player) for segment in C4Board.SEGMENTS + ) def __repr__(self) -> str: display: str = "" for r in reversed(range(C4Board.NUM_ROWS)): display += "|" for c in range(C4Board.NUM_COLUMNS): - display += f"{self.position[c][r]}" + "|" + display += f"{self.position[c][r]}|" display += "\n" return display diff --git a/Chapter8/tictactoe.py b/Chapter8/tictactoe.py index 74192ff..4566e58 100644 --- a/Chapter8/tictactoe.py +++ b/Chapter8/tictactoe.py @@ -70,7 +70,7 @@ def is_win(self) -> bool: def evaluate(self, player: Piece) -> float: if self.is_win and self.turn == player: return -1 - elif self.is_win and self.turn != player: + elif self.is_win: return 1 else: return 0 diff --git a/Chapter9/phone_number_mnemonics.py b/Chapter9/phone_number_mnemonics.py index 19f39f4..dfc0342 100644 --- a/Chapter9/phone_number_mnemonics.py +++ b/Chapter9/phone_number_mnemonics.py @@ -29,9 +29,10 @@ def possible_mnemonics(phone_number: str) -> Iterable[Tuple[str, ...]]: - letter_tuples: List[Tuple[str, ...]] = [] - for digit in phone_number: - letter_tuples.append(phone_mapping.get(digit, (digit,))) + letter_tuples: List[Tuple[str, ...]] = [ + phone_mapping.get(digit, (digit,)) for digit in phone_number + ] + return product(*letter_tuples)