Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Pascals Triangle] Remastered for recursion #3150

Merged
merged 27 commits into from
Dec 10, 2022
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
0bcaee9
[Pascals Triangle] Remastered for recursion
PaulT89 Jun 4, 2022
05344fa
[Pascals Triangle] Remastered for recursion
PaulT89 Jul 24, 2022
6c3267f
Merge branch 'pascals-triangle-branch' of https://github.com/PaulT89/…
PaulT89 Jul 24, 2022
c60692a
Updated Exercise
PaulT89 Jul 25, 2022
6bfc5e7
Fixed bug
PaulT89 Jul 25, 2022
c403316
Fixed template indentation
PaulT89 Jul 25, 2022
f117629
Manual recursion limit
PaulT89 Jul 25, 2022
c382b93
Revert "Manual recursion limit"
PaulT89 Jul 25, 2022
81238a7
Minor formatting changes
PaulT89 Jul 26, 2022
a49fcba
Canonical sync fix
PaulT89 Jul 26, 2022
36dd632
[Pascals Triangle] Remastered for recursion
PaulT89 Jul 24, 2022
a30a895
[Pascals Triangle] Remastered for recursion
PaulT89 Jun 4, 2022
fe98d34
Updated Exercise
PaulT89 Jul 25, 2022
23bf92f
Fixed bug
PaulT89 Jul 25, 2022
50fb038
Fixed template indentation
PaulT89 Jul 25, 2022
eb66ad8
Manual recursion limit
PaulT89 Jul 25, 2022
d17c04a
Revert "Manual recursion limit"
PaulT89 Jul 25, 2022
40d2d04
Minor formatting changes
PaulT89 Jul 26, 2022
98b4a16
Canonical sync fix
PaulT89 Jul 26, 2022
071c1d1
Updated instructions
PaulT89 Jul 30, 2022
31bf4df
Merge branch 'main' into pr/3150
BethanyG Oct 14, 2022
b9a70d0
Merge branch 'pascals-triangle-branch' of https://github.com/PaulT89/…
BethanyG Oct 14, 2022
7cb868e
Merge branch 'main' into pascals-triangle-branch
PaulT89 Oct 15, 2022
095d1a8
Merge remote-tracking branch 'upstream/main' into pascals-triangle-br…
PaulT89 Nov 27, 2022
b468398
Template changes and small reflink edits
BethanyG Dec 10, 2022
0b30cd3
Update config.json
BethanyG Dec 10, 2022
205a4d5
Update config.json
BethanyG Dec 10, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 15 additions & 9 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -1247,6 +1247,21 @@
],
"difficulty": 3
},
{
"slug": "pascals-triangle",
"name": "Pascals Triangle",
"uuid": "e1e1c7d7-c1d9-4027-b90d-fad573182419",
"practices": ["recursion"],
"prerequisites": [
"basics",
"conditionals",
"functions",
"function-arguments",
BethanyG marked this conversation as resolved.
Show resolved Hide resolved
"lists",
"numbers"
],
"difficulty": 3
BethanyG marked this conversation as resolved.
Show resolved Hide resolved
},
{
"slug": "grep",
"name": "Grep",
Expand Down Expand Up @@ -2109,15 +2124,6 @@
"difficulty": 3,
"status": "deprecated"
},
{
"slug": "pascals-triangle",
"name": "Pascal's Triangle",
"uuid": "e1e1c7d7-c1d9-4027-b90d-fad573182419",
"practices": [],
"prerequisites": [],
"difficulty": 3,
"status": "deprecated"
},
{
"slug": "point-mutations",
"name": "Point Mutations",
Expand Down
10 changes: 10 additions & 0 deletions exercises/practice/pascals-triangle/.docs/hints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Hints

## General

- A more detailed description of recursive programming can be found [here][g4g]
- This exercise involves a test to ensure that you used a recursive solution
- If you are having trouble completing this exercise, try [using a loop first, and then convert it into a recursive solution][educative]

[g4g]: https://www.geeksforgeeks.org/recursion/
[educative]: https://www.educative.io/collection/page/6151088528949248/4547996664463360/6292303276670976
43 changes: 43 additions & 0 deletions exercises/practice/pascals-triangle/.docs/instructions.append.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Instructions append

## How this Exercise is Implemented in Python: Recursion

This exercise is designed to be completed using [concept:python/recursion](), rather than loops.
A recursive function is a function that calls itself, which is useful when solving problems that are defined in terms of themselves.
To avoid infinite recursion (or more specifically, to avoid overflowing the stack), something called a "base case" is used.
When the base case is reached, a non-recursive value is returned, which allows the previous function call to resolve and return its value, and so on, rippling back down the stack until the first function call returns the answer.
We could write a recursive function to find the answer to 5! (i.e. 5 * 4 * 3 * 2 * 1) like so:

````python
def factorial(number):
if number <= 1: # base case
return 1

return number * factorial(number - 1) # recursive case

print(factorial(5)) # returns 120
````

Finally, it should be noted that Python limits the number of times recursive calls can be made (1000 by default) and does not optimize for [tail recursion][tail-recursion].

## Exception messages

Sometimes it is necessary to [raise an exception][raising].
When you do this, you should always include a **meaningful error message** to indicate what the source of the error is.
This makes your code more readable and helps significantly with debugging.
For situations where you know that the error source will be a certain type, you can choose to raise one of the [built in error types][built-in-errors], but should still include a meaningful message.

This particular exercise requires that you use the [raise statement][raise-statement] to "throw" multiple `ValueErrors` if the `rows()` function is passed a negative number.
The tests will only pass if you both `raise` the `exception` and include a message with it.

To raise a `ValueError` with a message, write the message as an argument to the `exception` type:

```python
# if the rows function is passed a negative number.
raise ValueError("number of rows is negative")
```

[built-in-errors]: https://docs.python.org/3/library/exceptions.html#base-classes
[raise-statement]: https://docs.python.org/3/reference/simple_stmts.html#the-raise-statement
[raising]: https://docs.python.org/3/tutorial/errors.html#raising-exceptions
[tail-recursion]: https://www.geeksforgeeks.org/tail-recursion/
26 changes: 26 additions & 0 deletions exercises/practice/pascals-triangle/.meta/additional_tests.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"cases": [
{
"description": "negative rows are invalid",
"property": "rows",
"input": {
"count": -1
},
"expected": {
"error": "number of rows is negative",
"type": "ValueError"
}
},
{
"description": "solution is recursive",
"property": "rows",
"input": {
"count": "OVERFLOW"
},
"expected": {
"error": "maximum recursion depth exceeded",
"type": "RecursionError"
}
}
]
}
3 changes: 2 additions & 1 deletion exercises/practice/pascals-triangle/.meta/config.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"blurb": "Compute Pascal's triangle up to a given number of rows.",
"authors": [
"betegelse"
"betegelse",
"PaulT89"
],
"contributors": [
"behrtam",
Expand Down
14 changes: 5 additions & 9 deletions exercises/practice/pascals-triangle/.meta/example.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
def rows(row_count):
def rows(row_count, previous_row=[1]):
if row_count < 0:
return None
raise ValueError("number of rows is negative")
elif row_count == 0:
return []
row_list = []
for idx in range(row_count):
ronald = [1]
for edx in range(idx):
ronald.append(sum(row_list[-1][edx:edx+2]))
row_list.append(ronald)
return row_list
temp_row = previous_row + [0]
new_row = list(map(sum, zip(temp_row, temp_row[::-1])))
return [previous_row] + rows(row_count - 1, new_row)
39 changes: 39 additions & 0 deletions exercises/practice/pascals-triangle/.meta/template.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{%- import "generator_macros.j2" as macros with context -%}
import sys
{{ macros.header() }}

TRIANGLE = [
[1],
[1, 1],
[1, 2, 1],
[1, 3, 3, 1],
[1, 4, 6, 4, 1],
[1, 5, 10, 10, 5, 1],
[1, 6, 15, 20, 15, 6, 1],
[1, 7, 21, 35, 35, 21, 7, 1],
[1, 8, 28, 56, 70, 56, 28, 8, 1],
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
]

class {{ exercise | camel_case }}Test(unittest.TestCase):
{%- for case in cases %}
def test_{{ case["description"] | to_snake }}(self):
self.assertEqual({{ case["property"] }}({{ case["input"]["count"] }}), TRIANGLE[:{{ case["input"]["count"] }}])
{% endfor %}

# Additional tests for this track
{%- for case in additional_cases %}
def test_{{ case["description"] | to_snake }}(self):
{%- if case is error_case %}
with self.assertRaises({{ case["expected"]["type"] }}) as err:
{%- if case["input"]["count"] == "OVERFLOW" %}
rows(sys.getrecursionlimit() + 10)
self.assertEqual(type(err.exception), RecursionError)
self.assertEqual(err.exception.args[0][:32], "maximum recursion depth exceeded")
{%- else %}
rows(-1)
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "number of rows is negative")
{%- endif %}
{%- endif %}
{% endfor %}
32 changes: 21 additions & 11 deletions exercises/practice/pascals-triangle/pascals_triangle_test.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import sys
import unittest

