diff --git a/exercises/practice/saddle-points/.approaches/config.json b/exercises/practice/saddle-points/.approaches/config.json new file mode 100644 index 0000000000..666fe033a6 --- /dev/null +++ b/exercises/practice/saddle-points/.approaches/config.json @@ -0,0 +1,43 @@ +{ + "introduction": { + "authors": ["BethanyG"], + "contributors": [] + }, + "approaches": [ + { + "uuid": "cc52d300-d1b3-48c9-9add-641bf9d1ec27", + "slug": "loop-and-append", + "title": "Loop and Append to List", + "blurb": "Use a series of loops to find maxima, minima and matrix coordinates.", + "authors": ["BethanyG"] + }, + { + "uuid": "452c2a9a-b4cc-4ed4-a6f8-d4b193f887c2", + "slug": "list-comprehension-and-nested-loop", + "title": "List Comprehensions with Nested Loop", + "blurb": "Use List Comprehensions to find maxima and minima and a nested loop for matrix coordinates.", + "authors": ["BethanyG"] + }, + { + "uuid": "d108db8d-d693-4f18-b551-e2a7e519783b", + "slug": "list-comprehension-and-nested-list-comprehension", + "title": "List Comprehensions with a Nested List Comprehension", + "blurb": "Use List Comprehensions to find maxima and minima and a nested comprehension for matrix coordinates", + "authors": ["BethanyG"] + }, + { + "uuid": "be78322f-0620-45ee-8e50-c4daae48addb", + "slug": "nested-list-comprehensions-with-coordinates", + "title": "Nested List Comprehensions with Coordinates.", + "blurb": "Use Nested List Comprehensions to find maxima, minima, and coordinates.", + "authors": ["BethanyG"] + }, + { + "uuid": "6649953d-5895-43cc-9471-a03eb7e78b84", + "slug": "nested-set-comprehensions-with-coordinates", + "title": "Nested Set Comprehensions with Coordinates.", + "blurb": "Use Nested Set Comprehensions to find maxima, minima, and coordinates.", + "authors": ["BethanyG"] + } + ] +} \ No newline at end of file diff --git a/exercises/practice/saddle-points/.approaches/introduction.md b/exercises/practice/saddle-points/.approaches/introduction.md new file mode 100644 index 0000000000..2cf7fb350b --- /dev/null +++ b/exercises/practice/saddle-points/.approaches/introduction.md @@ -0,0 +1,204 @@ +# Introduction + +There are several Pythonic ways to solve the Saddle Points exercise. +Among them are: + +- Use `loops` and appending to `lists` +- Use `list-comprehensions` for max/min and a nested `loop` for results. +- Use `list-comprehensions` for max/min and a nested `list comprehension` for results. +- Use nested `list-comprehensions` for max/min _with_ coordinates and a `list-comprehension` for results. +- Use nested `set-comprehensions` for max/min _with_ coordinates and a `list-comprehension` for results. + + +## General guidance + + +The goal of the Saddle Points exercise is to identify points in a number matrix (_modeled here as a `list` of `lists`_) where: + +* The point is the **maximum** value in its row. +* The point is the **minimum** value in its column. + +Points are returned as a list of _coordinate dictionaries_ `{'row':, 'column': }`. +The challenge here is to find an efficient method of extracting the coordinates from the matrix that match the max/min criteria. + +In core Python, a loop-within-loop cannot be easily avoided, due to the nested nature of the matrix representation. +All strategies here employ nested loops either in calculating the min/max, or in finding the row/column coordinates within the input matrix. + + +## Approach: Use Loops & Nested Loops and Append To Lists + +```python +def saddle_points(matrix): + if not matrix: + return [] + + if any(len(row) != len(matrix[0]) for row in matrix): + raise ValueError("irregular matrix") + + else: + row_maxima = [] + column_minima = [] + results = [] + + for row in matrix: + row_maxima.append(max(row)) + + for column in zip(*matrix): + column_minima.append(min(column)) + + for idx, value in enumerate(matrix[0]): + for index, value in enumerate(matrix): + if row_maxima[index] == column_minima[idx]: + results.append({'row': index+1, 'column': idx+1}) + + return results +``` + +This approach uses a series of loops and nested loops to iterate row-wise for max values, column-wise for min values, and matrix-wise for coordinates. +As max/min values are found, they are appended to row_maxima and column_minima lists. +Once the max and min lists are compiled, the matrix is traversed in a nested loop. +Where a row_maxima is also a column_minima, the resulting coordinates are appended to the _restults_ list. +For more information, see the [Loop and Append to List][loop-and-append] approach. + + +## Approach: Use List Comprehensions for Max/Min and a Nested Loop for Finding Coordinates + +```python +def saddle_points(matrix): + if not matrix: + return [] + + if any(len(row) != len(matrix[0]) for row in matrix): + raise ValueError("irregular matrix") + + else: + row_maxima = [max(row) for row in matrix] + column_minima = [min(col) for col in zip(*matrix)] + + results = [] + + for idx, value in enumerate(matrix[0]): + for index, element in enumerate(matrix): + if row_maxima[index] == column_minima[idx]: + results.append({'row': index+1, 'column': idx+1}) + + + return results or [] +``` + +This approach uses [`list comprehensions`][list-comprehensions] to compile the row_maxima and column_minima lists. +Once the max/min `lists` are compiled, the matrix is traversed in a nested loop, and the coordinates where row_maxima == column_minima are appended to the _results_ `list`. +For details, check out the [List Comprehensions and Nested Loop][ list-comprehension-and-nested-loop] approach. + + +## Approach: Use List Comprehensions for Max/Min & A Nested List Comprehension for Finding Coordinates + + +```python +def saddle_points(matrix): + if len(set(map(len, matrix))) > 1: + raise ValueError("irregular matrix") + + row_maxima = [max(row) for row in matrix] + col_minima = [min(col) for col in zip(*matrix)] + + return [{"row": rindex, "column": cindex} for + rindex, row in enumerate(matrix, start=1) for + cindex, _ in enumerate(row, start=1) if + row_maxima[rindex-1] == col_minima[cindex-1]] +``` + + +Like the approach above, two `list comprehensions` are used to compile row_maxima and column_minima. +Once the two lists are compiled, a nested `list comprehension` is then used to both traverse the matrix and compile the coordinate result list. +For more information, read the [List Comprehensions and Nested list Comprehension][list-comprehension-and-nested-list-comprehension] approach. + + + +## Approach: Use Nested List Comprehensions for Max/Min/Coordinates and a List Comprehension for Results + + + +```python +def saddle_points(matrix): + if not matrix: + return [] + + if any(len(item) != len(matrix[0]) for item in matrix): + raise ValueError("irregular matrix") + + else: + row_maxima = [(index, eindex, element) for index, row in + enumerate(matrix) for eindex, element in + enumerate(row) if max(row) == element] + + + col_minima = [(eindex, index, element) for index, col in + enumerate(zip(*matrix)) for eindex, element in + enumerate(col) if min(col) == element] + + + return [{'row': item[0]+1, 'column': item[1]+1} for + item in col_minima if item in row_maxima] +``` + +In this approach, the nested loops appear in the row_maxima and column_minima `list comprehensions`, which also compile the coordinates of each max/min 'point'. +The results are then compiled in a list comprehension that matches row entries to column entries. +For more information, see the [nested list comprehensions with coordinates][nested-list-comprehensions-with-coordinates] approach. + + +## Approach: Use Nested Set Comprehensions for Max/Min/Coordinates and a List Comprehension for Results + +```python +def saddle_points(matrix): + if not matrix: + return [] + + if any(len(row) != len(matrix[0]) for row in matrix): + raise ValueError("irregular matrix") + + else: + row_maxima = {(index, eindex, element) for index, row in + enumerate(matrix, start=1) for eindex, element in + enumerate(row, start=1) if max(row) == element} + + + column_minima = {(eindex, index, element) for index, col in + enumerate(zip(*matrix), start=1) for eindex, element in + enumerate(col, start=1) if min(col) == element} + + + return [{'row': item[0], 'column': item[1]} for + item in row_maxima & column_minima] +``` + +This approach is very similar to the one above, but uses nested `set comprehensions` to compile row_maxima, column_minima, and 'point' coordinates. +The results are then compiled in a `list comprehension` that iterates over the set intersection of row_maxima & column_minima. +For details, see the [nested set comprehension with coordinates][nested-set-comprehensions-with-coordinates] approach. + + + +## Other approaches + + +Beside these five idiomatic approaches, there are a multitude of possible variations using different combinations of loops, comprehensions, and filtering methods. + +However, it is very difficult to avoid nested loops, as each column of each row (_or vice-versa_) has to be identified by (row, column) coordinates and checked to see if the 'point' fulfills the Max/Min requirements. + + +## Which approach to use? + + +All of these approaches are roughly equivalent given the nested nature of the data. + +Using comprehensions and sets might still give a slight performance boost, but they may also be harder to read or understand for others. +The naive approach of loops and list-append is most likely the easiest to follow for those not familiar with the comprehension form. + + +[ list-comprehension-and-nested-loop]:https://exercism.org/tracks/python/exercises/saddle-points/approaches/list-comprehension-and-nested-loop +[ loop-and-append]: https://exercism.org/tracks/python/exercises/saddle-points/approaches/loop-and-append +[list-comprehension-and-nested-list-comprehension ]:https://exercism.org/tracks/python/exercises/saddle-points/approaches/list-comprehension-and-nested-list-comprehension +[list-comprehensions]: https://treyhunner.com/2015/12/python-list-comprehensions-now-in-color/#:~:text=What%20are%20list%20comprehensions%3F,can%20be%20transformed%20as%20needed. +[nested-list-comprehensions-with-coordinates ]:https://exercism.org/tracks/python/exercises/saddle-points/approaches/nested-list-comprehensions-with-coordinates +[nested-set-comprehensions-with-coordinates ]:https://exercism.org/tracks/python/exercises/saddle-points/approaches/nested-set-comprehensions-with-coordinates + diff --git a/exercises/practice/saddle-points/.approaches/list-comprehension-and-nested-list-comprehension/content.md b/exercises/practice/saddle-points/.approaches/list-comprehension-and-nested-list-comprehension/content.md new file mode 100644 index 0000000000..4132480621 --- /dev/null +++ b/exercises/practice/saddle-points/.approaches/list-comprehension-and-nested-list-comprehension/content.md @@ -0,0 +1,16 @@ +# List Comprehensions with a Nested List Comprehension + + +```python +def saddle_points(matrix): + if len(set(map(len, matrix))) > 1: + raise ValueError("irregular matrix") + + row_maxima = [max(row) for row in matrix] + col_minima = [min(col) for col in zip(*matrix)] + + return [{"row": rindex, "column": cindex} for + rindex, row in enumerate(matrix, start=1) for + cindex, _ in enumerate(row, start=1) if + row_maxima[rindex-1] == col_minima[cindex-1]] +``` diff --git a/exercises/practice/saddle-points/.approaches/list-comprehension-and-nested-list-comprehension/snippet.txt b/exercises/practice/saddle-points/.approaches/list-comprehension-and-nested-list-comprehension/snippet.txt new file mode 100644 index 0000000000..aa49d3dc95 --- /dev/null +++ b/exercises/practice/saddle-points/.approaches/list-comprehension-and-nested-list-comprehension/snippet.txt @@ -0,0 +1,7 @@ +row_maxima = [max(row) for row in matrix] +col_minima = [min(col) for col in zip(*matrix)] + +return [{"row": rindex, "column": cindex} for + rindex, row in enumerate(matrix, start=1) for + cindex, _ in enumerate(row, start=1) if + row_maxima[rindex-1] == col_minima[cindex-1]] \ No newline at end of file diff --git a/exercises/practice/saddle-points/.approaches/list-comprehension-and-nested-loop/content.md b/exercises/practice/saddle-points/.approaches/list-comprehension-and-nested-loop/content.md new file mode 100644 index 0000000000..0e932311f9 --- /dev/null +++ b/exercises/practice/saddle-points/.approaches/list-comprehension-and-nested-loop/content.md @@ -0,0 +1,24 @@ +# List Comprehensions And a Nested Loop + +```python +def saddle_points(matrix): + if not matrix: + return [] + + if any(len(row) != len(matrix[0]) for row in matrix): + raise ValueError("irregular matrix") + + else: + row_maxima = [max(row) for row in matrix] + column_minima = [min(col) for col in zip(*matrix)] + + results = [] + + for idx, value in enumerate(matrix[0]): + for index, element in enumerate(matrix): + if row_maxima[index] == column_minima[idx]: + results.append({'row': index+1, 'column': idx+1}) + + + return results or [] +``` diff --git a/exercises/practice/saddle-points/.approaches/list-comprehension-and-nested-loop/snippet.txt b/exercises/practice/saddle-points/.approaches/list-comprehension-and-nested-loop/snippet.txt new file mode 100644 index 0000000000..03da050c7c --- /dev/null +++ b/exercises/practice/saddle-points/.approaches/list-comprehension-and-nested-loop/snippet.txt @@ -0,0 +1,8 @@ +row_maxima = [max(row) for row in matrix] +column_minima = [min(col) for col in zip(*matrix)] +results = [] + +for idx, value in enumerate(matrix[0]): + for index, element in enumerate(matrix): + if row_maxima[index] == column_minima[idx]: + results.append({'row': index+1, 'column': idx+1}) \ No newline at end of file diff --git a/exercises/practice/saddle-points/.approaches/loop-and-append/content.md b/exercises/practice/saddle-points/.approaches/loop-and-append/content.md new file mode 100644 index 0000000000..c75fc477e3 --- /dev/null +++ b/exercises/practice/saddle-points/.approaches/loop-and-append/content.md @@ -0,0 +1,30 @@ +# Loop and Append to List + + +```python +def saddle_points(matrix): + if not matrix: + return [] + + if any(len(row) != len(matrix[0]) for row in matrix): + raise ValueError("irregular matrix") + + else: + row_maxima = [] + column_minima = [] + results = [] + + for row in matrix: + row_maxima.append(max(row)) + + for column in zip(*matrix): + column_minima.append(min(column)) + + for idx, value in enumerate(matrix[0]): + for index, value in enumerate(matrix): + if row_maxima[index] == column_minima[idx]: + results.append({'row': index+1, 'column': idx+1}) + + return results +``` + diff --git a/exercises/practice/saddle-points/.approaches/loop-and-append/snippet.txt b/exercises/practice/saddle-points/.approaches/loop-and-append/snippet.txt new file mode 100644 index 0000000000..cec1550036 --- /dev/null +++ b/exercises/practice/saddle-points/.approaches/loop-and-append/snippet.txt @@ -0,0 +1,8 @@ +row_maxima, column_minima, results = [], [], [] + +... + +for idx, value in enumerate(matrix[0]): + for index, value in enumerate(matrix): + if row_maxima[index] == column_minima[idx]: + results.append({'row': index+1, 'column': idx+1}) \ No newline at end of file diff --git a/exercises/practice/saddle-points/.approaches/nested-list-comprehensions-with-coordinates/content.md b/exercises/practice/saddle-points/.approaches/nested-list-comprehensions-with-coordinates/content.md new file mode 100644 index 0000000000..57a81c816c --- /dev/null +++ b/exercises/practice/saddle-points/.approaches/nested-list-comprehensions-with-coordinates/content.md @@ -0,0 +1,24 @@ +# Nested List Comprehensions with Coordinates + +```python +def saddle_points(matrix): + if not matrix: + return [] + + if any(len(item) != len(matrix[0]) for item in matrix): + raise ValueError("irregular matrix") + + else: + row_maxima = [(index, eindex, element) for index, row in + enumerate(matrix) for eindex, element in + enumerate(row) if max(row) == element] + + + col_minima = [(eindex, index, element) for index, col in + enumerate(zip(*matrix)) for eindex, element in + enumerate(col) if min(col) == element] + + + return [{'row': item[0]+1, 'column': item[1]+1} for + item in col_minima if item in row_maxima] +``` diff --git a/exercises/practice/saddle-points/.approaches/nested-list-comprehensions-with-coordinates/snippet.txt b/exercises/practice/saddle-points/.approaches/nested-list-comprehensions-with-coordinates/snippet.txt new file mode 100644 index 0000000000..5592718ca2 --- /dev/null +++ b/exercises/practice/saddle-points/.approaches/nested-list-comprehensions-with-coordinates/snippet.txt @@ -0,0 +1,7 @@ +row_maxima = [(index, eindex, element) for index, row in enumerate(matrix) for + eindex, element in enumerate(row) if max(row) == element] + +col_minima = [(eindex, index, element) for index, col in enumerate(zip(*matrix)) for + eindex, element in enumerate(col) if min(col) == element] + +return [{'row': item[0]+1, 'column': item[1]+1} for item in col_minima if item in row_maxima] \ No newline at end of file diff --git a/exercises/practice/saddle-points/.approaches/nested-set-comprehensions-with-coordinates/content.md b/exercises/practice/saddle-points/.approaches/nested-set-comprehensions-with-coordinates/content.md new file mode 100644 index 0000000000..aee707d4ce --- /dev/null +++ b/exercises/practice/saddle-points/.approaches/nested-set-comprehensions-with-coordinates/content.md @@ -0,0 +1,26 @@ +# Nested Set Comprehensions with Coordinates + +```python +def saddle_points(matrix): + if not matrix: + return [] + + if any(len(row) != len(matrix[0]) for row in matrix): + raise ValueError("irregular matrix") + + else: + row_maxima = {(index, eindex, element) for index, row in + enumerate(matrix, start=1) for eindex, element in + enumerate(row, start=1) if max(row) == element} + + + column_minima = {(eindex, index, element) for index, col in + enumerate(zip(*matrix), start=1) for eindex, element in + enumerate(col, start=1) if min(col) == element} + + + return [{'row': item[0], 'column': item[1]} for + item in row_maxima & column_minima] +``` + + diff --git a/exercises/practice/saddle-points/.approaches/nested-set-comprehensions-with-coordinates/snippet.txt b/exercises/practice/saddle-points/.approaches/nested-set-comprehensions-with-coordinates/snippet.txt new file mode 100644 index 0000000000..92a1b6f85c --- /dev/null +++ b/exercises/practice/saddle-points/.approaches/nested-set-comprehensions-with-coordinates/snippet.txt @@ -0,0 +1,7 @@ +row_maxima = {(index, eindex, element) for index, row in enumerate(matrix, start=1) for + eindex, element in enumerate(row, start=1) if max(row) == element} + +column_minima = {(eindex, index, element) for index, col in enumerate(zip(*matrix), start=1) for + eindex, element in enumerate(col, start=1) if min(col) == element} + +return [{'row': item[0], 'column': item[1]} for item in row_maxima & column_minima] \ No newline at end of file