Skip to content

Commit 190913a

Browse files
committed
Initial version taken from exercism/python
0 parents  commit 190913a

File tree

504 files changed

+23195
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

504 files changed

+23195
-0
lines changed

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2021 Exercism
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

accumulate/accumulate.py

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
def accumulate(collection, operation):
2+
pass

accumulate/accumulate_test.py

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import unittest
2+
3+
from accumulate import accumulate
4+
5+
6+
class AccumulateTest(unittest.TestCase):
7+
def test_empty_sequence(self):
8+
self.assertEqual(accumulate([], lambda x: x / 2), [])
9+
10+
def test_pow(self):
11+
self.assertEqual(
12+
accumulate([1, 2, 3, 4, 5], lambda x: x * x), [1, 4, 9, 16, 25])
13+
14+
def test_divmod(self):
15+
self.assertEqual(
16+
accumulate([10, 17, 23], lambda x: divmod(x, 7)),
17+
[(1, 3), (2, 3), (3, 2)])
18+
19+
def test_composition(self):
20+
inp = [10, 17, 23]
21+
self.assertEqual(
22+
accumulate(
23+
accumulate(inp, lambda x: divmod(x, 7)),
24+
lambda x: 7 * x[0] + x[1]), inp)
25+
26+
def test_capitalize(self):
27+
self.assertEqual(
28+
accumulate(['hello', 'world'], str.upper), ['HELLO', 'WORLD'])
29+
30+
def test_recursive(self):
31+
inp = ['a', 'b', 'c']
32+
out = [['a1', 'a2', 'a3'], ['b1', 'b2', 'b3'], ['c1', 'c2', 'c3']]
33+
self.assertEqual(
34+
accumulate(
35+
inp, lambda x: accumulate(list('123'), lambda y: x + y)), out)
36+
37+
38+
if __name__ == '__main__':
39+
unittest.main()

accumulate/instructions.md

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Instructions
2+
3+
Implement the `accumulate` operation, which, given a collection and an operation to perform on each element of the collection, returns a new collection containing the result of applying that operation to each element of the input collection.
4+
5+
Given the collection of numbers:
6+
7+
- 1, 2, 3, 4, 5
8+
9+
And the operation:
10+
11+
- square a number (`x => x * x`)
12+
13+
Your code should be able to produce the collection of squares:
14+
15+
- 1, 4, 9, 16, 25
16+
17+
Check out the test suite to see the expected function signature.
18+
19+
## Restrictions
20+
21+
Keep your hands off that collect/map/fmap/whatchamacallit functionality provided by your standard library!
22+
Solve this one yourself using other basic tools instead.

acronym/acronym.py

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
def abbreviate(words):
2+
pass

acronym/acronym_test.py

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# These tests are auto-generated with test data from:
2+
# https://github.com/exercism/problem-specifications/tree/main/exercises/acronym/canonical-data.json
3+
# File last updated on 2023-07-20
4+
5+
import unittest
6+
7+
from acronym import (
8+
abbreviate,
9+
)
10+
11+
12+
class AcronymTest(unittest.TestCase):
13+
def test_basic(self):
14+
self.assertEqual(abbreviate("Portable Network Graphics"), "PNG")
15+
16+
def test_lowercase_words(self):
17+
self.assertEqual(abbreviate("Ruby on Rails"), "ROR")
18+
19+
def test_punctuation(self):
20+
self.assertEqual(abbreviate("First In, First Out"), "FIFO")
21+
22+
def test_all_caps_word(self):
23+
self.assertEqual(abbreviate("GNU Image Manipulation Program"), "GIMP")
24+
25+
def test_punctuation_without_whitespace(self):
26+
self.assertEqual(abbreviate("Complementary metal-oxide semiconductor"), "CMOS")
27+
28+
def test_very_long_abbreviation(self):
29+
self.assertEqual(
30+
abbreviate(
31+
"Rolling On The Floor Laughing So Hard That My Dogs Came Over And Licked Me"
32+
),
33+
"ROTFLSHTMDCOALM",
34+
)
35+
36+
def test_consecutive_delimiters(self):
37+
self.assertEqual(abbreviate("Something - I made up from thin air"), "SIMUFTA")
38+
39+
def test_apostrophes(self):
40+
self.assertEqual(abbreviate("Halley's Comet"), "HC")
41+
42+
def test_underscore_emphasis(self):
43+
self.assertEqual(abbreviate("The Road _Not_ Taken"), "TRNT")

