summary refs log tree commit diff stats
path: root/raku/word-count
diff options
context:
space:
mode:
Diffstat (limited to 'raku/word-count')
-rw-r--r--raku/word-count/README.md54
-rw-r--r--raku/word-count/WordCount.rakumod5
-rw-r--r--raku/word-count/word-count.rakutest184
3 files changed, 243 insertions, 0 deletions
diff --git a/raku/word-count/README.md b/raku/word-count/README.md
new file mode 100644
index 0000000..bf97113
--- /dev/null
+++ b/raku/word-count/README.md
@@ -0,0 +1,54 @@
+# Word Count
+
+Given a phrase, count the occurrences of each _word_ in that phrase.
+
+For the purposes of this exercise you can expect that a _word_ will always be one of:
+
+1. A _number_ composed of one or more ASCII digits (ie "0" or "1234") OR
+2. A _simple word_ composed of one or more ASCII letters (ie "a" or "they") OR
+3. A _contraction_ of two _simple words_ joined by a single apostrophe (ie "it's" or "they're")
+
+When counting words you can assume the following rules:
+
+1. The count is _case insensitive_ (ie "You", "you", and "YOU" are 3 uses of the same word)
+2. The count is _unordered_; the tests will ignore how words and counts are ordered
+3. Other than the apostrophe in a _contraction_ all forms of _punctuation_ are ignored
+4. The words can be separated by _any_ form of whitespace (ie "\t", "\n", " ")
+
+For example, for the phrase `"That's the password: 'PASSWORD 123'!", cried the Special Agent.\nSo I fled.` the count would be:
+
+```text
+that's: 1
+the: 2
+password: 2
+123: 1
+cried: 1
+special: 1
+agent: 1
+so: 1
+i: 1
+fled: 1
+```
+
+## Resources
+
+Remember to check out the Raku [documentation](https://docs.raku.org/) and
+[resources](https://raku.org/resources/) pages for information, tips, and
+examples if you get stuck.
+
+## Running the tests
+
+There is a test suite and module included with the exercise.
+The test suite (a file with the extension `.rakutest`) will attempt to run routines
+from the module (a file with the extension `.rakumod`).
+Add/modify routines in the module so that the tests will pass! You can view the
+test data by executing the command `raku --doc *.rakutest` (\* being the name of the
+test suite), and run the test suite for the exercise by executing the command
+`prove6 .` in the exercise directory.
+
+## Source
+
+This is a classic toy problem, but we were reminded of it by seeing it in the Go Tour.
+
+## Submitting Incomplete Solutions
+It's possible to submit an incomplete solution so you can see how others have completed the exercise.
diff --git a/raku/word-count/WordCount.rakumod b/raku/word-count/WordCount.rakumod
new file mode 100644
index 0000000..62bc1a5
--- /dev/null
+++ b/raku/word-count/WordCount.rakumod
@@ -0,0 +1,5 @@
+unit module WordCount;
+
+sub count-words (Str $sentence) is export {
+    $sentence.lc.comb(/\w+\'\w+|\w+/).Bag;
+}
diff --git a/raku/word-count/word-count.rakutest b/raku/word-count/word-count.rakutest
new file mode 100644
index 0000000..45158aa
--- /dev/null
+++ b/raku/word-count/word-count.rakutest
@@ -0,0 +1,184 @@
+#!/usr/bin/env raku
+use Test;
+use JSON::Fast;
+use lib $?FILE.IO.dirname;
+use WordCount;
+plan 13;
+
+my @test-cases = from-json($=pod.pop.contents).List;
+for @test-cases -> %case {
+  cmp-ok count-words(%case<input><sentence>),
+    '~~', %case<expected>.Bag, %case<description>;
+}
+
+=head2 Test Cases
+=begin code
+[
+  {
+    "description": "count one word",
+    "expected": {
+      "word": 1
+    },
+    "input": {
+      "sentence": "word"
+    },
+    "property": "countWords"
+  },
+  {
+    "description": "count one of each word",
+    "expected": {
+      "each": 1,
+      "of": 1,
+      "one": 1
+    },
+    "input": {
+      "sentence": "one of each"
+    },
+    "property": "countWords"
+  },
+  {
+    "description": "multiple occurrences of a word",
+    "expected": {
+      "blue": 1,
+      "fish": 4,
+      "one": 1,
+      "red": 1,
+      "two": 1
+    },
+    "input": {
+      "sentence": "one fish two fish red fish blue fish"
+    },
+    "property": "countWords"
+  },
+  {
+    "description": "handles cramped lists",
+    "expected": {
+      "one": 1,
+      "three": 1,
+      "two": 1
+    },
+    "input": {
+      "sentence": "one,two,three"
+    },
+    "property": "countWords"
+  },
+  {
+    "description": "handles expanded lists",
+    "expected": {
+      "one": 1,
+      "three": 1,
+      "two": 1
+    },
+    "input": {
+      "sentence": "one,\ntwo,\nthree"
+    },
+    "property": "countWords"
+  },
+  {
+    "description": "ignore punctuation",
+    "expected": {
+      "as": 1,
+      "car": 1,
+      "carpet": 1,
+      "java": 1,
+      "javascript": 1
+    },
+    "input": {
+      "sentence": "car: carpet as java: javascript!!&@$%^&"
+    },
+    "property": "countWords"
+  },
+  {
+    "description": "include numbers",
+    "expected": {
+      "1": 1,
+      "2": 1,
+      "testing": 2
+    },
+    "input": {
+      "sentence": "testing, 1, 2 testing"
+    },
+    "property": "countWords"
+  },
+  {
+    "description": "normalize case",
+    "expected": {
+      "go": 3,
+      "stop": 2
+    },
+    "input": {
+      "sentence": "go Go GO Stop stop"
+    },
+    "property": "countWords"
+  },
+  {
+    "description": "with apostrophes",
+    "expected": {
+      "cry": 1,
+      "don't": 2,
+      "first": 1,
+      "laugh": 1,
+      "then": 1
+    },
+    "input": {
+      "sentence": "First: don't laugh. Then: don't cry."
+    },
+    "property": "countWords"
+  },
+  {
+    "description": "with quotations",
+    "expected": {
+      "and": 1,
+      "between": 1,
+      "can't": 1,
+      "joe": 1,
+      "large": 2,
+      "tell": 1
+    },
+    "input": {
+      "sentence": "Joe can't tell between 'large' and large."
+    },
+    "property": "countWords"
+  },
+  {
+    "description": "substrings from the beginning",
+    "expected": {
+      "a": 1,
+      "and": 1,
+      "app": 1,
+      "apple": 1,
+      "between": 1,
+      "can't": 1,
+      "joe": 1,
+      "tell": 1
+    },
+    "input": {
+      "sentence": "Joe can't tell between app, apple and a."
+    },
+    "property": "countWords"
+  },
+  {
+    "description": "multiple spaces not detected as a word",
+    "expected": {
+      "multiple": 1,
+      "whitespaces": 1
+    },
+    "input": {
+      "sentence": " multiple   whitespaces"
+    },
+    "property": "countWords"
+  },
+  {
+    "description": "alternating word separators not detected as a word",
+    "expected": {
+      "one": 1,
+      "three": 1,
+      "two": 1
+    },
+    "input": {
+      "sentence": ",\n,one,\n ,two \n 'three'"
+    },
+    "property": "countWords"
+  }
+]
+=end code