From 3bdbc6735cb475d695e6be973efac110db8fc3c4 Mon Sep 17 00:00:00 2001 From: Andy Glick Date: Sun, 19 Apr 2020 22:30:47 -0400 Subject: [PATCH 1/2] quick pass through the code -- using the sonarlint suggestions to \tune up" the code --- Chapter1/fib1.py | 3 ++- Chapter1/fib2.py | 10 ++++++++-- Chapter1/fib3.py | 1 + Chapter1/fib4.py | 8 ++++++-- Chapter1/fib5.py | 4 +++- Chapter1/fib6.py | 2 +- Chapter1/hanoi.py | 6 +++++- Chapter1/unbreakable_encryption.py | 4 ++-- 8 files changed, 28 insertions(+), 10 deletions(-) diff --git a/Chapter1/fib1.py b/Chapter1/fib1.py index 7bfdbf0..025d8b3 100644 --- a/Chapter1/fib1.py +++ b/Chapter1/fib1.py @@ -9,7 +9,8 @@ # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, +# distributed under the License is distribute +# d on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. diff --git a/Chapter1/fib2.py b/Chapter1/fib2.py index cf572c8..221b441 100644 --- a/Chapter1/fib2.py +++ b/Chapter1/fib2.py @@ -16,11 +16,17 @@ def fib2(n: int) -> int: + + # print(n) + if n < 2: # base case return n - return fib2(n - 2) + fib2(n - 1) # recursive case + else: + return fib2(n - 2) + fib2(n - 1) # recursive case if __name__ == "__main__": print(fib2(5)) - print(fib2(10)) \ No newline at end of file + print(fib2(10)) + print(fib2(20)) + print(fib2(38)) diff --git a/Chapter1/fib3.py b/Chapter1/fib3.py index 63b190f..f61cb20 100644 --- a/Chapter1/fib3.py +++ b/Chapter1/fib3.py @@ -20,6 +20,7 @@ def fib3(n: int) -> int: if n not in memo: memo[n] = fib3(n - 1) + fib3(n - 2) # memoization + return memo[n] diff --git a/Chapter1/fib4.py b/Chapter1/fib4.py index 86446c2..6ae57f9 100644 --- a/Chapter1/fib4.py +++ b/Chapter1/fib4.py @@ -18,9 +18,13 @@ @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 + result: int = n + else: + result: int = fib4(n - 2) + fib4(n - 1) # recursive case + + return result if __name__ == "__main__": diff --git a/Chapter1/fib5.py b/Chapter1/fib5.py index adde39d..b2dec31 100644 --- a/Chapter1/fib5.py +++ b/Chapter1/fib5.py @@ -14,7 +14,9 @@ # See the License for the specific language governing permissions and # limitations under the License. def fib5(n: int) -> int: - if n == 0: return n # special case + if n == 0: + return n # special case + last: int = 0 # initially set to fib(0) next: int = 1 # initially set to fib(1) for _ in range(1, n): diff --git a/Chapter1/fib6.py b/Chapter1/fib6.py index 0f036fd..f84ff20 100644 --- a/Chapter1/fib6.py +++ b/Chapter1/fib6.py @@ -27,5 +27,5 @@ def fib6(n: int) -> Generator[int, None, None]: if __name__ == "__main__": - for i in fib6(50): + for i in fib6(60): print(i) diff --git a/Chapter1/hanoi.py b/Chapter1/hanoi.py index 48ab600..2914f3a 100644 --- a/Chapter1/hanoi.py +++ b/Chapter1/hanoi.py @@ -32,13 +32,17 @@ def __repr__(self) -> str: return repr(self._container) -num_discs: int = 3 +num_discs: int = 10 tower_a: Stack[int] = Stack() tower_b: Stack[int] = Stack() tower_c: Stack[int] = Stack() for i in range(1, num_discs + 1): tower_a.push(i) +print(tower_a) +print(tower_b) +print(tower_c) +print("") def hanoi(begin: Stack[int], end: Stack[int], temp: Stack[int], n: int) -> None: if n == 1: diff --git a/Chapter1/unbreakable_encryption.py b/Chapter1/unbreakable_encryption.py index 2a41151..ce705a4 100644 --- a/Chapter1/unbreakable_encryption.py +++ b/Chapter1/unbreakable_encryption.py @@ -32,8 +32,8 @@ def encrypt(original: str) -> Tuple[int, int]: return dummy, encrypted -def decrypt(key1: int, key2: int) -> str: - decrypted: int = key1 ^ key2 # XOR +def decrypt(key01: int, key02: int) -> str: + decrypted: int = key01 ^ key02 # XOR temp: bytes = decrypted.to_bytes((decrypted.bit_length() + 7) // 8, "big") return temp.decode() From 1ff05d3c3a0bc9c9096a6826421c1bbae941cc21 Mon Sep 17 00:00:00 2001 From: Andy Glick Date: Sun, 19 Apr 2020 23:16:33 -0400 Subject: [PATCH 2/2] added .gitignore file, more code "tune ups" following sonarlint suggestions --- .gitignore | 177 +++++++++++++++++++++++++++++++++++++ Chapter2/generic_search.py | 53 +++-------- Chapter2/maze.py | 29 +++--- 3 files changed, 205 insertions(+), 54 deletions(-) diff --git a/.gitignore b/.gitignore index 894a44c..c75ba37 100644 --- a/.gitignore +++ b/.gitignore @@ -102,3 +102,180 @@ venv.bak/ # mypy .mypy_cache/ +### Python template +# Byte-compiled / optimized / DLL files + +# C extensions + +# Distribution / packaging +pip-wheel-metadata/ +share/python-wheels/ + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. + +# Installer logs + +# Unit test / coverage reports +.nox/ +*.py,cover +cover/ + +# Translations + +# Django stuff: +db.sqlite3-journal + +# Flask stuff: + +# Scrapy stuff: + +# Sphinx documentation + +# PyBuilder +.pybuilder/ + +# Jupyter Notebook + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat.pid + +# SageMath parsed files + +# Environments + +# Spyder project settings + +# Rope project settings + +# mkdocs documentation + +# mypy +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +### Windows template +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/ + +*.iml +*.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### macOS template +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk diff --git a/Chapter2/generic_search.py b/Chapter2/generic_search.py index 6e0ceb7..b3f1c2b 100644 --- a/Chapter2/generic_search.py +++ b/Chapter2/generic_search.py @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations -from typing import TypeVar, Iterable, Sequence, Generic, List, Callable, Set, Deque, Dict, Any, Optional +from typing import TypeVar, Iterable, Sequence, Generic, List, Callable, Set, Deque, Any, Optional from typing_extensions import Protocol from heapq import heappush, heappop @@ -94,24 +94,8 @@ def __lt__(self, other: Node) -> bool: def dfs(initial: T, goal_test: Callable[[T], bool], successors: Callable[[T], List[T]]) -> Optional[Node[T]]: # frontier is where we've yet to go frontier: Stack[Node[T]] = Stack() - frontier.push(Node(initial, None)) - # explored is where we've been - explored: Set[T] = {initial} - # keep going while there is more to explore - while not frontier.empty: - current_node: Node[T] = frontier.pop() - current_state: T = current_node.state - # if we found the goal, we're done - if goal_test(current_state): - return current_node - # check where we can go next and haven't explored - for child in successors(current_state): - if child in explored: # skip children we already explored - continue - explored.add(child) - frontier.push(Node(child, current_node)) - return None # went through everything and never found goal + return explore_nodes(frontier, goal_test, initial, successors) def node_to_path(node: Node[T]) -> List[T]: @@ -126,7 +110,7 @@ def node_to_path(node: Node[T]) -> List[T]: class Queue(Generic[T]): def __init__(self) -> None: - self._container: Deque[T] = Deque() + self._container: Deque[T] = Deque[T] @property def empty(self) -> bool: @@ -145,10 +129,14 @@ def __repr__(self) -> str: def bfs(initial: T, goal_test: Callable[[T], bool], successors: Callable[[T], List[T]]) -> Optional[Node[T]]: # frontier is where we've yet to go frontier: Queue[Node[T]] = Queue() + + return explore_nodes(frontier, goal_test, initial, successors) + + +def explore_nodes(frontier, goal_test, initial, successors): frontier.push(Node(initial, None)) # explored is where we've been explored: Set[T] = {initial} - # keep going while there is more to explore while not frontier.empty: current_node: Node[T] = frontier.pop() @@ -183,31 +171,14 @@ def __repr__(self) -> str: return repr(self._container) -def astar(initial: T, goal_test: Callable[[T], bool], successors: Callable[[T], List[T]], heuristic: Callable[[T], float]) -> Optional[Node[T]]: +def astar(initial: T, goal_test: Callable[[T], bool], successors: Callable[[T], List[T]], heuristic: Callable[[T], + float]) -> Optional[Node[T]]: # frontier is where we've yet to go frontier: PriorityQueue[Node[T]] = PriorityQueue() - frontier.push(Node(initial, None, 0.0, heuristic(initial))) - # explored is where we've been - explored: Dict[T, float] = {initial: 0.0} - - # keep going while there is more to explore - while not frontier.empty: - current_node: Node[T] = frontier.pop() - current_state: T = current_node.state - # if we found the goal, we're done - if goal_test(current_state): - return current_node - # check where we can go next and haven't explored - for child in successors(current_state): - new_cost: float = current_node.cost + 1 # 1 assumes a grid, need a cost function for more sophisticated apps - - if child not in explored or explored[child] > new_cost: - explored[child] = new_cost - frontier.push(Node(child, current_node, new_cost, heuristic(child))) - return None # went through everything and never found goal + return explore_nodes(frontier, goal_test, initial, successors) if __name__ == "__main__": print(linear_contains([1, 5, 15, 15, 15, 15, 20], 5)) # True print(binary_contains(["a", "d", "e", "f", "z"], "f")) # True - print(binary_contains(["john", "mark", "ronald", "sarah"], "sheila")) # False \ No newline at end of file + print(binary_contains(["john", "mark", "ronald", "sarah"], "sheila")) # False diff --git a/Chapter2/maze.py b/Chapter2/maze.py index 6283ba1..13a6594 100644 --- a/Chapter2/maze.py +++ b/Chapter2/maze.py @@ -13,11 +13,11 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from enum import Enum -from typing import List, NamedTuple, Callable, Optional import random +from enum import Enum from math import sqrt -from generic_search import dfs, bfs, node_to_path, astar, Node +from typing import List, NamedTuple, Callable, Optional +from Chapter2.generic_search import dfs, bfs, node_to_path, astar, Node class Cell(str, Enum): @@ -34,14 +34,15 @@ class MazeLocation(NamedTuple): class Maze: - def __init__(self, rows: int = 10, columns: int = 10, sparseness: float = 0.2, start: MazeLocation = MazeLocation(0, 0), goal: MazeLocation = MazeLocation(9, 9)) -> None: + def __init__(self, rows: int = 10, columns: int = 10, sparseness: float = 0.2, + start: MazeLocation = MazeLocation(0, 0), goal: MazeLocation = MazeLocation(9, 9)) -> None: # initialize basic instance variables self._rows: int = rows self._columns: int = columns 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 @@ -81,7 +82,7 @@ def mark(self, path: List[MazeLocation]): self._grid[maze_location.row][maze_location.column] = Cell.PATH self._grid[self.start.row][self.start.column] = Cell.START self._grid[self.goal.row][self.goal.column] = Cell.GOAL - + def clear(self, path: List[MazeLocation]): for maze_location in path: self._grid[maze_location.row][maze_location.column] = Cell.EMPTY @@ -90,19 +91,21 @@ def clear(self, path: List[MazeLocation]): def euclidean_distance(goal: MazeLocation) -> Callable[[MazeLocation], float]: - def distance(ml: MazeLocation) -> float: + def inner_distance(ml: MazeLocation) -> float: xdist: int = ml.column - goal.column ydist: int = ml.row - goal.row - return sqrt((xdist * xdist) + (ydist * ydist)) - return distance + return sqrt(xdist * xdist + ydist * ydist) + + return inner_distance def manhattan_distance(goal: MazeLocation) -> Callable[[MazeLocation], float]: - def distance(ml: MazeLocation) -> float: + def inner_distance(ml: MazeLocation) -> float: xdist: int = abs(ml.column - goal.column) ydist: int = abs(ml.row - goal.row) - return (xdist + ydist) - return distance + return xdist + ydist + + return inner_distance if __name__ == "__main__": @@ -134,4 +137,4 @@ def distance(ml: MazeLocation) -> float: else: path3: List[MazeLocation] = node_to_path(solution3) m.mark(path3) - print(m) \ No newline at end of file + print(m)