acronym/instructions.md

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Instructions
2+
3+
Convert a phrase to its acronym.
4+
5+
Techies love their TLA (Three Letter Acronyms)!
6+
7+
Help generate some jargon by writing a program that converts a long name like Portable Network Graphics to its acronym (PNG).
8+
9+
Punctuation is handled as follows: hyphens are word separators (like whitespace); all other punctuation can be removed from the input.
10+
11+
For example:
12+
13+
| Input | Output |
14+
| ------------------------- | ------ |
15+
| As Soon As Possible | ASAP |
16+
| Liquid-crystal display | LCD |
17+
| Thank George It's Friday! | TGIF |

acronym/introduction.md

+160
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
# Introduction
2+
3+
There are multiple Pythonic ways to solve the Acronym exercise.
4+
Among them are:
5+
6+
- Using `str.replace()` to scrub the input, and:
7+
- joining with a `for loop` with string concatenation via the `+` operator.
8+
- joining via `str.join()`, passing a `list-comprehension` or `generator-expression`.
9+
- joining via `str.join()`, passing `map()`.
10+
- joining via `functools.reduce()`.
11+
12+
- Using `re.findall()`/`re.finditer()` to scrub the input, and:
13+
- joining via `str.join()`, passing a `generator-expression`.
14+
15+
- Using `re.sub()` for both cleaning and joining (_using "only" regex for almost everything_)`
16+
17+
18+
## General Guidance
19+
20+
The goal of the Acronym exercise is to collect the first letters of each word in the input phrase and return them as a single capitalized string (_the acronym_).
21+
The challenge is to efficiently identify and capitalize the first letters while removing or ignoring non-letter characters such as `'`,`-`,`_`, and white space.
22+
23+
24+
There are two idiomatic strategies for non-letter character removal:
25+
- Python's built-in [`str.replace()`][str-replace].
26+
- The [`re`][re] module, (_regular expressions_).
27+
28+
For all but the most complex scenarios, using `str.replace()` is generally more efficient than using a regular expression.
29+
30+
31+
Forming the final acronym is most easily done with a direct or indirect `loop`, after splitting the input into a word list via [`str.split()`][str-split].
32+
The majority of these approaches demonstrate alternatives to the "classic" looping structure using various other iteration techniques.
33+
Some `regex` methods can avoid looping altogether, although they can become very non-performant due to excessive backtracking.
34+
35+
Strings are _immutable_, so any method to produce an acronym will be creating and returning a new `str`.
36+
37+
38+
## Approach: scrub with `replace()` and join via `for` loop
39+
40+
```python
41+
def abbreviate(to_abbreviate):
42+
phrase = to_abbreviate.replace('-', ' ').replace('_', ' ').upper().split()
43+
acronym = ''
44+
45+
for word in phrase:
46+
acronym += word[0]
47+
48+
return acronym
49+
```
50+
51+
For more information, take a look at the [loop approach][approach-loop].
52+
53+
54+
## Approach: scrub with `replace()` and join via `list comprehension` or `Generator expression`
55+
56+
57+
```python
58+
def abbreviate(to_abbreviate):
59+
phrase = to_abbreviate.replace('-', ' ').replace('_', ' ').upper().split()
60+
61+
return ''.join([word[0] for word in phrase])
62+
63+
###OR###
64+
65+
def abbreviate(to_abbreviate):
66+
phrase = to_abbreviate.replace('-', ' ').replace('_', ' ').upper().split()
67+
68+
# note the parenthesis instead of square brackets.
69+
return ''.join((word[0] for word in phrase))
70+
```
71+
72+
For more information, check out the [list-comprehension][approach-list-comprehension] approach or the [generator-expression][approach-generator-expression] approach.
73+
74+
75+
## Approach: scrub with `replace()` and join via `map()`
76+
77+
```python
78+
def abbreviate(to_abbreviate):
79+
phrase = to_abbreviate.replace("_", " ").replace("-", " ").upper().split()
80+
81+
return ''.join(map(lambda word: word[0], phrase))
82+
```
83+
84+
For more information, read the [map][approach-map-function] approach.
85+
86+
87+
## Approach: scrub with `replace()` and join via `functools.reduce()`
88+
89+
```python
90+
from functools import reduce
91+
92+
93+
def abbreviate(to_abbreviate):
94+
phrase = to_abbreviate.replace("_", " ").replace("-", " ").upper().split()
95+
96+
return reduce(lambda start, word: start + word[0], phrase, "")
97+
```
98+
99+
For more information, take a look at the [functools.reduce()][approach-functools-reduce] approach.
100+
101+
102+
## Approach: filter with `re.findall()` and join via `str.join()`
103+
104+
```python
105+
import re
106+
107+
108+
def abbreviate(phrase):
109+
removed = re.findall(r"[a-zA-Z']+", phrase)
110+
111+
return ''.join(word[0] for word in removed).upper()
112+
```
113+
114+
For more information, take a look at the [regex-join][approach-regex-join] approach.
115+
116+
117+
## Approach: use `re.sub()`
118+
119+
```python
120+
import re
121+
122+
123+
def abbreviate_regex_sub(to_abbreviate):
124+
pattern = re.compile(r"(?<!_)\B[\w']+|[ ,\-_]")
125+
126+
return re.sub(pattern, "", to_abbreviate.upper())
127+
```
128+
129+
For more information, read the [regex-sub][approach-regex-sub] approach.
130+
131+
132+
## Other approaches
133+
134+
Besides these seven idiomatic approaches, there are a multitude of possible variations using different string cleaning and joining methods.
135+
136+
However, these listed approaches cover the majority of 'mainstream' strategies.
137+
138+
139+
## Which approach to use?
140+
141+
All seven approaches are idiomatic, and show multiple paradigms and possibilities.
142+
All approaches are also `O(n)`, with `n` being the length of the input string.
143+
No matter the removal method, the entire input string must be iterated through to be cleaned and the first letters extracted.
144+
145+
Of these strategies, the `loop` approach is the fastest, although `list-comprehension`, `map`, and `reduce` have near-identical performance for the test data.
146+
All approaches are fairly succinct and readable, although the 'classic' loop is probably the easiest understood by those coming to Python from other programming languages.
147+
148+
149+
The least performant for the test data was using a `generator-expression`, `re.findall` and `re.sub` (_least performant_).
150+
151+
To compare performance of the approaches, take a look at the [Performance article][article-performance].
152+
153+
[approach-functools-reduce]: https://exercism.org/tracks/python/exercises/acronym/approaches/functools-reduce
154+
[approach-generator-expression]: https://exercism.org/tracks/python/exercises/acronym/approaches/generator-expression
155+
[approach-list-comprehension]: https://exercism.org/tracks/python/exercises/acronym/approaches/list-comprehension
156+
[approach-loop]: https://exercism.org/tracks/python/exercises/acronym/approaches/loop
157+
[approach-map-function]: https://exercism.org/tracks/python/exercises/acronym/approaches/map-function
158+
[approach-regex-join]: https://exercism.org/tracks/python/exercises/acronym/approaches/regex-join
159+
[approach-regex-sub]: https://exercism.org/tracks/python/exercises/acronym/approaches/regex-sub
160+
[article-performance]: https://exercism.org/tracks/python/exercises/isogram/articles/performance

affine-cipher/affine_cipher.py

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
def encode(plain_text, a, b):
2+
pass
3+
4+
5+
def decode(ciphered_text, a, b):
6+
pass

0 commit comments

Comments
 (0)