diff --git a/exercises/practice/atbash-cipher/.approaches/config.json b/exercises/practice/atbash-cipher/.approaches/config.json index ed1edeb5065..dc57da36ca2 100644 --- a/exercises/practice/atbash-cipher/.approaches/config.json +++ b/exercises/practice/atbash-cipher/.approaches/config.json @@ -1,6 +1,7 @@ { "introduction": { - "authors": ["safwansamsudeen"] + "authors": ["safwansamsudeen"], + "contributors": ["yrahcaz7"] }, "approaches": [ { @@ -8,14 +9,16 @@ "slug": "mono-function", "title": "Mono-function", "blurb": "Use one function for both tasks", - "authors": ["safwansamsudeen"] + "authors": ["safwansamsudeen"], + "contributors": ["yrahcaz7"] }, { "uuid": "9a7a17e0-4ad6-4d97-a8b9-c74d47f3e000", "slug": "separate-functions", - "title": "Separate Functions", + "title": "Separate functions", "blurb": "Use separate functions, and perhaps helper ones", - "authors": ["safwansamsudeen"] + "authors": ["safwansamsudeen"], + "contributors": ["yrahcaz7"] } ] } diff --git a/exercises/practice/atbash-cipher/.approaches/introduction.md b/exercises/practice/atbash-cipher/.approaches/introduction.md index 6c7180eff9a..ce9786409a8 100644 --- a/exercises/practice/atbash-cipher/.approaches/introduction.md +++ b/exercises/practice/atbash-cipher/.approaches/introduction.md @@ -1,44 +1,54 @@ # Introduction + Atbash cipher in Python can be solved in many ways. ## General guidance -The first thing is to have a "key" mapping - possibly in a `dict` or `str.maketrans`, otherwise the value would have to be calculated on the fly. -Then, you have to "clean" up the string to be encoded by removing numbers/whitespace. + +The first thing is to have a "key" mapping — possibly in a `dict` or `str.maketrans()`, otherwise the value would have to be calculated on the fly. +Next, you have to "clean" up the string to be encoded by removing punctuation/whitespace. Finally, you break it up into chunks of five before returning it. -For decoding, it's similar - clean up (which automatically joins the chunks) and translate using the _same_ key - the realization that the same key can be used is crucial in solving this in an idiomatic manner. +For decoding, the process is similar — clean up (_which automatically joins the chunks_) and translate using the **_same_** key — realizing that the same key can be used is crucial in solving this in an idiomatic manner. + +## Approach: Separate functions + +We use `str.maketrans()` to create the encoding. +In `encode()`, we use a [generator expression][generator-expression] in `str.join()`. -## Approach: separate functions -We use `str.maketrans` to create the encoding. -In `encode`, we use a [generator expression][generator-expression] in `str.join`. ```python from string import ascii_lowercase + ENCODING = str.maketrans(ascii_lowercase, ascii_lowercase[::-1]) -def encode(text: str): +def encode(text): res = "".join(chr for chr in text.lower() if chr.isalnum()).translate(ENCODING) return " ".join(res[index:index+5] for index in range(0, len(res), 5)) -def decode(text: str): - return "".join(chr.lower() for chr in text if chr.isalnum()).translate(ENCODING) +def decode(text): + return "".join(chr.lower() for chr in text if not chr.isspace()).translate(ENCODING) ``` + Read more on this [approach here][approach-separate-functions]. -## Approach: mono-function -Notice that there the majority of the code is repetitive? -A fun way to solve this would be to keep it all inside the `encode` function, and merely chunk it if `decode` is False: -For variation, this approach shows a different way to translate the text. +## Approach: Mono-function + +Notice that the majority of the code is repetitive? +A fun way to solve this would be to keep it all inside the `encode()` function, and merely chunk it if `decode` is `False`: +For variation, this approach also shows a different way to translate the text. + ```python from string import ascii_lowercase as asc_low + ENCODING = {chr: asc_low[id] for id, chr in enumerate(asc_low[::-1])} -def encode(text: str, decode: bool = False): - res = "".join(ENCODING.get(chr, chr) for chr in text.lower() if chr.isalnum()) - return res if decode else " ".join(res[index:index+5] for index in range(0, len(res), 5)) +def encode(text, decode = False): + line = "".join(ENCODING.get(chr, chr) for chr in text.lower() if chr.isalnum()) + return line if decode else " ".join(line[index:index+5] for index in range(0, len(line), 5)) -def decode(text: str): +def decode(text): return encode(text, True) ``` + For more detail, [read here][approach-mono-function]. [approach-separate-functions]: https://exercism.org/tracks/python/exercises/atbash-cipher/approaches/separate-functions diff --git a/exercises/practice/atbash-cipher/.approaches/mono-function/content.md b/exercises/practice/atbash-cipher/.approaches/mono-function/content.md index 879664ce207..0c0da8e42a1 100644 --- a/exercises/practice/atbash-cipher/.approaches/mono-function/content.md +++ b/exercises/practice/atbash-cipher/.approaches/mono-function/content.md @@ -1,46 +1,54 @@ -## Approach: Mono-function -Notice that there the majority of the code is repetitive? -A fun way to solve this would be to keep it all inside the `encode` function, and merely chunk it if `decode` is False: -For variation, this approach shows a different way to translate the text. +# Approach: Mono-function + +Notice that the majority of the code is repetitive? +A fun way to solve this would be to keep it all inside the `encode()` function, and merely chunk it if `decode` is `False`: +For variation, this approach also shows a different way to translate the text. + ```python from string import ascii_lowercase as asc_low + ENCODING = {chr: asc_low[id] for id, chr in enumerate(asc_low[::-1])} -def encode(text: str, decode: bool = False): - res = "".join(ENCODING.get(chr, chr) for chr in text.lower() if chr.isalnum()) - return res if decode else " ".join(res[index:index+5] for index in range(0, len(res), 5)) +def encode(text, decode = False): + line = "".join(ENCODING.get(chr, chr) for chr in text.lower() if chr.isalnum()) + return line if decode else " ".join(line[index:index+5] for index in range(0, len(line), 5)) -def decode(text: str): +def decode(text): return encode(text, True) ``` -To explain the translation: we use a `dict` comprehension in which we reverse the ASCII lowercase digits, and enumerate through them - that is, `z` is 0, `y` is 1, and so on. -We access the character at that index and set it to the value of `c` - so `z` translates to `a`. -In the calculation of the result, we try to obtain the value of the character using `dict.get`, which accepts a default parameter. -In this case, the character itself is the default - that is, numbers won't be found in the translation key, and thus should remain as numbers. +Here, we use a dictionary comprehension in which we reverse the order of the ASCII lowercase digits and enumerate through them — that is, `z` is at index 0, `y` is at index 1, and so on. +For each code point, we set the value of `chr` in the resulting dictionary to the code point at the respective index — so `z` translates to `a`. + +In the calculation of the result, we try to obtain the value of the code point using `dict.get()`, which accepts a default parameter. +In this case, the code point itself is the default — that is, numbers won't be found in the translation key, and thus should remain as numbers. + +We use a [conditional expression (also known as a ternary operator)][conditional-expression] to check if we actually mean to decode the function, in which case we return the result as is. +If not, we "chunk" the result by joining every five code points with a space. -We use a [ternary operator][ternary-operator] to check if we actually mean to decode the function, in which case we return the result as is. -If not, we chunk the result by joining every five characters with a space. +Another possible way to solve this would be to use a function that returns another function (_a higher-order function or [closure][closure]_) that encodes or decodes based on the outer function's parameter: -Another possible way to solve this would be to use a function that returns a function that encodes or decodes based on the parameters: ```python -from string import ascii_lowercase as alc +from string import ascii_lowercase as asc_low -lowercase = {chr: alc[id] for id, chr in enumerate(alc[::-1])} +ENCODING = {chr: asc_low[id] for id, chr in enumerate(asc_low[::-1])} -def code(decode=False): +def code(decode = False): def func(text): - line = "".join(lowercase.get(chr, chr) for chr in text.lower() if chr.isalnum()) + line = "".join(ENCODING.get(chr, chr) for chr in text.lower() if chr.isalnum()) return line if decode else " ".join(line[index:index+5] for index in range(0, len(line), 5)) return func - encode = code() decode = code(True) ``` -The logic is the same - we've instead used one function that generates two _other_ functions based on the boolean value of its parameter. -`encode` is set to the function that's returned, and performs encoding. -`decode` is set a function that _decodes_. -[ternary-operator]: https://www.tutorialspoint.com/ternary-operator-in-python -[decorator]: https://realpython.com/primer-on-python-decorators/ \ No newline at end of file +The logic is the same — the only change is that now we use use one function that generates two _other_ functions based on the boolean value of its parameter. + +Here, we first call `code()` with no argument and set `encode` to the function that's returned, which performs encoding. +Then we call `code(True)` to get the decoding version of the function and set `decode` to that function. + +After that, we can call `encode()` and `decode()` as normal, and both functions successfully perform their indended task. + +[closure]: https://realpython.com/python-closure/ +[conditional-expression]: https://docs.python.org/3/reference/expressions.html#conditional-expressions diff --git a/exercises/practice/atbash-cipher/.approaches/mono-function/snippet.txt b/exercises/practice/atbash-cipher/.approaches/mono-function/snippet.txt index 84e8b793008..24ba495094a 100644 --- a/exercises/practice/atbash-cipher/.approaches/mono-function/snippet.txt +++ b/exercises/practice/atbash-cipher/.approaches/mono-function/snippet.txt @@ -1,8 +1,8 @@ from string import ascii_lowercase as asc_low ENCODING = {chr: asc_low[id] for id, chr in enumerate(asc_low[::-1])} -def encode(text: str, decode: bool = False): - res = "".join(ENCODING.get(chr, chr) for chr in text.lower() if chr.isalnum()) - return res if decode else " ".join(res[index:index+5] for index in range(0, len(res), 5)) -def decode(text: str): +def encode(text, decode = False): + line = "".join(ENCODING.get(chr, chr) for chr in text.lower() if chr.isalnum()) + return line if decode else " ".join(line[index:index+5] for index in range(0, len(line), 5)) +def decode(text): return encode(text, True) \ No newline at end of file diff --git a/exercises/practice/atbash-cipher/.approaches/separate-functions/content.md b/exercises/practice/atbash-cipher/.approaches/separate-functions/content.md index 60e02a22055..1890625819c 100644 --- a/exercises/practice/atbash-cipher/.approaches/separate-functions/content.md +++ b/exercises/practice/atbash-cipher/.approaches/separate-functions/content.md @@ -1,45 +1,57 @@ -## Approach: Separate Functions -We use `str.maketrans` to create the encoding. -`.maketrans`/`.translate` is extremely fast compared to other methods of translation. -If you're interested, [read more][str-maketrans] about it. +# Approach: Separate functions + +We use `str.maketrans()` to create the encoding. +`str.maketrans()`/`str.translate()` is extremely fast compared to other methods of translation. +If you're interested, you can [read more about it here][str-maketrans]. + +In `encode()`, we use a [generator expression][generator-expression] in `str.join()`, which is more efficient — and neater — than a list comprehension. -In `encode`, we use a [generator expression][generator-expression] in `str.join`, which is more efficient - and neater - than a list comprehension. ```python from string import ascii_lowercase + ENCODING = str.maketrans(ascii_lowercase, ascii_lowercase[::-1]) -def encode(text: str): +def encode(text): res = "".join(chr for chr in text.lower() if chr.isalnum()).translate(ENCODING) return " ".join(res[index:index+5] for index in range(0, len(res), 5)) -def decode(text: str): - return "".join(chr.lower() for chr in text if chr.isalnum()).translate(ENCODING) +def decode(text): + return "".join(chr.lower() for chr in text if not chr.isspace()).translate(ENCODING) ``` -In `encode`, we first join together every character if the character is alphanumeric - as we use `text.lower()`, the characters are all lowercase as needed. -Then, we translate it and return a version joining every five characters with a space in between. -`decode` does the exact same thing, except it doesn't return a chunked output. -Instead of cleaning the input by checking that it's alphanumeric, we check that it's not a whitespace character. +In `encode()`, we first join together every code point that is an alphanumeric character — as we use `text.lower()`, the characters are all lowercase as needed. +Then, we translate it and return a version joining every five code points with a space in between. + +`decode()` does the exact same thing, except it doesn't return a chunked output and it cleans the input differently. +To clean the input, `decode()` only removes code points that are whitespace characters instead of all non-alphanumeric characters. It might be cleaner to use helper functions: + ```python from string import ascii_lowercase + ENCODING = str.maketrans(ascii_lowercase, ascii_lowercase[::-1]) + + def clean(text): return "".join([chr.lower() for chr in text if chr.isalnum()]) + def chunk(text): return " ".join(text[index:index+5] for index in range(0, len(text), 5)) + def encode(text): return chunk(clean(text).translate(ENCODING)) def decode(text): return clean(text).translate(ENCODING) ``` -Note that checking that `chr` _is_ alphanumeric achieves the same result as checking that it's _not_ whitespace, although it's not as explicit. + +Note that for `decode()`, checking that `chr` _is_ alphanumeric achieves the same result as checking that it _is not_ whitespace, although it's not as explicit. As this is a helper function, this is acceptable enough. -You can also make `chunk` recursive: +You can also make `chunk()` recursive, but this is not recommended: + ```python def chunk(text): if len(text) <= 5: @@ -48,4 +60,4 @@ def chunk(text): ``` [generator-expression]: https://www.programiz.com/python-programming/generator -[str-maketrans]: https://www.programiz.com/python-programming/methods/string/maketrans \ No newline at end of file +[str-maketrans]: https://www.programiz.com/python-programming/methods/string/maketrans diff --git a/exercises/practice/atbash-cipher/.approaches/separate-functions/snippet.txt b/exercises/practice/atbash-cipher/.approaches/separate-functions/snippet.txt index fbfe0b75fa5..f57a8dda721 100644 --- a/exercises/practice/atbash-cipher/.approaches/separate-functions/snippet.txt +++ b/exercises/practice/atbash-cipher/.approaches/separate-functions/snippet.txt @@ -1,8 +1,8 @@ from string import ascii_lowercase ENCODING = str.maketrans(ascii_lowercase, ascii_lowercase[::-1]) -def encode(text: str): +def encode(text): res = "".join(chr for chr in text.lower() if chr.isalnum()).translate(ENCODING) return " ".join(res[index:index+5] for index in range(0, len(res), 5)) -def decode(text: str): +def decode(text): return "".join(chr.lower() for chr in text if not chr.isspace()).translate(ENCODING) \ No newline at end of file diff --git a/exercises/practice/rna-transcription/.approaches/config.json b/exercises/practice/rna-transcription/.approaches/config.json index 9ab41145480..7ec0363a3f6 100644 --- a/exercises/practice/rna-transcription/.approaches/config.json +++ b/exercises/practice/rna-transcription/.approaches/config.json @@ -9,14 +9,16 @@ "slug": "translate-maketrans", "title": "translate maketrans", "blurb": "Use translate with maketrans to return the value.", - "authors": ["bobahop"] + "authors": ["bobahop"], + "contributors": ["yrahcaz7"] }, { "uuid": "fbc6be87-dec4-4c4b-84cf-fcc1ed2d6d41", "slug": "dictionary-join", "title": "dictionary join", "blurb": "Use a dictionary look-up with join to return the value.", - "authors": ["bobahop"] + "authors": ["bobahop"], + "contributors": ["yrahcaz7"] } ] } diff --git a/exercises/practice/rna-transcription/.approaches/dictionary-join/content.md b/exercises/practice/rna-transcription/.approaches/dictionary-join/content.md index fcf0c58953a..ead5254ad9c 100644 --- a/exercises/practice/rna-transcription/.approaches/dictionary-join/content.md +++ b/exercises/practice/rna-transcription/.approaches/dictionary-join/content.md @@ -6,7 +6,6 @@ LOOKUP = {'G': 'C', 'C': 'G', 'T': 'A', 'A': 'U'} def to_rna(dna_strand): return ''.join(LOOKUP[nucleotide] for nucleotide in dna_strand) - ``` This approach starts by defining a [dictionary][dictionaries] to map the DNA values to RNA values. @@ -18,7 +17,7 @@ It indicates that the value is not intended to be changed. In the `to_rna()` function, the [`join()`][join] method is called on an empty string, and is passed the list created from a [generator expression][generator-expression]. -The generator expression iterates each character in the input, +The generator expression iterates over each code point in the input, looks up the DNA character in the look-up dictionary, and outputs its matching RNA character as an element in the list. The `join()` method collects the RNA characters back into a string. diff --git a/exercises/practice/rna-transcription/.approaches/introduction.md b/exercises/practice/rna-transcription/.approaches/introduction.md index 54b4c1f7d30..032532946e2 100644 --- a/exercises/practice/rna-transcription/.approaches/introduction.md +++ b/exercises/practice/rna-transcription/.approaches/introduction.md @@ -18,7 +18,6 @@ LOOKUP = str.maketrans('GCTA', 'CGAU') def to_rna(dna_strand): return dna_strand.translate(LOOKUP) - ``` For more information, check the [`translate()` with `maketrans()` approach][approach-translate-maketrans]. @@ -31,7 +30,6 @@ LOOKUP = {'G': 'C', 'C': 'G', 'T': 'A', 'A': 'U'} def to_rna(dna_strand): return ''.join(LOOKUP[nucleotide] for nucleotide in dna_strand) - ``` For more information, check the [dictionary look-up with `join()` approach][approach-dictionary-join]. diff --git a/exercises/practice/rna-transcription/.approaches/translate-maketrans/content.md b/exercises/practice/rna-transcription/.approaches/translate-maketrans/content.md index 9373cf12b26..374cadd65e9 100644 --- a/exercises/practice/rna-transcription/.approaches/translate-maketrans/content.md +++ b/exercises/practice/rna-transcription/.approaches/translate-maketrans/content.md @@ -6,7 +6,6 @@ LOOKUP = str.maketrans('GCTA', 'CGAU') def to_rna(dna_strand): return dna_strand.translate(LOOKUP) - ``` This approach starts by defining a [dictionary][dictionaries] (also called a translation table in this context) by calling the [`maketrans()`][maketrans] method. @@ -18,7 +17,7 @@ It indicates that the value is not intended to be changed. The translation table that is created uses the [Unicode][Unicode] _code points_ (sometimes called the ordinal values) for each letter in the two strings. As Unicode was designed to be backwards compatible with [ASCII][ASCII] and because the exercise uses Latin letters, the code points in the translation table can be interpreted as ASCII. However, the functions can deal with any Unicode character. -You can learn more by reading about [strings and their representation in the Exercism Python syllabus][concept-string]. +You can learn more by reading about [strings and their representation in the Exercism Python syllabus][concept-strings]. The Unicode value for "G" in the first string is the key for the Unicode value of "C" in the second string, and so on.