food chain exercise

This commit is contained in:
Xevion
2019-07-24 15:37:53 -05:00
parent 5402cee2da
commit 91c98fd9b7
4 changed files with 278 additions and 0 deletions

View File

@@ -0,0 +1 @@
{"track":"python","exercise":"food-chain","id":"b890dc4d044149a7bfbd854912c68bbf","url":"https://exercism.io/my/solutions/b890dc4d044149a7bfbd854912c68bbf","handle":"Xevion","is_requester":true,"auto_approve":false}

113
python/food-chain/README.md Normal file
View File

@@ -0,0 +1,113 @@
# Food Chain
Generate the lyrics of the song 'I Know an Old Lady Who Swallowed a Fly'.
While you could copy/paste the lyrics,
or read them from a file, this problem is much more
interesting if you approach it algorithmically.
This is a [cumulative song](http://en.wikipedia.org/wiki/Cumulative_song) of unknown origin.
This is one of many common variants.
```text
I know an old lady who swallowed a fly.
I don't know why she swallowed the fly. Perhaps she'll die.
I know an old lady who swallowed a spider.
It wriggled and jiggled and tickled inside her.
She swallowed the spider to catch the fly.
I don't know why she swallowed the fly. Perhaps she'll die.
I know an old lady who swallowed a bird.
How absurd to swallow a bird!
She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.
She swallowed the spider to catch the fly.
I don't know why she swallowed the fly. Perhaps she'll die.
I know an old lady who swallowed a cat.
Imagine that, to swallow a cat!
She swallowed the cat to catch the bird.
She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.
She swallowed the spider to catch the fly.
I don't know why she swallowed the fly. Perhaps she'll die.
I know an old lady who swallowed a dog.
What a hog, to swallow a dog!
She swallowed the dog to catch the cat.
She swallowed the cat to catch the bird.
She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.
She swallowed the spider to catch the fly.
I don't know why she swallowed the fly. Perhaps she'll die.
I know an old lady who swallowed a goat.
Just opened her throat and swallowed a goat!
She swallowed the goat to catch the dog.
She swallowed the dog to catch the cat.
She swallowed the cat to catch the bird.
She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.
She swallowed the spider to catch the fly.
I don't know why she swallowed the fly. Perhaps she'll die.
I know an old lady who swallowed a cow.
I don't know how she swallowed a cow!
She swallowed the cow to catch the goat.
She swallowed the goat to catch the dog.
She swallowed the dog to catch the cat.
She swallowed the cat to catch the bird.
She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.
She swallowed the spider to catch the fly.
I don't know why she swallowed the fly. Perhaps she'll die.
I know an old lady who swallowed a horse.
She's dead, of course!
```
## Exception messages
Sometimes it is necessary to raise an exception. When you do this, you should include a meaningful error message to
indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. Not
every exercise will require you to raise an exception, but for those that do, the tests will only pass if you include
a message.
To raise a message with an exception, just write it as an argument to the exception type. For example, instead of
`raise Exception`, you should write:
```python
raise Exception("Meaningful message indicating the source of the error")
```
## Running the tests
To run the tests, run the appropriate command below ([why they are different](https://github.com/pytest-dev/pytest/issues/1629#issue-161422224)):
- Python 2.7: `py.test food_chain_test.py`
- Python 3.4+: `pytest food_chain_test.py`
Alternatively, you can tell Python to run the pytest module (allowing the same command to be used regardless of Python version):
`python -m pytest food_chain_test.py`
### Common `pytest` options
- `-v` : enable verbose output
- `-x` : stop running tests on first failure
- `--ff` : run failures from previous test before running other test cases
For other options, see `python -m pytest -h`
## Submitting Exercises
Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/food-chain` directory.
You can find your Exercism workspace by running `exercism debug` and looking for the line that starts with `Workspace`.
For more detailed information about running tests, code style and linting,
please see [Running the Tests](http://exercism.io/tracks/python/tests).
## Source
Wikipedia [http://en.wikipedia.org/wiki/There_Was_an_Old_Lady_Who_Swallowed_a_Fly](http://en.wikipedia.org/wiki/There_Was_an_Old_Lady_Who_Swallowed_a_Fly)
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -0,0 +1,53 @@
intro = "I know an old lady who swallowed a {}."
intro_addendums = [
"It wriggled and jiggled and tickled inside her.",
"How absurd to swallow a bird!",
"Imagine that, to swallow a cat!",
"What a hog, to swallow a dog!",
"Just opened her throat and swallowed a goat!",
"I don't know how she swallowed a cow!"
]
names = [
"fly",
"spider",
"bird",
"cat",
"dog",
"goat",
"cow",
"horse"
]
middle = "She swallowed the {} to catch the {}."
outro = "I don't know why she swallowed the fly. Perhaps she'll die."
last = "She\'s dead, of course!"
special_middle = "that wriggled and jiggled and tickled inside her"
# Just does the math for constructing single and multiple verses. I realized only halfway through that
# everything was going to be in lists, so I just used split, which honestly is easier than needing some kind of flattening
# or list extending setup to get the lists to be in proper order
def recite(start_verse, end_verse):
if start_verse == end_verse:
return construct(start_verse - 1).split('\n')
return '\n\n'.join(construct(verse) for verse in range(start_verse - 1, end_verse)).split('\n')
# Constructs a specific verse
def construct(verse):
# Constructs the middle part. Only the spider verse makes this function required.
def makemiddle(versen):
# Special spider inner verse
if versen == 2:
return middle.format(names[versen], names[versen-1] + ' ' + special_middle)
return middle.format(names[versen], names[versen-1])
# Horse verse
if verse == 7:
return "{}\n{}".format(intro.format(names[verse]), last)
# Constructs the intoduction of a verse
constructed_intro = '{}{}'.format(
intro.format(names[verse]),
'\n' + intro_addendums[verse-1] if verse > 0 else ''
)
# Constructs the middle verse using make middle
constructed_middle = '\n'.join([''] + [makemiddle(versen) for versen in range(verse, 0, -1)]) if verse >= 1 else ''
# Finally constructs the entire properly.
return "{}{}{}".format(constructed_intro, constructed_middle, '\n' + outro)

View File

@@ -0,0 +1,111 @@
import unittest
from food_chain import recite
# Tests adapted from `problem-specifications//canonical-data.json` @ v2.1.0
class FoodChainTest(unittest.TestCase):
def test_fly(self):
expected = [
"I know an old lady who swallowed a fly.",
"I don't know why she swallowed the fly. Perhaps she'll die.",
]
self.assertEqual(recite(1, 1), expected)
def test_spider(self):
expected = [
"I know an old lady who swallowed a spider.",
"It wriggled and jiggled and tickled inside her.",
"She swallowed the spider to catch the fly.",
"I don't know why she swallowed the fly. Perhaps she'll die.",
]
self.assertEqual(recite(2, 2), expected)
def test_bird(self):
expected = [
"I know an old lady who swallowed a bird.",
"How absurd to swallow a bird!",
"She swallowed the bird to catch the spider that "
"wriggled and jiggled and tickled inside her.",
"She swallowed the spider to catch the fly.",
"I don't know why she swallowed the fly. Perhaps she'll die.",
]
self.assertEqual(recite(3, 3), expected)
def test_cat(self):
expected = [
"I know an old lady who swallowed a cat.",
"Imagine that, to swallow a cat!",
"She swallowed the cat to catch the bird.",
"She swallowed the bird to catch the spider that "
"wriggled and jiggled and tickled inside her.",
"She swallowed the spider to catch the fly.",
"I don't know why she swallowed the fly. Perhaps she'll die.",
]
self.assertEqual(recite(4, 4), expected)
def test_dog(self):
expected = [
"I know an old lady who swallowed a dog.",
"What a hog, to swallow a dog!",
"She swallowed the dog to catch the cat.",
"She swallowed the cat to catch the bird.",
"She swallowed the bird to catch the spider that wriggled "
"and jiggled and tickled inside her.",
"She swallowed the spider to catch the fly.",
"I don't know why she swallowed the fly. Perhaps she'll die.",
]
self.assertEqual(recite(5, 5), expected)
def test_goat(self):
expected = [
"I know an old lady who swallowed a goat.",
"Just opened her throat and swallowed a goat!",
"She swallowed the goat to catch the dog.",
"She swallowed the dog to catch the cat.",
"She swallowed the cat to catch the bird.",
"She swallowed the bird to catch the spider that "
"wriggled and jiggled and tickled inside her.",
"She swallowed the spider to catch the fly.",
"I don't know why she swallowed the fly. Perhaps she'll die.",
]
self.assertEqual(recite(6, 6), expected)
def test_cow(self):
expected = [
"I know an old lady who swallowed a cow.",
"I don't know how she swallowed a cow!",
"She swallowed the cow to catch the goat.",
"She swallowed the goat to catch the dog.",
"She swallowed the dog to catch the cat.",
"She swallowed the cat to catch the bird.",
"She swallowed the bird to catch the spider that "
"wriggled and jiggled and tickled inside her.",
"She swallowed the spider to catch the fly.",
"I don't know why she swallowed the fly. Perhaps she'll die.",
]
self.assertEqual(recite(7, 7), expected)
def test_horse(self):
expected = [
"I know an old lady who swallowed a horse.",
"She's dead, of course!",
]
self.assertEqual(recite(8, 8), expected)
def test_multiple_verses(self):
expected = recite(1, 1) + [""] + recite(2, 2) + [""] + recite(3, 3)
self.assertEqual(recite(1, 3), expected)
def test_full_song(self):
expected = []
for n in range(1, 9):
expected += recite(n, n) + [""]
expected.pop()
self.assertEqual(recite(1, 8), expected)
if __name__ == '__main__':
unittest.main()