diff --git a/episodes/10-defensive.md b/episodes/10-defensive.md index c945a9627..3e13d44c8 100644 --- a/episodes/10-defensive.md +++ b/episodes/10-defensive.md @@ -409,12 +409,11 @@ let's put them all in a function: ```python def test_range_overlap(): - assert range_overlap([ (0.0, 1.0), (5.0, 6.0) ]) == None - assert range_overlap([ (0.0, 1.0), (1.0, 2.0) ]) == None assert range_overlap([ (0.0, 1.0) ]) == (0.0, 1.0) assert range_overlap([ (2.0, 3.0), (2.0, 4.0) ]) == (2.0, 3.0) assert range_overlap([ (0.0, 1.0), (0.0, 2.0), (-1.0, 1.0) ]) == (0.0, 1.0) - assert range_overlap([]) == None + assert range_overlap([ (0.0, 1.0), (5.0, 6.0) ]) == None + assert range_overlap([ (0.0, 1.0), (1.0, 2.0) ]) == None ``` We can now test `range_overlap` with a single function call: @@ -426,20 +425,20 @@ test_range_overlap() ```error --------------------------------------------------------------------------- AssertionError Traceback (most recent call last) - in () +Cell , line 1 ----> 1 test_range_overlap() - in test_range_overlap() +Cell , line 3, in test_range_overlap() 1 def test_range_overlap(): -----> 2 assert range_overlap([ (0.0, 1.0), (5.0, 6.0) ]) == None - 3 assert range_overlap([ (0.0, 1.0), (1.0, 2.0) ]) == None - 4 assert range_overlap([ (0.0, 1.0) ]) == (0.0, 1.0) - 5 assert range_overlap([ (2.0, 3.0), (2.0, 4.0) ]) == (2.0, 3.0) + 2 assert range_overlap([ (0.0, 1.0) ]) == (0.0, 1.0) +----> 3 assert range_overlap([ (2.0, 3.0), (2.0, 4.0) ]) == (2.0, 3.0) + 4 assert range_overlap([ (0.0, 1.0), (0.0, 2.0), (-1.0, 1.0) ]) == (0.0, 1.0) + 5 assert range_overlap([ (0.0, 1.0), (5.0, 6.0) ]) == None -AssertionError: +AssertionError: ``` -The first test that was supposed to produce `None` fails, +The test that was supposed to produce a simple range fails, so we know something is wrong with our function. We *don't* know whether the other tests passed or failed because Python halted the program as soon as it spotted the first error. @@ -451,6 +450,65 @@ regardless of the input values. This violates another important rule of programming: *always initialize from data*. +We can update our function so that it initializes `max_left` and `min_right` +from the input data itself. + +```python +def range_overlap(ranges): + """Return common overlap among a set of [left, right] ranges.""" + max_left, min_right = ranges[0] + for (left, right) in ranges: + max_left = max(max_left, left) + min_right = min(min_right, right) + return (max_left, min_right) +``` + +Re-running our tests we see that we now pass the second test, +but fail one of the non-overlapping cases. + +```python +test_range_overlap() +``` +```error +--------------------------------------------------------------------------- +AssertionError Traceback (most recent call last) +Cell , line 1 +----> 1 test_range_overlap() + +Cell , line 5, in test_range_overlap() + 3 assert range_overlap([ (2.0, 3.0), (2.0, 4.0) ]) == (2.0, 3.0) + 4 assert range_overlap([ (0.0, 1.0), (0.0, 2.0), (-1.0, 1.0) ]) == (0.0, 1.0) +----> 5 assert range_overlap([ (0.0, 1.0), (5.0, 6.0) ]) == None + 6 assert range_overlap([ (0.0, 1.0), (1.0, 2.0) ]) == None + +AssertionError: +``` +It turns out that for our current implementation of the `range_overlap` function +we never handled the case where there is no overlap. This shows up when +`max_left` is either equal to or greater than `min_right`, which isn't an actual range. +We can edit our function one last time to catch this behaviour and return `None`. + +```python +def range_overlap(ranges): + """Return common overlap among a set of [left, right] ranges.""" + max_left, min_right = ranges[0] + for (left, right) in ranges: + max_left = max(max_left, left) + min_right = min(min_right, right) + if max_left >= min_right: + overlap = None + else: + overlap = (max_left, min_right) + return overlap +``` + +```python +test_range_overlap() +``` + +We get no output from `test_range_overlap()`, +which means all the tests passed. + ::::::::::::::::::::::::::::::::::::::: challenge ## Pre- and Post-Conditions