Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 1.5k
[Sublist] Fix existing approaches & add new ones#4190
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
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
14781d713ce6e43c882ae5057d932ffdd82122e57333cc44eFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,21 +1,38 @@ | ||
| { | ||
| "introduction": { | ||
| "authors": ["safwansamsudeen"] | ||
| "authors": ["safwansamsudeen"], | ||
| "contributors": ["yrahcaz7"] | ||
| }, | ||
| "approaches": [ | ||
| { | ||
| "uuid": "db47397a-4551-49e8-8775-7e7aad79a38b", | ||
| "slug": "list-manipulation", | ||
| "title": "List manipulation", | ||
| "blurb": "Manipulate and check lists to solve the exercise", | ||
| "authors": ["safwansamsudeen"] | ||
| "authors": ["safwansamsudeen"], | ||
| "contributors": ["yrahcaz7"] | ||
| }, | ||
| { | ||
| "uuid": "61366160-c859-4d16-9085-171428209b8d", | ||
| "slug": "using-strings", | ||
| "title": "Using strings", | ||
| "blurb": "Convert the lists to string and use string manipulation to solve the exercise", | ||
| "authors": ["safwansamsudeen"] | ||
| "authors": ["safwansamsudeen"], | ||
| "contributors": ["yrahcaz7"] | ||
| }, | ||
| { | ||
| "uuid": "b2695c39-c1c7-47f0-bfcd-5e9703674bea", | ||
| "slug": "manual-loop", | ||
| "title": "Manual looping", | ||
| "blurb": "Manually track indexes while looping through the lists to solve the exercise", | ||
| "authors": ["yrahcaz7"] | ||
| }, | ||
| { | ||
| "uuid": "a1eeaf9b-a9b3-421e-bfad-44f7e1575450", | ||
| "slug": "sort-lists", | ||
| "title": "Sorting lists", | ||
| "blurb": "Sort the lists to determine the shorter and longer ones to solve the exercise", | ||
| "authors": ["yrahcaz7"] | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,24 +1,24 @@ | ||
| # Introduction | ||
| There are two broad ways to solve Sublist. | ||
| There are four broad ways to solve Sublist, though one of them ("using strings") is not recommended. | ||
| ## General guidance | ||
| To write the code, you need to branch out (probably with `if`) into the four different possible conditions, and return the appropriate name of the category. | ||
| ## Approach: list manipulation | ||
| To write the code, you need to branch out (probably with `if`) into the four different possible conditions, and return the appropriate category (`SUBLIST`, `SUPERLIST`, `EQUAL`, or `UNEQUAL`). | ||
| Note that you shouldn't return the category's value directly, as that would introduce [magic values][magic-values] into your code. | ||
| ## Approach: List manipulation | ||
| The direct approach would be to manipulate and check the given lists to solve this. | ||
| This solution uses a helper function, which simplifies things, but the approach can be implemented without it. | ||
| ```python | ||
| SUBLIST = 1 | ||
| SUPERLIST = 2 | ||
| EQUAL = 3 | ||
| UNEQUAL = 4 | ||
| def check_sub_sequences(list_one, list_two): | ||
| n1 = len(list_one) | ||
| n2 = len(list_two) | ||
| return any(list_two[i:i+n1] == list_one for i in range(n2 - n1 + 1)) | ||
| def check_sub_sequences(list_a, list_b): | ||
| len_a = len(list_a) | ||
| len_b = len(list_b) | ||
| return any(list_b[i : i + len_a] == list_a for i in range(len_b - len_a + 1)) | ||
| def sublist(list_one, list_two): | ||
| if list_one == list_two: | ||
| return EQUAL | ||
| @@ -31,29 +31,89 @@ def sublist(list_one, list_two): | ||
| Read more on the [detail of this approach][approach-list-manipulation]. | ||
| ## Approach: using strings | ||
| Another seemingly clever approach is to convert the lists to strings and then | ||
| use the `in` operator to check for sub-sequences. | ||
| **However, this does not work.** | ||
| ## Approach: Manual looping | ||
| This approach uses a helper function that manually loops through the lists to determine if the first list is a sublist of the second one. | ||
| This approach is the longest one by far, though it may be more comprehensible to some. | ||
| ```python | ||
| SUBLIST = 1 | ||
| SUPERLIST = 2 | ||
| EQUAL = 3 | ||
| UNEQUAL = 4 | ||
| def check_sub_sequences(list_a, list_b): | ||
| len_a, len_b = len(list_a), len(list_b) | ||
| index_a, index_b = 0, 0 | ||
| next_index_b = 1 | ||
| while index_a < len_a and index_b < len_b: | ||
| if list_a[index_a] == list_b[index_b]: | ||
| index_a += 1 | ||
| else: | ||
| index_a, index_b = 0, next_index_b | ||
| next_index_b += 1 | ||
| index_b += 1 | ||
| if index_a == len_a: | ||
| if len_a == len_b: | ||
| return EQUAL | ||
| return SUBLIST | ||
| return UNEQUAL | ||
| def sublist(list_one, list_two): | ||
Yrahcaz7 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| list_one_check = (str(list_one).strip("[]") + ",") | ||
| list_two_check = (str(list_two).strip("[]") + ",") | ||
| result = check_sub_sequences(list_one, list_two) | ||
| if result == UNEQUAL and check_sub_sequences(list_two, list_one) == SUBLIST: | ||
| result = SUPERLIST | ||
| return result | ||
| ``` | ||
| Learn more about the [details of this approach here][approach-manual-loop]. | ||
| ## Approach: Sorting lists | ||
| This approach uses the `sorted()` function to determine which list is shorter and which is longer. | ||
| Knowing this information, one can implement a simplified version of the list manipulation approach. | ||
| ```python | ||
| def sublist(list_one, list_two): | ||
| if list_one == list_two: | ||
| return EQUAL | ||
| if not list_one: | ||
| return SUBLIST | ||
| if not list_two: | ||
| return SUPERLIST | ||
| shorter, longer = sorted((list_one, list_two), key=len) | ||
| for index in range(len(longer) - len(shorter) + 1): | ||
| if longer[index : index + len(shorter)] == shorter: | ||
| return SUPERLIST if longer is list_one else SUBLIST | ||
| return UNEQUAL | ||
| ``` | ||
| Read more on the [detail of this approach][approach-sort-lists]. | ||
| ## Approach: Using strings | ||
| Another seemingly clever approach is to convert the lists to strings and then use the `in` operator to check for sub-sequences. | ||
| **However, this does not work.** | ||
| ```python | ||
| def sublist(list_one, list_two): | ||
| list_one_check = str(list_one).strip("[]") + "," | ||
| list_two_check = str(list_two).strip("[]") + "," | ||
| if list_one_check == list_two_check: | ||
| return EQUAL | ||
| elif list_one_check in list_two_check: | ||
| if list_one_check in list_two_check: | ||
| return SUBLIST | ||
| elif list_two_check in list_one_check: | ||
| if list_two_check in list_one_check: | ||
| return SUPERLIST | ||
| return UNEQUAL | ||
| ``` | ||
| To understand more about this approach and **why it fails**, [read here][approach-using-strings]. | ||
| [magic-values]: https://stackoverflow.com/questions/47882/what-is-a-magic-number-and-why-is-it-bad | ||
| [approach-list-manipulation]: https://exercism.org/tracks/python/exercises/sublist/approaches/list-manipulation | ||
| [approach-manual-loop]: https://exercism.org/tracks/python/exercises/sublist/approaches/manual-loop | ||
| [approach-sort-lists]: https://exercism.org/tracks/python/exercises/sublist/approaches/sort-lists | ||
| [approach-using-strings]: https://exercism.org/tracks/python/exercises/sublist/approaches/using-strings | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| # Manual looping | ||
| This approach uses a helper function that manually loops through the lists to determine if the first list is a sublist of the second one. | ||
| This approach is the longest one by far, though it may be more comprehensible to some. | ||
| ```python | ||
| SUBLIST = 1 | ||
| SUPERLIST = 2 | ||
| EQUAL = 3 | ||
| UNEQUAL = 4 | ||
| def check_sub_sequences(list_a, list_b): | ||
| len_a, len_b = len(list_a), len(list_b) | ||
| index_a, index_b = 0, 0 | ||
| next_index_b = 1 | ||
| while index_a < len_a and index_b < len_b: | ||
| if list_a[index_a] == list_b[index_b]: | ||
| index_a += 1 | ||
| else: | ||
| index_a, index_b = 0, next_index_b | ||
| next_index_b += 1 | ||
| index_b += 1 | ||
| if index_a == len_a: | ||
| if len_a == len_b: | ||
| return EQUAL | ||
| return SUBLIST | ||
| return UNEQUAL | ||
| def sublist(list_one, list_two): | ||
| result = check_sub_sequences(list_one, list_two) | ||
| if result == UNEQUAL and check_sub_sequences(list_two, list_one) == SUBLIST: | ||
| result = SUPERLIST | ||
| return result | ||
| ``` | ||
| ~~~~exercism/note | ||
| You might wonder why the lists in the helper function are named `list_a` and `list_b` instead of `list_one` and `list_two`. | ||
| This is because if the parameters have the same name, Pylint thinks the parameters are being passed in incorrectly when we call `check_sub_sequences(list_two, list_one)`. | ||
| (The exact warning generated is [`W1114 arguments-out-of-order`][w1114].) | ||
| [w1114]: https://pylint.readthedocs.io/en/stable/user_guide/messages/warning/arguments-out-of-order.html | ||
| ~~~~ | ||
| In this approach, the first thing `sublist()` does is call the helper function. | ||
| That function then loops through the lists, keeping track of an index for both lists so it can test all necessary combinations to determine if `list_one` is a sublist of `list_two`. | ||
| However, the helper function only determines if `list_one` is equal to or a sublist of `list_two`, not if `list_one` is a superlist of `list_two`. | ||
| That is why if the helper function returns `UNEQUAL`, `sublist()` needs to make sure that it isn't acutally a superlist. | ||
| `sublist()` does this by calling the helper function with its arguments reversed: `check_sub_sequences(list_two, list_one)`. | ||
| If the result is `SUBLIST`, that means `list_two` is a sublist of `list_one`, thus `list_one` must be a superlist of `list_two`. | ||
| Thus all possibilities are covered, and `sublist()` returns the result. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| while index_one < len(list_one) and index_two < len(list_two): | ||
| if list_one[index_one] == list_two[index_two]: | ||
| index_one += 1 | ||
| else: | ||
| index_one = 0 | ||
| index_two = next_index_two | ||
| next_index_two += 1 | ||
| index_two += 1 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| # Sorting lists | ||
| This approach uses the `sorted()` function to determine which list is shorter and which is longer. | ||
| Knowing this information, one can implement a simplified version of the [list manipulation approach][approach-list-manipulation]. | ||
| ```python | ||
| SUBLIST = 1 | ||
| SUPERLIST = 2 | ||
| EQUAL = 3 | ||
| UNEQUAL = 4 | ||
| def sublist(list_one, list_two): | ||
| if list_one == list_two: | ||
| return EQUAL | ||
| if not list_one: | ||
| return SUBLIST | ||
| if not list_two: | ||
| return SUPERLIST | ||
| shorter, longer = sorted((list_one, list_two), key=len) | ||
| for index in range(len(longer) - len(shorter) + 1): | ||
| if longer[index : index + len(shorter)] == shorter: | ||
| return SUPERLIST if longer is list_one else SUBLIST | ||
| return UNEQUAL | ||
Yrahcaz7 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| ``` | ||
| Here, the case of the lists being equal is checked first. | ||
| Then the special cases of empty lists are handled, returning `SUBLIST` or `SUPERLIST` as necessary. | ||
| Once those simple cases are out of the way, the `sorted()` function is used with the keyword argument `key` set to the `len()` function. | ||
| This makes `sorted()` sort the items according to their length. | ||
| Once `sorted()` does its work, we use multiple assignment to unpack the results into the `shorter` and `longer` variables. | ||
| Then, for each slice of length `len(shorter)` in `longer`, we test if that slice is equal to `shorter`. | ||
| If we find such a slice, that means `shorter` is a sublist of `longer`. | ||
| Then we use a [conditional expression][conditional-expression] along with the `is` operator to return `SUBLIST` or `SUPERLIST` depending on which of the original lists is `longer`. | ||
| If we do not find such a slice, we can eliminate `SUBLIST` and `SUPERLIST` from the possible categories, thus the two lists must be `UNEQUAL`. | ||
| [approach-list-manipulation]: https://exercism.org/tracks/python/exercises/sublist/approaches/list-manipulation | ||
| [conditional-expression]: https://docs.python.org/3/reference/expressions.html#conditional-expressions | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| def sublist(list_one, list_two): | ||
| ... | ||
| shorter, longer = sorted((list_one, list_two), key=len) | ||
| for index in range(len(longer) - len(shorter) + 1): | ||
| if longer[index : index + len(shorter)] == shorter: | ||
| return SUPERLIST if longer is list_one else SUBLIST | ||
| return UNEQUAL |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.