Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
23 changes: 20 additions & 3 deletions exercises/practice/sublist/.approaches/config.json
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"]
}
]
}
110 changes: 85 additions & 25 deletions exercises/practice/sublist/.approaches/introduction.md
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.
Comment thread
Yrahcaz7 marked this conversation as resolved.

## 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
Expand All @@ -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):
Comment thread
Yrahcaz7 marked this conversation as resolved.
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
34 changes: 20 additions & 14 deletions exercises/practice/sublist/.approaches/list-manipulation/content.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# 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.

Expand All @@ -8,11 +9,11 @@ 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
Expand All @@ -23,16 +24,21 @@ def sublist(list_one, list_two):
return UNEQUAL
```

We first check for equality using the `==` operator, if so, then we return `EQUAL`.
A common way to do this differently would be to return `1` directly, but this is better practice as we [remove magic values][magic values].
~~~~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
~~~~

After that we call `check_sub_sequences` passing in `list_one` and `list_two`.
In the helper function, we check if `any` of the possible sub-sequences in `list_two` of length `n1` (the length of the first list) are equal to the first list.
If so, then we conclude that `list_one` is a `SUBLIST` of `list_two`.
In this approach, we first check for equality using the `==` operator, and if the lists are equal, then we return `EQUAL`.
After that, we call `check_sub_sequences()`, passing in `list_one` and `list_two` for the parameters `list_a` and `list_b`.

To find whether `list_one` is a `SUPERLIST` of `list_two`, we just reverse this process - pass in the lists in the opposite order.
Thus, we check if `any` of the possible sub-sequences in `list_one` of length `n2` (the length of the second list) are equal to the second list.
In the helper function, we check if `any` of the possible sub-sequences in `list_b` of length `len_a` (the length of `list_a`) are equal to `list_a`.
If so, then we conclude that `list_a` is a `SUBLIST` of `list_b`.

If none of the above conditions are true, we conclude that the two lists are unequal.
To find whether `list_one` is a `SUPERLIST` of `list_two`, we just reverse this process — pass in the lists in the opposite order.
Thus, we check if `any` of the possible sub-sequences in `list_one` of the length of `list_two` are equal to `list_two`.

[magic values]: https://stackoverflow.com/questions/47882/what-is-a-magic-number-and-why-is-it-bad
If none of the above conditions are true, we conclude that the two lists are `UNEQUAL`.
56 changes: 56 additions & 0 deletions exercises/practice/sublist/.approaches/manual-loop/content.md
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
44 changes: 44 additions & 0 deletions exercises/practice/sublist/.approaches/sort-lists/content.md
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
Comment thread
Yrahcaz7 marked this conversation as resolved.
```

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
8 changes: 8 additions & 0 deletions exercises/practice/sublist/.approaches/sort-lists/snippet.txt
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
Loading
Loading