from pascals_triangle import rows
from pascals_triangle import (
rows,
)


# Tests adapted from `problem-specifications//canonical-data.json` @ v1.2.0
# Tests adapted from `problem-specifications//canonical-data.json`

TRIANGLE = [
[1],
Expand All @@ -15,13 +17,13 @@
[1, 6, 15, 20, 15, 6, 1],
[1, 7, 21, 35, 35, 21, 7, 1],
[1, 8, 28, 56, 70, 56, 28, 8, 1],
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1],
]


class PascalsTriangleTest(unittest.TestCase):
def test_zero_rows(self):
self.assertEqual(rows(0), [])
self.assertEqual(rows(0), TRIANGLE[:0])

def test_single_row(self):
self.assertEqual(rows(1), TRIANGLE[:1])
Expand All @@ -44,9 +46,17 @@ def test_six_rows(self):
def test_ten_rows(self):
self.assertEqual(rows(10), TRIANGLE[:10])

def test_negative_rows(self):
self.assertEqual(rows(-1), None)


if __name__ == '__main__':
unittest.main()
# Additional tests for this track
def test_negative_rows_are_invalid(self):
with self.assertRaises(ValueError) as err:
rows(-1)
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "number of rows is negative")

def test_solution_is_recursive(self):
with self.assertRaises(RecursionError) as err:
rows(sys.getrecursionlimit() + 10)
self.assertEqual(type(err.exception), RecursionError)
self.assertEqual(
err.exception.args[0][:32], "maximum recursion depth exceeded"
)