Skip to content

Commit

Permalink
parallel-letter-frequency: Implement exercise to resolve exercism#744 (
Browse files Browse the repository at this point in the history
…exercism#891)

* parallel-letter-frequency: Implement exercise to resolve exercism#744

* parallel-letter-frequency: Removed infinite loop

* parallel-letter-frequency: README format fixes
  • Loading branch information
forgeRW authored and smalley committed Nov 12, 2017
1 parent b0a86c6 commit 60ce289
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 0 deletions.
14 changes: 14 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,20 @@
"logic"
]
},
{
"uuid": "7126f86c-0e7f-7080-b4cb-3c8457dfcadcfe7e446",
"slug": "parallel-letter-frequency",
"core": false,
"unlocked_by": null,
"difficulty": 5,
"topics": [
"threading",
"parallellism",
"loops",
"queues",
"strings"
]
},
{
"uuid": "ca7c5ef1-5135-4fb4-8e68-669ef0f2a51a",
"slug": "rna-transcription",
Expand Down
24 changes: 24 additions & 0 deletions exercises/parallel-letter-frequency/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Parallel Letter Frequency

Count the frequency of letters in texts using parallel computation.

Parallelism is about doing things in parallel that can also be done
sequentially. A common example is counting the frequency of letters.
Create a function that returns the total frequency of each letter in a
list of texts and that employs parallelism.

The letters used consists of ASCII letters `a` to `z`, inclusive, and is case
insensitive.

## Submitting Exercises

Note that, when trying to submit an exercise, make sure the solution is in the `exercism/python/<exerciseName>` directory.

For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.


For more detailed information about running tests, code style and linting,
please see the [help page](http://exercism.io/languages/python).

## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
56 changes: 56 additions & 0 deletions exercises/parallel-letter-frequency/example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# -*- coding: utf-8 -*-
from collections import Counter
from threading import Lock, Thread
from time import sleep
import sys
if sys.version[0] == '2':
from Queue import Queue
else:
from queue import Queue

total_workers = 3 # Maximum number of threads chosen arbitrarily


class LetterCounter(object):

def __init__(self):
self.lock = Lock()
self.value = Counter()

def add_counter(self, counter_to_add):
self.lock.acquire()
try:
self.value = self.value + counter_to_add
finally:
self.lock.release()


def count_letters(queue_of_texts, letter_to_frequency, worker_id):
while not queue_of_texts.empty():
sleep(worker_id + 1)
line_input = queue_of_texts.get()
if line_input is not None:
letters_in_line = Counter([x for x in line_input.lower() if
x.isalpha()])
letter_to_frequency.add_counter(letters_in_line)
queue_of_texts.task_done()
if line_input is None:
break


def calculate(list_of_texts):
queue_of_texts = Queue()
[queue_of_texts.put(line) for line in list_of_texts]
letter_to_frequency = LetterCounter()
threads = []
for i in range(total_workers):
worker = Thread(target=count_letters, args=(queue_of_texts,
letter_to_frequency, i))
worker.start()
threads.append(worker)
queue_of_texts.join()
for i in range(total_workers):
queue_of_texts.put(None)
for t in threads:
t.join()
return letter_to_frequency.value
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def calculate(text_input):
pass
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# -*- coding: utf-8 -*-
from collections import Counter
import unittest

from parallel_letter_frequency import calculate


class ParallelLetterFrequencyTest(unittest.TestCase):
def test_one_letter(self):
actual = calculate(['a'])
expected = {'a': 1}
self.assertDictEqual(actual, expected)

def test_case_insensitivity(self):
actual = calculate(['aA'])
expected = {'a': 2}
self.assertDictEqual(actual, expected)

def test_numbers(self):
actual = calculate(['012', '345', '6789'])
expected = {}
self.assertDictEqual(actual, expected)

def test_punctuations(self):
actual = calculate(['[]\;,', './{}|', ':"<>?'])
expected = {}
self.assertDictEqual(actual, expected)

def test_whitespaces(self):
actual = calculate([' ', '\t ', '\n\n'])
expected = {}
self.assertDictEqual(actual, expected)

def test_repeated_string_with_known_frequencies(self):
letter_frequency = 3
text_input = 'abc\n' * letter_frequency
actual = calculate(text_input.split('\n'))
expected = {'a': letter_frequency, 'b': letter_frequency,
'c': letter_frequency}
self.assertDictEqual(actual, expected)

def test_multiline_text(self):
text_input = "3 Quotes from Excerism Homepage:\n" + \
"\tOne moment you feel like you're\n" + \
"getting it. The next moment you're\n" + \
"stuck.\n" + \
"\tYou know what it’s like to be fluent.\n" + \
"Suddenly you’re feeling incompetent\n" + \
"and clumsy.\n" + \
"\tHaphazard, convoluted code is\n" + \
"infuriating, not to mention costly. That\n" + \
"slapdash explosion of complexity is an\n" + \
"expensive yak shave waiting to\n" + \
"happen."
actual = calculate(text_input.split('\n'))
expected = Counter([x for x in text_input.lower() if x.isalpha()])
self.assertDictEqual(actual, expected)


if __name__ == '__main__':
unittest.main()

0 comments on commit 60ce289

Please sign in to comment.