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
21 changes: 21 additions & 0 deletions exercises/practice/sublist/.approaches/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"introduction": {
"authors": ["safwansamsudeen"]
},
"approaches": [
{
"uuid": "db47397a-4551-49e8-8775-7e7aad79a38b",
"slug": "list-manipulation",
"title": "List manipulation",
"blurb": "Manipulate and check lists to solve the exercise",
"authors": ["safwansamsudeen"]
},
{
"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"]
}
]
}
60 changes: 60 additions & 0 deletions exercises/practice/sublist/.approaches/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Introduction
There are two broad ways to solve Sublist.

## 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
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 sublist(list_one, list_two):
if list_one == list_two:
return EQUAL
if check_sub_sequences(list_one, list_two):
return SUBLIST
if check_sub_sequences(list_two, list_one):
return SUPERLIST
return UNEQUAL
```

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.**
```python
SUBLIST = 1
SUPERLIST = 2
EQUAL = 3
UNEQUAL = 4

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:
return SUBLIST
elif 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].

[approach-list-manipulation]: https://exercism.org/tracks/python/exercises/sublist/approaches/list-manipulation
[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,38 @@
# 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 sublist(list_one, list_two):
if list_one == list_two:
return EQUAL
if check_sub_sequences(list_one, list_two):
return SUBLIST
if check_sub_sequences(list_two, list_one):
return SUPERLIST
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].

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`.

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.

If none of the above conditions are true, we conclude that the two lists are unequal.

[magic values]: https://stackoverflow.com/questions/47882/what-is-a-magic-number-and-why-is-it-bad
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
def sublist(list_one, list_two):
if list_one == list_two:
return EQUAL
if check_sub_sequences(list_one, list_two):
return SUBLIST
if check_sub_sequences(list_two, list_one):
return SUPERLIST
return UNEQUAL
47 changes: 47 additions & 0 deletions exercises/practice/sublist/.approaches/using-strings/content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Using strings
~~~~exercism/caution
**This approach does not work, and this document exists to explain that.**
Please do not use it in your code.
~~~~

Another seemingly clever solution is to convert the lists to strings and then
use the `in` operator to check for sub-sequences.
Note that this approach, even if it worked, is not as performant as the
previous one.
```python
SUBLIST = 1
SUPERLIST = 2
EQUAL = 3
UNEQUAL = 4

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:
return SUBLIST
elif list_two_check in list_one_check:
return SUPERLIST
return UNEQUAL
```
Let's parse the code to see what it does.
In this approach, we convert the lists to strings, so `[1, 2, 3]` becomes `"[1, 2, 3]"`, remove the brackets `"1, 2, 3"`, and add a comma `"1, 2, 3,"`.
We check equality and then use the `in` operator to check for `SUBLIST` or `SUPERLIST`, and finally return `UNEQUAL`.

We add a comma because, say, we call `sublist` with `[1, 2]` and `[1, 22]`. `"1, 2" in "1, 22"` evaluates to `True`, so
the **function would wrongly mark it as `SUBLIST`**.

This test can be overridden by changing the code like this:
```python
list_one_check = str(list_one).strip("[]") + ','
list_two_check = str(list_two).strip("[]") + ','
```
Yet, the test case (which doesn't exist in the Exercism test suite) `["1", "2"]` and `["5", "'1', '2',", "7"]` would
fail.

Students can add any arbitrary string into the representation to try to "defeat" this test - `list_one_check = str
(list_one) + TOKEN`. The test suite currently test `TOKEN = ''`, but not others.

[gen-exp]: https://www.programiz.com/python-programming/generator
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Failing approach
def sublist(list_one, list_two):
list_one_check = str(list_one).strip("[]")
...
elif list_one_check in list_two_check:
return SUBLIST
...
return UNEQUAL