├── README.md ├── accumulate ├── README.md ├── accumulate.exs └── accumulate_test.exs ├── acronym ├── README.md ├── acronym.exs └── acronym_test.exs ├── anagram ├── README.md ├── anagram.exs └── anagram_test.exs ├── beer-song ├── README.md ├── beer_song.exs └── beer_song_test.exs ├── bob ├── README.md ├── bob.exs └── bob_test.exs ├── bracket-push ├── README.md ├── bracket_push.exs └── bracket_push_test.exs ├── hamming ├── README.md ├── hamming.exs └── hamming_test.exs ├── list-ops ├── README.md ├── list_ops.exs └── list_ops_test.exs ├── nucleotide-count ├── README.md ├── dna.exs └── nucleotide_count_test.exs ├── phone-number ├── README.md ├── phone_number.exs └── phone_number_test.exs ├── rna-transcription ├── README.md ├── dna.exs └── rna_transcription_test.exs ├── run-length-encoding ├── README.md ├── rle.exs └── rle_test.exs ├── space-age ├── README.md ├── space_age.exs └── space_age_test.exs ├── sublist ├── README.md ├── sublist.exs └── sublist_test.exs └── word-count ├── README.md ├── word_count.exs └── word_count_test.exs /README.md: -------------------------------------------------------------------------------- 1 | # elixir.challenges 2 | This repository consists of code challenges that I solve in my spare time. 3 | -------------------------------------------------------------------------------- /accumulate/README.md: -------------------------------------------------------------------------------- 1 | # Accumulate 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 22 | provided by your standard library! 23 | Solve this one yourself using other basic tools instead. 24 | 25 | Elixir specific: it's perfectly fine to use `Enum.reduce` or 26 | `Enumerable.reduce`. 27 | 28 | Lisp specific: it's perfectly fine to use `MAPCAR` or the equivalent, 29 | as this is idiomatic Lisp, not a library function. 30 | 31 | ## Running tests 32 | 33 | Execute the tests with: 34 | 35 | ```bash 36 | $ elixir bob_test.exs 37 | ``` 38 | 39 | (Replace `bob_test.exs` with the name of the test file.) 40 | 41 | 42 | ### Pending tests 43 | 44 | In the test suites, all but the first test have been skipped. 45 | 46 | Once you get a test passing, you can unskip the next one by 47 | commenting out the relevant `@tag :pending` with a `#` symbol. 48 | 49 | For example: 50 | 51 | ```elixir 52 | # @tag :pending 53 | test "shouting" do 54 | assert Bob.hey("WATCH OUT!") == "Whoa, chill out!" 55 | end 56 | ``` 57 | 58 | Or, you can enable all the tests by commenting out the 59 | `ExUnit.configure` line in the test suite. 60 | 61 | ```elixir 62 | # ExUnit.configure exclude: :pending, trace: true 63 | ``` 64 | 65 | For more detailed information about the Elixir track, please 66 | see the [help page](http://exercism.io/languages/elixir). 67 | 68 | ## Source 69 | 70 | Conversation with James Edward Gray II [https://twitter.com/jeg2](https://twitter.com/jeg2) 71 | -------------------------------------------------------------------------------- /accumulate/accumulate.exs: -------------------------------------------------------------------------------- 1 | defmodule Accumulate do 2 | @doc """ 3 | Given a list and a function, apply the function to each list item and 4 | replace it with the function's return value. 5 | 6 | Returns a list. 7 | 8 | ## Examples 9 | 10 | iex> Accumulate.accumulate([], fn(x) -> x * 2 end) 11 | [] 12 | 13 | iex> Accumulate.accumulate([1, 2, 3], fn(x) -> x * 2 end) 14 | [2, 4, 6] 15 | 16 | """ 17 | 18 | @spec accumulate(list, (any -> any)) :: list 19 | def accumulate([h|t], fun) do 20 | [fun.(h)|accumulate(t, fun)] 21 | end 22 | 23 | def accumulate([], _), do: [] 24 | end 25 | -------------------------------------------------------------------------------- /accumulate/accumulate_test.exs: -------------------------------------------------------------------------------- 1 | if !System.get_env("EXERCISM_TEST_EXAMPLES") do 2 | Code.load_file("accumulate.exs") 3 | end 4 | 5 | ExUnit.start 6 | ExUnit.configure exclude: :pending, trace: true 7 | 8 | defmodule AccumulateTest do 9 | use ExUnit.Case 10 | 11 | test "accumulate empty list" do 12 | assert Accumulate.accumulate([], fn(n) -> n * n end) == [] 13 | end 14 | 15 | test "accumulate square numbers" do 16 | assert Accumulate.accumulate([1, 2, 3], fn(n) -> n * n end) == [1, 4, 9] 17 | end 18 | 19 | test "accumulate upcased strings" do 20 | fun = fn(w) -> String.upcase(w) end 21 | assert Accumulate.accumulate(["hello", "world"], fun) == ["HELLO", "WORLD"] 22 | end 23 | 24 | test "accumulate reversed strings" do 25 | fun = fn(w) -> String.reverse(w) end 26 | words = ~w(the quick brown fox etc) 27 | expected = ["eht", "kciuq", "nworb", "xof", "cte"] 28 | assert Accumulate.accumulate(words, fun) == expected 29 | end 30 | 31 | test "accumulate recursively" do 32 | chars = ~w(a b c) 33 | nums = ~w(1 2 3) 34 | fun = fn(c) -> for num <- nums, do: c <> num end 35 | expected = [["a1", "a2", "a3"], ["b1", "b2", "b3"], ["c1", "c2", "c3"]] 36 | assert Accumulate.accumulate(chars, fun) == expected 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /acronym/README.md: -------------------------------------------------------------------------------- 1 | # Acronym 2 | 3 | Convert a long 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 8 | like Portable Network Graphics to its acronym (PNG). 9 | 10 | 11 | ## Running tests 12 | 13 | Execute the tests with: 14 | 15 | ```bash 16 | $ elixir bob_test.exs 17 | ``` 18 | 19 | (Replace `bob_test.exs` with the name of the test file.) 20 | 21 | 22 | ### Pending tests 23 | 24 | In the test suites, all but the first test have been skipped. 25 | 26 | Once you get a test passing, you can unskip the next one by 27 | commenting out the relevant `@tag :pending` with a `#` symbol. 28 | 29 | For example: 30 | 31 | ```elixir 32 | # @tag :pending 33 | test "shouting" do 34 | assert Bob.hey("WATCH OUT!") == "Whoa, chill out!" 35 | end 36 | ``` 37 | 38 | Or, you can enable all the tests by commenting out the 39 | `ExUnit.configure` line in the test suite. 40 | 41 | ```elixir 42 | # ExUnit.configure exclude: :pending, trace: true 43 | ``` 44 | 45 | For more detailed information about the Elixir track, please 46 | see the [help page](http://exercism.io/languages/elixir). 47 | 48 | ## Source 49 | 50 | Julien Vanier [https://github.com/monkbroc](https://github.com/monkbroc) 51 | -------------------------------------------------------------------------------- /acronym/acronym.exs: -------------------------------------------------------------------------------- 1 | defmodule Acronym do 2 | @doc """ 3 | Generate an acronym from a string. 4 | "This is a string" => "TIAS" 5 | """ 6 | @spec abbreviate(String.t()) :: String.t() 7 | def abbreviate(string) do 8 | Regex.scan(~r{[A-Z]?[a-z]*}, string) 9 | |> List.flatten 10 | |> Enum.map(&(String.first(&1))) 11 | |> Enum.join 12 | |> String.upcase 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /acronym/acronym_test.exs: -------------------------------------------------------------------------------- 1 | if !System.get_env("EXERCISM_TEST_EXAMPLES") do 2 | Code.load_file("acronym.exs") 3 | end 4 | 5 | ExUnit.start 6 | ExUnit.configure exclude: :pending, trace: true 7 | 8 | defmodule AcronymTest do 9 | use ExUnit.Case 10 | 11 | test "it produces acronyms from title case" do 12 | assert Acronym.abbreviate("Portable Networks Graphic") === "PNG" 13 | end 14 | 15 | test "it produces acronyms from lower case" do 16 | assert Acronym.abbreviate("Ruby on Rails") === "ROR" 17 | end 18 | 19 | test "it produces acronyms from inconsistent case" do 20 | assert Acronym.abbreviate("HyperText Markup Language") === "HTML" 21 | end 22 | 23 | test "it ignores punctuation" do 24 | assert Acronym.abbreviate("First in, First out") === "FIFO" 25 | end 26 | 27 | test "produces acronyms ignoring punctuation and casing" do 28 | assert Acronym.abbreviate("Complementary Metal-Oxide semiconductor") === "CMOS" 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /anagram/README.md: -------------------------------------------------------------------------------- 1 | # Anagram 2 | 3 | Write a program that, given a word and a list of possible anagrams, selects the correct sublist. 4 | 5 | Given `"listen"` and a list of candidates like `"enlists" "google" 6 | "inlets" "banana"` the program should return a list containing 7 | `"inlets"`. 8 | 9 | ## Running tests 10 | 11 | ```bash 12 | $ elixir bob_test.exs 13 | ``` 14 | 15 | (Replace `bob_test.exs` with the name of the test file.) 16 | 17 | ## Source 18 | 19 | Inspired by the Extreme Startup game [view source](https://github.com/rchatley/extreme_startup) 20 | -------------------------------------------------------------------------------- /anagram/anagram.exs: -------------------------------------------------------------------------------- 1 | defmodule Anagram do 2 | @doc """ 3 | Returns all candidates that are anagrams of, but not equal to, 'base'. 4 | """ 5 | @spec match(String.t, [String.t]) :: [String.t] 6 | def match(base, candidates) do 7 | Enum.filter(candidates, &anagram?(&1, base)) 8 | end 9 | 10 | defp anagram?(candidate, base) do 11 | do_anagram(String.downcase(candidate), String.downcase(base)) 12 | end 13 | 14 | defp do_anagram(a, a), do: false 15 | defp do_anagram(a, b), do: hash_for(a) === hash_for(b) 16 | 17 | defp hash_for(word), do: word |> String.codepoints |> Enum.sort 18 | 19 | end 20 | -------------------------------------------------------------------------------- /anagram/anagram_test.exs: -------------------------------------------------------------------------------- 1 | if System.get_env("EXERCISM_TEST_EXAMPLES") do 2 | Code.load_file("example.exs") 3 | else 4 | Code.load_file("anagram.exs") 5 | end 6 | 7 | ExUnit.start 8 | 9 | defmodule AnagramTest do 10 | use ExUnit.Case 11 | 12 | test "no matches" do 13 | matches = Anagram.match "diaper", ["hello", "world", "zombies", "pants"] 14 | assert matches == [] 15 | end 16 | 17 | test "detect simple anagram" do 18 | matches = Anagram.match "ant", ["tan", "stand", "at"] 19 | assert matches == ["tan"] 20 | end 21 | 22 | test "detect multiple anagrams" do 23 | matches = Anagram.match "master", ["stream", "pigeon", "maters"] 24 | assert matches == ["stream", "maters"] 25 | end 26 | 27 | test "do not detect anagram subsets" do 28 | matches = Anagram.match "good", ~w(dog goody) 29 | assert matches == [] 30 | end 31 | 32 | test "detect anagram" do 33 | matches = Anagram.match "listen", ~w(enlists google inlets banana) 34 | assert matches == ["inlets"] 35 | end 36 | 37 | test "multiple anagrams" do 38 | matches = Anagram.match "allergy", ~w(gallery ballerina regally clergy largely leading) 39 | assert matches == ["gallery", "regally", "largely"] 40 | end 41 | 42 | test "anagrams must use all letters exactly once" do 43 | matches = Anagram.match "patter", ["tapper"] 44 | assert matches == [] 45 | end 46 | 47 | test "detect anagrams with case-insensitive subject" do 48 | matches = Anagram.match "Orchestra", ~w(cashregister carthorse radishes) 49 | assert matches == ["carthorse"] 50 | end 51 | 52 | test "detect anagrams with case-insensitive candidate" do 53 | matches = Anagram.match "orchestra", ~w(cashregister Carthorse radishes) 54 | assert matches == ["Carthorse"] 55 | end 56 | 57 | test "anagrams must not be the source word" do 58 | matches = Anagram.match "corn", ["corn", "dark", "Corn", "rank", "CORN", "cron", "park"] 59 | assert matches == ["cron"] 60 | end 61 | 62 | test "do not detect words based on checksum" do 63 | matches = Anagram.match "mass", ["last"] 64 | assert matches == [] 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /beer-song/README.md: -------------------------------------------------------------------------------- 1 | # Beer Song 2 | 3 | Write a program which produces the lyrics to that beloved classic, that field-trip favorite: 99 Bottles of Beer on the Wall. 4 | 5 | Note that not all verses are identical. 6 | 7 | ```plain 8 | 99 bottles of beer on the wall, 99 bottles of beer. 9 | Take one down and pass it around, 98 bottles of beer on the wall. 10 | 11 | 98 bottles of beer on the wall, 98 bottles of beer. 12 | Take one down and pass it around, 97 bottles of beer on the wall. 13 | 14 | 97 bottles of beer on the wall, 97 bottles of beer. 15 | Take one down and pass it around, 96 bottles of beer on the wall. 16 | 17 | 96 bottles of beer on the wall, 96 bottles of beer. 18 | Take one down and pass it around, 95 bottles of beer on the wall. 19 | 20 | 95 bottles of beer on the wall, 95 bottles of beer. 21 | Take one down and pass it around, 94 bottles of beer on the wall. 22 | 23 | 94 bottles of beer on the wall, 94 bottles of beer. 24 | Take one down and pass it around, 93 bottles of beer on the wall. 25 | 26 | 93 bottles of beer on the wall, 93 bottles of beer. 27 | Take one down and pass it around, 92 bottles of beer on the wall. 28 | 29 | 92 bottles of beer on the wall, 92 bottles of beer. 30 | Take one down and pass it around, 91 bottles of beer on the wall. 31 | 32 | 91 bottles of beer on the wall, 91 bottles of beer. 33 | Take one down and pass it around, 90 bottles of beer on the wall. 34 | 35 | 90 bottles of beer on the wall, 90 bottles of beer. 36 | Take one down and pass it around, 89 bottles of beer on the wall. 37 | 38 | 89 bottles of beer on the wall, 89 bottles of beer. 39 | Take one down and pass it around, 88 bottles of beer on the wall. 40 | 41 | 88 bottles of beer on the wall, 88 bottles of beer. 42 | Take one down and pass it around, 87 bottles of beer on the wall. 43 | 44 | 87 bottles of beer on the wall, 87 bottles of beer. 45 | Take one down and pass it around, 86 bottles of beer on the wall. 46 | 47 | 86 bottles of beer on the wall, 86 bottles of beer. 48 | Take one down and pass it around, 85 bottles of beer on the wall. 49 | 50 | 85 bottles of beer on the wall, 85 bottles of beer. 51 | Take one down and pass it around, 84 bottles of beer on the wall. 52 | 53 | 84 bottles of beer on the wall, 84 bottles of beer. 54 | Take one down and pass it around, 83 bottles of beer on the wall. 55 | 56 | 83 bottles of beer on the wall, 83 bottles of beer. 57 | Take one down and pass it around, 82 bottles of beer on the wall. 58 | 59 | 82 bottles of beer on the wall, 82 bottles of beer. 60 | Take one down and pass it around, 81 bottles of beer on the wall. 61 | 62 | 81 bottles of beer on the wall, 81 bottles of beer. 63 | Take one down and pass it around, 80 bottles of beer on the wall. 64 | 65 | 80 bottles of beer on the wall, 80 bottles of beer. 66 | Take one down and pass it around, 79 bottles of beer on the wall. 67 | 68 | 79 bottles of beer on the wall, 79 bottles of beer. 69 | Take one down and pass it around, 78 bottles of beer on the wall. 70 | 71 | 78 bottles of beer on the wall, 78 bottles of beer. 72 | Take one down and pass it around, 77 bottles of beer on the wall. 73 | 74 | 77 bottles of beer on the wall, 77 bottles of beer. 75 | Take one down and pass it around, 76 bottles of beer on the wall. 76 | 77 | 76 bottles of beer on the wall, 76 bottles of beer. 78 | Take one down and pass it around, 75 bottles of beer on the wall. 79 | 80 | 75 bottles of beer on the wall, 75 bottles of beer. 81 | Take one down and pass it around, 74 bottles of beer on the wall. 82 | 83 | 74 bottles of beer on the wall, 74 bottles of beer. 84 | Take one down and pass it around, 73 bottles of beer on the wall. 85 | 86 | 73 bottles of beer on the wall, 73 bottles of beer. 87 | Take one down and pass it around, 72 bottles of beer on the wall. 88 | 89 | 72 bottles of beer on the wall, 72 bottles of beer. 90 | Take one down and pass it around, 71 bottles of beer on the wall. 91 | 92 | 71 bottles of beer on the wall, 71 bottles of beer. 93 | Take one down and pass it around, 70 bottles of beer on the wall. 94 | 95 | 70 bottles of beer on the wall, 70 bottles of beer. 96 | Take one down and pass it around, 69 bottles of beer on the wall. 97 | 98 | 69 bottles of beer on the wall, 69 bottles of beer. 99 | Take one down and pass it around, 68 bottles of beer on the wall. 100 | 101 | 68 bottles of beer on the wall, 68 bottles of beer. 102 | Take one down and pass it around, 67 bottles of beer on the wall. 103 | 104 | 67 bottles of beer on the wall, 67 bottles of beer. 105 | Take one down and pass it around, 66 bottles of beer on the wall. 106 | 107 | 66 bottles of beer on the wall, 66 bottles of beer. 108 | Take one down and pass it around, 65 bottles of beer on the wall. 109 | 110 | 65 bottles of beer on the wall, 65 bottles of beer. 111 | Take one down and pass it around, 64 bottles of beer on the wall. 112 | 113 | 64 bottles of beer on the wall, 64 bottles of beer. 114 | Take one down and pass it around, 63 bottles of beer on the wall. 115 | 116 | 63 bottles of beer on the wall, 63 bottles of beer. 117 | Take one down and pass it around, 62 bottles of beer on the wall. 118 | 119 | 62 bottles of beer on the wall, 62 bottles of beer. 120 | Take one down and pass it around, 61 bottles of beer on the wall. 121 | 122 | 61 bottles of beer on the wall, 61 bottles of beer. 123 | Take one down and pass it around, 60 bottles of beer on the wall. 124 | 125 | 60 bottles of beer on the wall, 60 bottles of beer. 126 | Take one down and pass it around, 59 bottles of beer on the wall. 127 | 128 | 59 bottles of beer on the wall, 59 bottles of beer. 129 | Take one down and pass it around, 58 bottles of beer on the wall. 130 | 131 | 58 bottles of beer on the wall, 58 bottles of beer. 132 | Take one down and pass it around, 57 bottles of beer on the wall. 133 | 134 | 57 bottles of beer on the wall, 57 bottles of beer. 135 | Take one down and pass it around, 56 bottles of beer on the wall. 136 | 137 | 56 bottles of beer on the wall, 56 bottles of beer. 138 | Take one down and pass it around, 55 bottles of beer on the wall. 139 | 140 | 55 bottles of beer on the wall, 55 bottles of beer. 141 | Take one down and pass it around, 54 bottles of beer on the wall. 142 | 143 | 54 bottles of beer on the wall, 54 bottles of beer. 144 | Take one down and pass it around, 53 bottles of beer on the wall. 145 | 146 | 53 bottles of beer on the wall, 53 bottles of beer. 147 | Take one down and pass it around, 52 bottles of beer on the wall. 148 | 149 | 52 bottles of beer on the wall, 52 bottles of beer. 150 | Take one down and pass it around, 51 bottles of beer on the wall. 151 | 152 | 51 bottles of beer on the wall, 51 bottles of beer. 153 | Take one down and pass it around, 50 bottles of beer on the wall. 154 | 155 | 50 bottles of beer on the wall, 50 bottles of beer. 156 | Take one down and pass it around, 49 bottles of beer on the wall. 157 | 158 | 49 bottles of beer on the wall, 49 bottles of beer. 159 | Take one down and pass it around, 48 bottles of beer on the wall. 160 | 161 | 48 bottles of beer on the wall, 48 bottles of beer. 162 | Take one down and pass it around, 47 bottles of beer on the wall. 163 | 164 | 47 bottles of beer on the wall, 47 bottles of beer. 165 | Take one down and pass it around, 46 bottles of beer on the wall. 166 | 167 | 46 bottles of beer on the wall, 46 bottles of beer. 168 | Take one down and pass it around, 45 bottles of beer on the wall. 169 | 170 | 45 bottles of beer on the wall, 45 bottles of beer. 171 | Take one down and pass it around, 44 bottles of beer on the wall. 172 | 173 | 44 bottles of beer on the wall, 44 bottles of beer. 174 | Take one down and pass it around, 43 bottles of beer on the wall. 175 | 176 | 43 bottles of beer on the wall, 43 bottles of beer. 177 | Take one down and pass it around, 42 bottles of beer on the wall. 178 | 179 | 42 bottles of beer on the wall, 42 bottles of beer. 180 | Take one down and pass it around, 41 bottles of beer on the wall. 181 | 182 | 41 bottles of beer on the wall, 41 bottles of beer. 183 | Take one down and pass it around, 40 bottles of beer on the wall. 184 | 185 | 40 bottles of beer on the wall, 40 bottles of beer. 186 | Take one down and pass it around, 39 bottles of beer on the wall. 187 | 188 | 39 bottles of beer on the wall, 39 bottles of beer. 189 | Take one down and pass it around, 38 bottles of beer on the wall. 190 | 191 | 38 bottles of beer on the wall, 38 bottles of beer. 192 | Take one down and pass it around, 37 bottles of beer on the wall. 193 | 194 | 37 bottles of beer on the wall, 37 bottles of beer. 195 | Take one down and pass it around, 36 bottles of beer on the wall. 196 | 197 | 36 bottles of beer on the wall, 36 bottles of beer. 198 | Take one down and pass it around, 35 bottles of beer on the wall. 199 | 200 | 35 bottles of beer on the wall, 35 bottles of beer. 201 | Take one down and pass it around, 34 bottles of beer on the wall. 202 | 203 | 34 bottles of beer on the wall, 34 bottles of beer. 204 | Take one down and pass it around, 33 bottles of beer on the wall. 205 | 206 | 33 bottles of beer on the wall, 33 bottles of beer. 207 | Take one down and pass it around, 32 bottles of beer on the wall. 208 | 209 | 32 bottles of beer on the wall, 32 bottles of beer. 210 | Take one down and pass it around, 31 bottles of beer on the wall. 211 | 212 | 31 bottles of beer on the wall, 31 bottles of beer. 213 | Take one down and pass it around, 30 bottles of beer on the wall. 214 | 215 | 30 bottles of beer on the wall, 30 bottles of beer. 216 | Take one down and pass it around, 29 bottles of beer on the wall. 217 | 218 | 29 bottles of beer on the wall, 29 bottles of beer. 219 | Take one down and pass it around, 28 bottles of beer on the wall. 220 | 221 | 28 bottles of beer on the wall, 28 bottles of beer. 222 | Take one down and pass it around, 27 bottles of beer on the wall. 223 | 224 | 27 bottles of beer on the wall, 27 bottles of beer. 225 | Take one down and pass it around, 26 bottles of beer on the wall. 226 | 227 | 26 bottles of beer on the wall, 26 bottles of beer. 228 | Take one down and pass it around, 25 bottles of beer on the wall. 229 | 230 | 25 bottles of beer on the wall, 25 bottles of beer. 231 | Take one down and pass it around, 24 bottles of beer on the wall. 232 | 233 | 24 bottles of beer on the wall, 24 bottles of beer. 234 | Take one down and pass it around, 23 bottles of beer on the wall. 235 | 236 | 23 bottles of beer on the wall, 23 bottles of beer. 237 | Take one down and pass it around, 22 bottles of beer on the wall. 238 | 239 | 22 bottles of beer on the wall, 22 bottles of beer. 240 | Take one down and pass it around, 21 bottles of beer on the wall. 241 | 242 | 21 bottles of beer on the wall, 21 bottles of beer. 243 | Take one down and pass it around, 20 bottles of beer on the wall. 244 | 245 | 20 bottles of beer on the wall, 20 bottles of beer. 246 | Take one down and pass it around, 19 bottles of beer on the wall. 247 | 248 | 19 bottles of beer on the wall, 19 bottles of beer. 249 | Take one down and pass it around, 18 bottles of beer on the wall. 250 | 251 | 18 bottles of beer on the wall, 18 bottles of beer. 252 | Take one down and pass it around, 17 bottles of beer on the wall. 253 | 254 | 17 bottles of beer on the wall, 17 bottles of beer. 255 | Take one down and pass it around, 16 bottles of beer on the wall. 256 | 257 | 16 bottles of beer on the wall, 16 bottles of beer. 258 | Take one down and pass it around, 15 bottles of beer on the wall. 259 | 260 | 15 bottles of beer on the wall, 15 bottles of beer. 261 | Take one down and pass it around, 14 bottles of beer on the wall. 262 | 263 | 14 bottles of beer on the wall, 14 bottles of beer. 264 | Take one down and pass it around, 13 bottles of beer on the wall. 265 | 266 | 13 bottles of beer on the wall, 13 bottles of beer. 267 | Take one down and pass it around, 12 bottles of beer on the wall. 268 | 269 | 12 bottles of beer on the wall, 12 bottles of beer. 270 | Take one down and pass it around, 11 bottles of beer on the wall. 271 | 272 | 11 bottles of beer on the wall, 11 bottles of beer. 273 | Take one down and pass it around, 10 bottles of beer on the wall. 274 | 275 | 10 bottles of beer on the wall, 10 bottles of beer. 276 | Take one down and pass it around, 9 bottles of beer on the wall. 277 | 278 | 9 bottles of beer on the wall, 9 bottles of beer. 279 | Take one down and pass it around, 8 bottles of beer on the wall. 280 | 281 | 8 bottles of beer on the wall, 8 bottles of beer. 282 | Take one down and pass it around, 7 bottles of beer on the wall. 283 | 284 | 7 bottles of beer on the wall, 7 bottles of beer. 285 | Take one down and pass it around, 6 bottles of beer on the wall. 286 | 287 | 6 bottles of beer on the wall, 6 bottles of beer. 288 | Take one down and pass it around, 5 bottles of beer on the wall. 289 | 290 | 5 bottles of beer on the wall, 5 bottles of beer. 291 | Take one down and pass it around, 4 bottles of beer on the wall. 292 | 293 | 4 bottles of beer on the wall, 4 bottles of beer. 294 | Take one down and pass it around, 3 bottles of beer on the wall. 295 | 296 | 3 bottles of beer on the wall, 3 bottles of beer. 297 | Take one down and pass it around, 2 bottles of beer on the wall. 298 | 299 | 2 bottles of beer on the wall, 2 bottles of beer. 300 | Take one down and pass it around, 1 bottle of beer on the wall. 301 | 302 | 1 bottle of beer on the wall, 1 bottle of beer. 303 | Take it down and pass it around, no more bottles of beer on the wall. 304 | 305 | No more bottles of beer on the wall, no more bottles of beer. 306 | Go to the store and buy some more, 99 bottles of beer on the wall. 307 | ``` 308 | 309 | ## For bonus points 310 | 311 | Did you get the tests passing and the code clean? If you want to, these 312 | are some additional things you could try: 313 | 314 | * Remove as much duplication as you possibly can. 315 | * Optimize for readability, even if it means introducing duplication. 316 | * If you've removed all the duplication, do you have a lot of 317 | conditionals? Try replacing the conditionals with polymorphism, if it 318 | applies in this language. How readable is it? 319 | 320 | Then please share your thoughts in a comment on the submission. Did this 321 | experiment make the code better? Worse? Did you learn anything from it? 322 | 323 | ## Running tests 324 | 325 | Execute the tests with: 326 | 327 | ```bash 328 | $ elixir bob_test.exs 329 | ``` 330 | 331 | (Replace `bob_test.exs` with the name of the test file.) 332 | 333 | 334 | ### Pending tests 335 | 336 | In the test suites, all but the first test have been skipped. 337 | 338 | Once you get a test passing, you can unskip the next one by 339 | commenting out the relevant `@tag :pending` with a `#` symbol. 340 | 341 | For example: 342 | 343 | ```elixir 344 | # @tag :pending 345 | test "shouting" do 346 | assert Bob.hey("WATCH OUT!") == "Whoa, chill out!" 347 | end 348 | ``` 349 | 350 | Or, you can enable all the tests by commenting out the 351 | `ExUnit.configure` line in the test suite. 352 | 353 | ```elixir 354 | # ExUnit.configure exclude: :pending, trace: true 355 | ``` 356 | 357 | For more detailed information about the Elixir track, please 358 | see the [help page](http://exercism.io/languages/elixir). 359 | 360 | ## Source 361 | 362 | Learn to Program by Chris Pine [http://pine.fm/LearnToProgram/?Chapter=06](http://pine.fm/LearnToProgram/?Chapter=06) 363 | -------------------------------------------------------------------------------- /beer-song/beer_song.exs: -------------------------------------------------------------------------------- 1 | defmodule BeerSong do 2 | @doc """ 3 | Get a single verse of the beer song 4 | """ 5 | @spec verse(integer) :: String.t 6 | 7 | def verse(1) do 8 | "No more bottles of beer on the wall, no more bottles of beer.\nGo to the store and buy some more, 99 bottles of beer on the wall.\n" 9 | end 10 | 11 | def verse(2) do 12 | "1 bottle of beer on the wall, 1 bottle of beer.\nTake it down and pass it around, no more bottles of beer on the wall.\n" 13 | end 14 | 15 | def verse(3) do 16 | "2 bottles of beer on the wall, 2 bottles of beer.\nTake one down and pass it around, 1 bottle of beer on the wall.\n" 17 | end 18 | 19 | def verse(number) do 20 | "#{number-1} bottles of beer on the wall, #{number-1} bottles of beer.\nTake one down and pass it around, #{number-2} bottles of beer on the wall.\n" 21 | end 22 | 23 | @doc """ 24 | Get the entire beer song for a given range of numbers of bottles. 25 | """ 26 | @spec lyrics(Range.t) :: String.t 27 | def lyrics(range \\ 100..1) do 28 | Enum.map(range, &(verse(&1))) 29 | |> Enum.join("\n") 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /beer-song/beer_song_test.exs: -------------------------------------------------------------------------------- 1 | if !System.get_env("EXERCISM_TEST_EXAMPLES") do 2 | Code.load_file("beer_song.exs") 3 | end 4 | 5 | ExUnit.start 6 | ExUnit.configure exclude: :pending, trace: true 7 | 8 | defmodule BeerSongTest do 9 | use ExUnit.Case 10 | 11 | test "getting just the 100th verse" do 12 | assert BeerSong.verse(100) == """ 13 | 99 bottles of beer on the wall, 99 bottles of beer. 14 | Take one down and pass it around, 98 bottles of beer on the wall. 15 | """ 16 | end 17 | 18 | test "getting just the 99th verse" do 19 | assert BeerSong.verse(99) == """ 20 | 98 bottles of beer on the wall, 98 bottles of beer. 21 | Take one down and pass it around, 97 bottles of beer on the wall. 22 | """ 23 | end 24 | 25 | test "getting just the 2nd verse" do 26 | assert BeerSong.verse(2) == """ 27 | 1 bottle of beer on the wall, 1 bottle of beer. 28 | Take it down and pass it around, no more bottles of beer on the wall. 29 | """ 30 | end 31 | 32 | test "getting just the 1st verse" do 33 | assert BeerSong.verse(1) == """ 34 | No more bottles of beer on the wall, no more bottles of beer. 35 | Go to the store and buy some more, 99 bottles of beer on the wall. 36 | """ 37 | end 38 | 39 | test "getting the last 4 verses" do 40 | assert BeerSong.lyrics(4..1) == """ 41 | 3 bottles of beer on the wall, 3 bottles of beer. 42 | Take one down and pass it around, 2 bottles of beer on the wall. 43 | 44 | 2 bottles of beer on the wall, 2 bottles of beer. 45 | Take one down and pass it around, 1 bottle of beer on the wall. 46 | 47 | 1 bottle of beer on the wall, 1 bottle of beer. 48 | Take it down and pass it around, no more bottles of beer on the wall. 49 | 50 | No more bottles of beer on the wall, no more bottles of beer. 51 | Go to the store and buy some more, 99 bottles of beer on the wall. 52 | """ 53 | end 54 | 55 | test "getting the whole song" do 56 | assert BeerSong.lyrics == """ 57 | 99 bottles of beer on the wall, 99 bottles of beer. 58 | Take one down and pass it around, 98 bottles of beer on the wall. 59 | 60 | 98 bottles of beer on the wall, 98 bottles of beer. 61 | Take one down and pass it around, 97 bottles of beer on the wall. 62 | 63 | 97 bottles of beer on the wall, 97 bottles of beer. 64 | Take one down and pass it around, 96 bottles of beer on the wall. 65 | 66 | 96 bottles of beer on the wall, 96 bottles of beer. 67 | Take one down and pass it around, 95 bottles of beer on the wall. 68 | 69 | 95 bottles of beer on the wall, 95 bottles of beer. 70 | Take one down and pass it around, 94 bottles of beer on the wall. 71 | 72 | 94 bottles of beer on the wall, 94 bottles of beer. 73 | Take one down and pass it around, 93 bottles of beer on the wall. 74 | 75 | 93 bottles of beer on the wall, 93 bottles of beer. 76 | Take one down and pass it around, 92 bottles of beer on the wall. 77 | 78 | 92 bottles of beer on the wall, 92 bottles of beer. 79 | Take one down and pass it around, 91 bottles of beer on the wall. 80 | 81 | 91 bottles of beer on the wall, 91 bottles of beer. 82 | Take one down and pass it around, 90 bottles of beer on the wall. 83 | 84 | 90 bottles of beer on the wall, 90 bottles of beer. 85 | Take one down and pass it around, 89 bottles of beer on the wall. 86 | 87 | 89 bottles of beer on the wall, 89 bottles of beer. 88 | Take one down and pass it around, 88 bottles of beer on the wall. 89 | 90 | 88 bottles of beer on the wall, 88 bottles of beer. 91 | Take one down and pass it around, 87 bottles of beer on the wall. 92 | 93 | 87 bottles of beer on the wall, 87 bottles of beer. 94 | Take one down and pass it around, 86 bottles of beer on the wall. 95 | 96 | 86 bottles of beer on the wall, 86 bottles of beer. 97 | Take one down and pass it around, 85 bottles of beer on the wall. 98 | 99 | 85 bottles of beer on the wall, 85 bottles of beer. 100 | Take one down and pass it around, 84 bottles of beer on the wall. 101 | 102 | 84 bottles of beer on the wall, 84 bottles of beer. 103 | Take one down and pass it around, 83 bottles of beer on the wall. 104 | 105 | 83 bottles of beer on the wall, 83 bottles of beer. 106 | Take one down and pass it around, 82 bottles of beer on the wall. 107 | 108 | 82 bottles of beer on the wall, 82 bottles of beer. 109 | Take one down and pass it around, 81 bottles of beer on the wall. 110 | 111 | 81 bottles of beer on the wall, 81 bottles of beer. 112 | Take one down and pass it around, 80 bottles of beer on the wall. 113 | 114 | 80 bottles of beer on the wall, 80 bottles of beer. 115 | Take one down and pass it around, 79 bottles of beer on the wall. 116 | 117 | 79 bottles of beer on the wall, 79 bottles of beer. 118 | Take one down and pass it around, 78 bottles of beer on the wall. 119 | 120 | 78 bottles of beer on the wall, 78 bottles of beer. 121 | Take one down and pass it around, 77 bottles of beer on the wall. 122 | 123 | 77 bottles of beer on the wall, 77 bottles of beer. 124 | Take one down and pass it around, 76 bottles of beer on the wall. 125 | 126 | 76 bottles of beer on the wall, 76 bottles of beer. 127 | Take one down and pass it around, 75 bottles of beer on the wall. 128 | 129 | 75 bottles of beer on the wall, 75 bottles of beer. 130 | Take one down and pass it around, 74 bottles of beer on the wall. 131 | 132 | 74 bottles of beer on the wall, 74 bottles of beer. 133 | Take one down and pass it around, 73 bottles of beer on the wall. 134 | 135 | 73 bottles of beer on the wall, 73 bottles of beer. 136 | Take one down and pass it around, 72 bottles of beer on the wall. 137 | 138 | 72 bottles of beer on the wall, 72 bottles of beer. 139 | Take one down and pass it around, 71 bottles of beer on the wall. 140 | 141 | 71 bottles of beer on the wall, 71 bottles of beer. 142 | Take one down and pass it around, 70 bottles of beer on the wall. 143 | 144 | 70 bottles of beer on the wall, 70 bottles of beer. 145 | Take one down and pass it around, 69 bottles of beer on the wall. 146 | 147 | 69 bottles of beer on the wall, 69 bottles of beer. 148 | Take one down and pass it around, 68 bottles of beer on the wall. 149 | 150 | 68 bottles of beer on the wall, 68 bottles of beer. 151 | Take one down and pass it around, 67 bottles of beer on the wall. 152 | 153 | 67 bottles of beer on the wall, 67 bottles of beer. 154 | Take one down and pass it around, 66 bottles of beer on the wall. 155 | 156 | 66 bottles of beer on the wall, 66 bottles of beer. 157 | Take one down and pass it around, 65 bottles of beer on the wall. 158 | 159 | 65 bottles of beer on the wall, 65 bottles of beer. 160 | Take one down and pass it around, 64 bottles of beer on the wall. 161 | 162 | 64 bottles of beer on the wall, 64 bottles of beer. 163 | Take one down and pass it around, 63 bottles of beer on the wall. 164 | 165 | 63 bottles of beer on the wall, 63 bottles of beer. 166 | Take one down and pass it around, 62 bottles of beer on the wall. 167 | 168 | 62 bottles of beer on the wall, 62 bottles of beer. 169 | Take one down and pass it around, 61 bottles of beer on the wall. 170 | 171 | 61 bottles of beer on the wall, 61 bottles of beer. 172 | Take one down and pass it around, 60 bottles of beer on the wall. 173 | 174 | 60 bottles of beer on the wall, 60 bottles of beer. 175 | Take one down and pass it around, 59 bottles of beer on the wall. 176 | 177 | 59 bottles of beer on the wall, 59 bottles of beer. 178 | Take one down and pass it around, 58 bottles of beer on the wall. 179 | 180 | 58 bottles of beer on the wall, 58 bottles of beer. 181 | Take one down and pass it around, 57 bottles of beer on the wall. 182 | 183 | 57 bottles of beer on the wall, 57 bottles of beer. 184 | Take one down and pass it around, 56 bottles of beer on the wall. 185 | 186 | 56 bottles of beer on the wall, 56 bottles of beer. 187 | Take one down and pass it around, 55 bottles of beer on the wall. 188 | 189 | 55 bottles of beer on the wall, 55 bottles of beer. 190 | Take one down and pass it around, 54 bottles of beer on the wall. 191 | 192 | 54 bottles of beer on the wall, 54 bottles of beer. 193 | Take one down and pass it around, 53 bottles of beer on the wall. 194 | 195 | 53 bottles of beer on the wall, 53 bottles of beer. 196 | Take one down and pass it around, 52 bottles of beer on the wall. 197 | 198 | 52 bottles of beer on the wall, 52 bottles of beer. 199 | Take one down and pass it around, 51 bottles of beer on the wall. 200 | 201 | 51 bottles of beer on the wall, 51 bottles of beer. 202 | Take one down and pass it around, 50 bottles of beer on the wall. 203 | 204 | 50 bottles of beer on the wall, 50 bottles of beer. 205 | Take one down and pass it around, 49 bottles of beer on the wall. 206 | 207 | 49 bottles of beer on the wall, 49 bottles of beer. 208 | Take one down and pass it around, 48 bottles of beer on the wall. 209 | 210 | 48 bottles of beer on the wall, 48 bottles of beer. 211 | Take one down and pass it around, 47 bottles of beer on the wall. 212 | 213 | 47 bottles of beer on the wall, 47 bottles of beer. 214 | Take one down and pass it around, 46 bottles of beer on the wall. 215 | 216 | 46 bottles of beer on the wall, 46 bottles of beer. 217 | Take one down and pass it around, 45 bottles of beer on the wall. 218 | 219 | 45 bottles of beer on the wall, 45 bottles of beer. 220 | Take one down and pass it around, 44 bottles of beer on the wall. 221 | 222 | 44 bottles of beer on the wall, 44 bottles of beer. 223 | Take one down and pass it around, 43 bottles of beer on the wall. 224 | 225 | 43 bottles of beer on the wall, 43 bottles of beer. 226 | Take one down and pass it around, 42 bottles of beer on the wall. 227 | 228 | 42 bottles of beer on the wall, 42 bottles of beer. 229 | Take one down and pass it around, 41 bottles of beer on the wall. 230 | 231 | 41 bottles of beer on the wall, 41 bottles of beer. 232 | Take one down and pass it around, 40 bottles of beer on the wall. 233 | 234 | 40 bottles of beer on the wall, 40 bottles of beer. 235 | Take one down and pass it around, 39 bottles of beer on the wall. 236 | 237 | 39 bottles of beer on the wall, 39 bottles of beer. 238 | Take one down and pass it around, 38 bottles of beer on the wall. 239 | 240 | 38 bottles of beer on the wall, 38 bottles of beer. 241 | Take one down and pass it around, 37 bottles of beer on the wall. 242 | 243 | 37 bottles of beer on the wall, 37 bottles of beer. 244 | Take one down and pass it around, 36 bottles of beer on the wall. 245 | 246 | 36 bottles of beer on the wall, 36 bottles of beer. 247 | Take one down and pass it around, 35 bottles of beer on the wall. 248 | 249 | 35 bottles of beer on the wall, 35 bottles of beer. 250 | Take one down and pass it around, 34 bottles of beer on the wall. 251 | 252 | 34 bottles of beer on the wall, 34 bottles of beer. 253 | Take one down and pass it around, 33 bottles of beer on the wall. 254 | 255 | 33 bottles of beer on the wall, 33 bottles of beer. 256 | Take one down and pass it around, 32 bottles of beer on the wall. 257 | 258 | 32 bottles of beer on the wall, 32 bottles of beer. 259 | Take one down and pass it around, 31 bottles of beer on the wall. 260 | 261 | 31 bottles of beer on the wall, 31 bottles of beer. 262 | Take one down and pass it around, 30 bottles of beer on the wall. 263 | 264 | 30 bottles of beer on the wall, 30 bottles of beer. 265 | Take one down and pass it around, 29 bottles of beer on the wall. 266 | 267 | 29 bottles of beer on the wall, 29 bottles of beer. 268 | Take one down and pass it around, 28 bottles of beer on the wall. 269 | 270 | 28 bottles of beer on the wall, 28 bottles of beer. 271 | Take one down and pass it around, 27 bottles of beer on the wall. 272 | 273 | 27 bottles of beer on the wall, 27 bottles of beer. 274 | Take one down and pass it around, 26 bottles of beer on the wall. 275 | 276 | 26 bottles of beer on the wall, 26 bottles of beer. 277 | Take one down and pass it around, 25 bottles of beer on the wall. 278 | 279 | 25 bottles of beer on the wall, 25 bottles of beer. 280 | Take one down and pass it around, 24 bottles of beer on the wall. 281 | 282 | 24 bottles of beer on the wall, 24 bottles of beer. 283 | Take one down and pass it around, 23 bottles of beer on the wall. 284 | 285 | 23 bottles of beer on the wall, 23 bottles of beer. 286 | Take one down and pass it around, 22 bottles of beer on the wall. 287 | 288 | 22 bottles of beer on the wall, 22 bottles of beer. 289 | Take one down and pass it around, 21 bottles of beer on the wall. 290 | 291 | 21 bottles of beer on the wall, 21 bottles of beer. 292 | Take one down and pass it around, 20 bottles of beer on the wall. 293 | 294 | 20 bottles of beer on the wall, 20 bottles of beer. 295 | Take one down and pass it around, 19 bottles of beer on the wall. 296 | 297 | 19 bottles of beer on the wall, 19 bottles of beer. 298 | Take one down and pass it around, 18 bottles of beer on the wall. 299 | 300 | 18 bottles of beer on the wall, 18 bottles of beer. 301 | Take one down and pass it around, 17 bottles of beer on the wall. 302 | 303 | 17 bottles of beer on the wall, 17 bottles of beer. 304 | Take one down and pass it around, 16 bottles of beer on the wall. 305 | 306 | 16 bottles of beer on the wall, 16 bottles of beer. 307 | Take one down and pass it around, 15 bottles of beer on the wall. 308 | 309 | 15 bottles of beer on the wall, 15 bottles of beer. 310 | Take one down and pass it around, 14 bottles of beer on the wall. 311 | 312 | 14 bottles of beer on the wall, 14 bottles of beer. 313 | Take one down and pass it around, 13 bottles of beer on the wall. 314 | 315 | 13 bottles of beer on the wall, 13 bottles of beer. 316 | Take one down and pass it around, 12 bottles of beer on the wall. 317 | 318 | 12 bottles of beer on the wall, 12 bottles of beer. 319 | Take one down and pass it around, 11 bottles of beer on the wall. 320 | 321 | 11 bottles of beer on the wall, 11 bottles of beer. 322 | Take one down and pass it around, 10 bottles of beer on the wall. 323 | 324 | 10 bottles of beer on the wall, 10 bottles of beer. 325 | Take one down and pass it around, 9 bottles of beer on the wall. 326 | 327 | 9 bottles of beer on the wall, 9 bottles of beer. 328 | Take one down and pass it around, 8 bottles of beer on the wall. 329 | 330 | 8 bottles of beer on the wall, 8 bottles of beer. 331 | Take one down and pass it around, 7 bottles of beer on the wall. 332 | 333 | 7 bottles of beer on the wall, 7 bottles of beer. 334 | Take one down and pass it around, 6 bottles of beer on the wall. 335 | 336 | 6 bottles of beer on the wall, 6 bottles of beer. 337 | Take one down and pass it around, 5 bottles of beer on the wall. 338 | 339 | 5 bottles of beer on the wall, 5 bottles of beer. 340 | Take one down and pass it around, 4 bottles of beer on the wall. 341 | 342 | 4 bottles of beer on the wall, 4 bottles of beer. 343 | Take one down and pass it around, 3 bottles of beer on the wall. 344 | 345 | 3 bottles of beer on the wall, 3 bottles of beer. 346 | Take one down and pass it around, 2 bottles of beer on the wall. 347 | 348 | 2 bottles of beer on the wall, 2 bottles of beer. 349 | Take one down and pass it around, 1 bottle of beer on the wall. 350 | 351 | 1 bottle of beer on the wall, 1 bottle of beer. 352 | Take it down and pass it around, no more bottles of beer on the wall. 353 | 354 | No more bottles of beer on the wall, no more bottles of beer. 355 | Go to the store and buy some more, 99 bottles of beer on the wall. 356 | """ 357 | end 358 | end 359 | -------------------------------------------------------------------------------- /bob/README.md: -------------------------------------------------------------------------------- 1 | # Bob 2 | 3 | Bob is a lackadaisical teenager. In conversation, his responses are very limited. 4 | 5 | Bob answers 'Sure.' if you ask him a question. 6 | 7 | He answers 'Whoa, chill out!' if you yell at him. 8 | 9 | He says 'Fine. Be that way!' if you address him without actually saying 10 | anything. 11 | 12 | He answers 'Whatever.' to anything else. 13 | 14 | ## Instructions 15 | 16 | Run the test file, and fix each of the errors in turn. When you get the 17 | first test to pass, go to the first pending or skipped test, and make 18 | that pass as well. When all of the tests are passing, feel free to 19 | submit. 20 | 21 | Remember that passing code is just the first step. The goal is to work 22 | towards a solution that is as readable and expressive as you can make 23 | it. 24 | 25 | Please make your solution as general as possible. Good code doesn't just 26 | pass the test suite, it works with any input that fits the 27 | specification. 28 | 29 | Have fun! 30 | 31 | 32 | ## Running tests 33 | 34 | ```bash 35 | $ elixir bob_test.exs 36 | ``` 37 | 38 | (Replace `bob_test.exs` with the name of the test file.) 39 | 40 | ## Source 41 | 42 | Inspired by the 'Deaf Grandma' exercise in Chris Pine's Learn to Program tutorial. [view source](http://pine.fm/LearnToProgram/?Chapter=06) 43 | -------------------------------------------------------------------------------- /bob/bob.exs: -------------------------------------------------------------------------------- 1 | defmodule InputType do 2 | 3 | def nothing?(input) do 4 | String.strip(input) == "" 5 | end 6 | 7 | def question?(input) do 8 | String.ends_with?(input, "?") 9 | end 10 | 11 | def yelling?(input) do 12 | (String.upcase(input) == input) && (String.match?(input, ~r/\p{L}/)) 13 | end 14 | 15 | end 16 | 17 | defmodule Teenager do 18 | import InputType, only: [nothing?: 1, question?: 1, yelling?: 1] 19 | 20 | def hey(input) do 21 | cond do 22 | nothing?(input) -> "Fine. Be that way!" 23 | question?(input) -> "Sure." 24 | yelling?(input) -> "Whoa, chill out!" 25 | true -> "Whatever." 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /bob/bob_test.exs: -------------------------------------------------------------------------------- 1 | if System.get_env("EXERCISM_TEST_EXAMPLES") do 2 | Code.load_file("example.exs") 3 | else 4 | Code.load_file("bob.exs") 5 | end 6 | 7 | ExUnit.start 8 | ExUnit.configure(exclude: :pending) 9 | 10 | defmodule TeenagerTest do 11 | use ExUnit.Case, async: true 12 | 13 | test "stating something" do 14 | assert Teenager.hey("Tom-ay-to, tom-aaaah-to.") == "Whatever." 15 | end 16 | 17 | @tag :pending 18 | test "shouting" do 19 | assert Teenager.hey("WATCH OUT!") == "Whoa, chill out!" 20 | end 21 | 22 | @tag :pending 23 | test "asking a question" do 24 | assert Teenager.hey("Does this cryogenic chamber make me look fat?") == "Sure." 25 | end 26 | 27 | @tag :pending 28 | test "talking forcefully" do 29 | assert Teenager.hey("Let's go make out behind the gym!") == "Whatever." 30 | end 31 | 32 | @tag :pending 33 | test "talking in capitals" do 34 | assert Teenager.hey("This Isn't Shouting!") == "Whatever." 35 | end 36 | 37 | @tag :pending 38 | test "shouting numbers" do 39 | assert Teenager.hey("1, 2, 3 GO!") == "Whoa, chill out!" 40 | end 41 | 42 | @tag :pending 43 | test "shouting with special characters" do 44 | assert Teenager.hey("ZOMG THE %^*@#$(*^ ZOMBIES ARE COMING!!11!!1!") == "Whoa, chill out!" 45 | end 46 | 47 | @tag :pending 48 | test "shouting with no exclamation mark" do 49 | assert Teenager.hey("I HATE YOU") == "Whoa, chill out!" 50 | end 51 | 52 | @tag :pending 53 | test "statement containing question mark" do 54 | assert Teenager.hey("Ending with ? means a question.") == "Whatever." 55 | end 56 | 57 | @tag :pending 58 | test "silence" do 59 | assert Teenager.hey("") == "Fine. Be that way!" 60 | end 61 | 62 | @tag :pending 63 | test "prolonged silence" do 64 | assert Teenager.hey(" ") == "Fine. Be that way!" 65 | end 66 | 67 | @tag :pending 68 | test "only numbers" do 69 | assert Teenager.hey("1, 2, 3") == "Whatever." 70 | end 71 | 72 | @tag :pending 73 | test "question with numbers" do 74 | assert Teenager.hey("4?") == "Sure." 75 | end 76 | 77 | @tag :pending 78 | test "shouting in Russian" do 79 | # Hopefully this is Russian for "get out" 80 | assert Teenager.hey("УХОДИТЬ") == "Whoa, chill out!" 81 | end 82 | end 83 | -------------------------------------------------------------------------------- /bracket-push/README.md: -------------------------------------------------------------------------------- 1 | # Bracket Push 2 | 3 | Make sure the brackets and braces all match. 4 | 5 | Ensure that all the brackets and braces are matched correctly, 6 | and nested correctly. 7 | 8 | ## Running tests 9 | 10 | Execute the tests with: 11 | 12 | ```bash 13 | $ elixir bob_test.exs 14 | ``` 15 | 16 | (Replace `bob_test.exs` with the name of the test file.) 17 | 18 | 19 | ### Pending tests 20 | 21 | In the test suites, all but the first test have been skipped. 22 | 23 | Once you get a test passing, you can unskip the next one by 24 | commenting out the relevant `@tag :pending` with a `#` symbol. 25 | 26 | For example: 27 | 28 | ```elixir 29 | # @tag :pending 30 | test "shouting" do 31 | assert Bob.hey("WATCH OUT!") == "Whoa, chill out!" 32 | end 33 | ``` 34 | 35 | Or, you can enable all the tests by commenting out the 36 | `ExUnit.configure` line in the test suite. 37 | 38 | ```elixir 39 | # ExUnit.configure exclude: :pending, trace: true 40 | ``` 41 | 42 | For more detailed information about the Elixir track, please 43 | see the [help page](http://exercism.io/languages/elixir). 44 | 45 | ## Source 46 | 47 | Ginna Baker 48 | -------------------------------------------------------------------------------- /bracket-push/bracket_push.exs: -------------------------------------------------------------------------------- 1 | defmodule BracketPush do 2 | @brackets %{ 3 | "(" => ")", 4 | "[" => "]", 5 | "{" => "}" 6 | } 7 | 8 | @doc """ 9 | Checks that all the brackets and braces in the string are matched correctly, and nested correctly 10 | """ 11 | @spec check_brackets(String.t) :: boolean 12 | def check_brackets(str) do 13 | str 14 | |> string_with_only_brackets 15 | |> String.codepoints 16 | |> do_check_brackets([]) 17 | end 18 | 19 | defp string_with_only_brackets(str) do 20 | String.replace(str, ~r/[^\p{Ps}\p{Pe}]/, "") 21 | end 22 | 23 | defp do_check_brackets([], []), do: true 24 | defp do_check_brackets([], _), do: false 25 | defp do_check_brackets([h|t], acc) do 26 | cond do 27 | Map.has_key?(@brackets, h) -> 28 | do_check_brackets(t, [@brackets[h]|acc]) 29 | !Map.has_key?(@brackets, h) && Enum.empty?(acc) -> 30 | false 31 | h != hd(acc) -> 32 | false 33 | h == hd(acc) -> 34 | do_check_brackets(t, tl(acc)) 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /bracket-push/bracket_push_test.exs: -------------------------------------------------------------------------------- 1 | if !System.get_env("EXERCISM_TEST_EXAMPLES") do 2 | Code.load_file("bracket_push.exs") 3 | end 4 | 5 | ExUnit.start 6 | ExUnit.configure exclude: :pending, trace: true 7 | 8 | defmodule BracketPushTest do 9 | use ExUnit.Case 10 | 11 | test "empty string" do 12 | assert BracketPush.check_brackets("") 13 | end 14 | 15 | test "appropriate bracketing in a set of brackets" do 16 | assert BracketPush.check_brackets("{}") 17 | end 18 | 19 | test "unclosed brackets" do 20 | refute BracketPush.check_brackets("{{") 21 | end 22 | 23 | test "more than one pair of brackets" do 24 | assert BracketPush.check_brackets("{}[]") 25 | end 26 | 27 | test "brackets are out of order" do 28 | refute BracketPush.check_brackets("}{") 29 | end 30 | 31 | test "nested brackets" do 32 | assert BracketPush.check_brackets("{[()]}") 33 | end 34 | 35 | test "unbalanced nested brackets" do 36 | refute BracketPush.check_brackets("{[}]") 37 | end 38 | 39 | test "bracket closure with deeper nesting" do 40 | refute BracketPush.check_brackets("{[)][]}") 41 | end 42 | 43 | test "bracket closure in a long string of brackets" do 44 | assert BracketPush.check_brackets("{[]([()])}") 45 | end 46 | 47 | test "should ignore non-bracket characters" do 48 | assert BracketPush.check_brackets("{hello[]([a()])b}c") 49 | end 50 | 51 | test "string with newlines" do 52 | assert BracketPush.check_brackets("[]\n{()}\n[(({}))]\n") 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /hamming/README.md: -------------------------------------------------------------------------------- 1 | # Hamming 2 | 3 | Write a program that can calculate the Hamming difference between two DNA strands. 4 | 5 | A mutation is simply a mistake that occurs during the creation or 6 | copying of a nucleic acid, in particular DNA. Because nucleic acids are 7 | vital to cellular functions, mutations tend to cause a ripple effect 8 | throughout the cell. Although mutations are technically mistakes, a very 9 | rare mutation may equip the cell with a beneficial attribute. In fact, 10 | the macro effects of evolution are attributable by the accumulated 11 | result of beneficial microscopic mutations over many generations. 12 | 13 | The simplest and most common type of nucleic acid mutation is a point 14 | mutation, which replaces one base with another at a single nucleotide. 15 | 16 | By counting the number of differences between two homologous DNA strands 17 | taken from different genomes with a common ancestor, we get a measure of 18 | the minimum number of point mutations that could have occurred on the 19 | evolutionary path between the two strands. 20 | 21 | This is called the 'Hamming distance'. 22 | 23 | It is found by comparing two DNA strands and counting how many of the 24 | nucleotides are different from their equivalent in the other string. 25 | 26 | GAGCCTACTAACGGGAT 27 | CATCGTAATGACGGCCT 28 | ^ ^ ^ ^ ^ ^^ 29 | 30 | The Hamming distance between these two DNA strands is 7. 31 | 32 | # Implementation notes 33 | 34 | The Hamming distance is only defined for sequences of equal length. This means 35 | that based on the definition, each language could deal with getting sequences 36 | of equal length differently. 37 | 38 | ## Running tests 39 | 40 | Execute the tests with: 41 | 42 | ```bash 43 | $ elixir bob_test.exs 44 | ``` 45 | 46 | (Replace `bob_test.exs` with the name of the test file.) 47 | 48 | 49 | ### Pending tests 50 | 51 | In the test suites, all but the first test have been skipped. 52 | 53 | Once you get a test passing, you can unskip the next one by 54 | commenting out the relevant `@tag :pending` with a `#` symbol. 55 | 56 | For example: 57 | 58 | ```elixir 59 | # @tag :pending 60 | test "shouting" do 61 | assert Bob.hey("WATCH OUT!") == "Whoa, chill out!" 62 | end 63 | ``` 64 | 65 | Or, you can enable all the tests by commenting out the 66 | `ExUnit.configure` line in the test suite. 67 | 68 | ```elixir 69 | # ExUnit.configure exclude: :pending, trace: true 70 | ``` 71 | 72 | For more detailed information about the Elixir track, please 73 | see the [help page](http://exercism.io/languages/elixir). 74 | 75 | ## Source 76 | 77 | The Calculating Point Mutations problem at Rosalind [http://rosalind.info/problems/hamm/](http://rosalind.info/problems/hamm/) 78 | -------------------------------------------------------------------------------- /hamming/hamming.exs: -------------------------------------------------------------------------------- 1 | defmodule DNA do 2 | @doc """ 3 | Returns number of differences between two strands of DNA, known as the Hamming Distance. 4 | 5 | ## Examples 6 | 7 | iex> DNA.hamming_distance('AAGTCATA', 'TAGCGATC') 8 | 4 9 | """ 10 | @spec hamming_distance([char], [char]) :: non_neg_integer 11 | 12 | def hamming_distance('', ''), do: 0 13 | def hamming_distance(strand, strand), do: 0 14 | def hamming_distance(strand1, strand2) when length(strand1) != length(strand2) do 15 | nil 16 | end 17 | def hamming_distance([h|t1], [h|t2]), do: hamming_distance(t1, t2) 18 | def hamming_distance([h1|t1], [h2|t2]), do: hamming_distance(t1, t2) + 1 19 | end 20 | -------------------------------------------------------------------------------- /hamming/hamming_test.exs: -------------------------------------------------------------------------------- 1 | if !System.get_env("EXERCISM_TEST_EXAMPLES") do 2 | Code.load_file("hamming.exs") 3 | end 4 | 5 | ExUnit.start 6 | ExUnit.configure exclude: :pending, trace: true 7 | 8 | defmodule DNATest do 9 | use ExUnit.Case 10 | 11 | test "no difference between empty strands" do 12 | assert DNA.hamming_distance('', '') == 0 13 | end 14 | 15 | test "no difference between identical strands" do 16 | assert DNA.hamming_distance('GGACTGA', 'GGACTGA') == 0 17 | end 18 | 19 | test "small hamming distance in middle somewhere" do 20 | assert DNA.hamming_distance('GGACG', 'GGTCG') == 1 21 | end 22 | 23 | test "distance with same nucleotides in different locations" do 24 | assert DNA.hamming_distance('TAG', 'GAT') == 2 25 | end 26 | 27 | test "larger distance" do 28 | assert DNA.hamming_distance('ACCAGGG', 'ACTATGG') == 2 29 | end 30 | 31 | test "hamming distance is undefined for strands of different lengths" do 32 | assert DNA.hamming_distance('AAAC', 'TAGGGGAGGCTAGCGGTAGGAC') == nil 33 | assert DNA.hamming_distance('GACTACGGACAGGACACC', 'GACATCGC') == nil 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /list-ops/README.md: -------------------------------------------------------------------------------- 1 | # List Ops 2 | 3 | Implement basic list operations 4 | 5 | In functional languages list operations like `length`, `map`, and 6 | `reduce` are very common. Implement a series of basic list operations, 7 | without using existing functions. 8 | 9 | ## Running tests 10 | 11 | ```bash 12 | $ elixir bob_test.exs 13 | ``` 14 | 15 | (Replace `bob_test.exs` with the name of the test file.) 16 | 17 | ## Source 18 | 19 | [view source]() 20 | -------------------------------------------------------------------------------- /list-ops/list_ops.exs: -------------------------------------------------------------------------------- 1 | defmodule ListOps do 2 | # Please don't use any external modules (especially List) in your 3 | # implementation. The point of this exercise is to create these basic functions 4 | # yourself. 5 | # 6 | # Note that `++` is a function from an external module (Kernel, which is 7 | # automatically imported) and so shouldn't be used either. 8 | 9 | @spec count(list) :: non_neg_integer 10 | def count(l) do 11 | do_count(l, 0) 12 | end 13 | 14 | defp do_count([], n), do: n 15 | 16 | defp do_count([_|tail], n) do 17 | do_count(tail, n + 1) 18 | end 19 | 20 | @spec reverse(list) :: list 21 | def reverse(l) do 22 | do_reverse(l, []) 23 | end 24 | 25 | defp do_reverse([], acc), do: acc 26 | 27 | defp do_reverse([head|tail], acc) do 28 | do_reverse(tail, [head|acc]) 29 | end 30 | 31 | @spec map(list, (any -> any)) :: list 32 | def map(l, f) do 33 | do_map(l, f, []) |> reverse 34 | end 35 | 36 | defp do_map([], _, acc), do: acc 37 | 38 | defp do_map([head|tail], f, acc) do 39 | do_map(tail, f, [f.(head)|acc]) 40 | end 41 | 42 | @spec filter(list, (any -> as_boolean(term))) :: list 43 | def filter(l, f) do 44 | do_filter(l, f, []) |> reverse 45 | end 46 | 47 | defp do_filter([], _, acc), do: acc 48 | 49 | defp do_filter([head|tail], f, acc) do 50 | if f.(head) do 51 | do_filter(tail, f, [head|acc]) 52 | else 53 | do_filter(tail, f, acc) 54 | end 55 | end 56 | 57 | @type acc :: any 58 | @spec reduce(list, acc, ((any, acc) -> acc)) :: acc 59 | def reduce(l, acc, f) do 60 | do_reduce(l, acc, f) 61 | end 62 | 63 | defp do_reduce([], acc, _), do: acc 64 | 65 | defp do_reduce([head|tail], acc, f) do 66 | do_reduce(tail, f.(head, acc), f) 67 | end 68 | 69 | @spec append(list, list) :: list 70 | def append(a, b) do 71 | do_append(reverse(a), b) 72 | end 73 | 74 | defp do_append([], b), do: b 75 | 76 | defp do_append([head|tail], b) do 77 | do_append(tail, [head|b]) 78 | end 79 | 80 | @spec concat([[any]]) :: [any] 81 | def concat(ll) do 82 | reverse(ll) |> reduce([], &(append(&1, &2))) 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /list-ops/list_ops_test.exs: -------------------------------------------------------------------------------- 1 | if System.get_env("EXERCISM_TEST_EXAMPLES") do 2 | Code.load_file("example.exs") 3 | else 4 | Code.load_file("list_ops.exs") 5 | end 6 | 7 | ExUnit.start 8 | 9 | defmodule ListOpsTest do 10 | alias ListOps, as: L 11 | 12 | use ExUnit.Case, async: true 13 | 14 | defp odd?(n), do: rem(n, 2) == 1 15 | 16 | test "count of empty list" do 17 | assert L.count([]) == 0 18 | end 19 | 20 | test "count of normal list" do 21 | assert L.count([1,3,5,7]) == 4 22 | end 23 | 24 | test "count of huge list" do 25 | assert L.count(Enum.to_list(1..1_000_000)) == 1_000_000 26 | end 27 | 28 | test "reverse of empty list" do 29 | assert L.reverse([]) == [] 30 | end 31 | 32 | test "reverse of normal list" do 33 | assert L.reverse([1,3,5,7]) == [7,5,3,1] 34 | end 35 | 36 | test "reverse of huge list" do 37 | assert L.reverse(Enum.to_list(1..1_000_000)) == Enum.to_list(1_000_000..1) 38 | end 39 | 40 | test "map of empty list" do 41 | assert L.map([], &(&1+1)) == [] 42 | end 43 | 44 | test "map of normal list" do 45 | assert L.map([1,3,5,7], &(&1+1)) == [2,4,6,8] 46 | end 47 | 48 | test "map of huge list" do 49 | assert L.map(Enum.to_list(1..1_000_000), &(&1+1)) == 50 | Enum.to_list(2..1_000_001) 51 | end 52 | 53 | test "filter of empty list" do 54 | assert L.filter([], &odd?/1) == [] 55 | end 56 | 57 | test "filter of normal list" do 58 | assert L.filter([1,2,3,4], &odd?/1) == [1,3] 59 | end 60 | 61 | test "filter of huge list" do 62 | assert L.filter(Enum.to_list(1..1_000_000), &odd?/1) == 63 | Enum.map(1..500_000, &(&1*2-1)) 64 | end 65 | 66 | test "reduce of empty list" do 67 | assert L.reduce([], 0, &(&1+&2)) == 0 68 | end 69 | 70 | test "reduce of normal list" do 71 | assert L.reduce([1,2,3,4], -3, &(&1+&2)) == 7 72 | end 73 | 74 | test "reduce of huge list" do 75 | assert L.reduce(Enum.to_list(1..1_000_000), 0, &(&1+&2)) == 76 | Enum.reduce(1..1_000_000, 0, &(&1+&2)) 77 | end 78 | 79 | test "reduce with non-commutative function" do 80 | assert L.reduce([1,2,3,4], 10, fn x, acc -> acc - x end) == 0 81 | end 82 | 83 | test "append of empty lists" do 84 | assert L.append([], []) == [] 85 | end 86 | 87 | test "append of empty and non-empty list" do 88 | assert L.append([], [1,2,3,4]) == [1,2,3,4] 89 | end 90 | 91 | test "append of non-empty and empty list" do 92 | assert L.append([1,2,3,4], []) == [1,2,3,4] 93 | end 94 | 95 | test "append of non-empty lists" do 96 | assert L.append([1,2,3], [4,5]) == [1,2,3,4,5] 97 | end 98 | 99 | test "append of huge lists" do 100 | assert L.append(Enum.to_list(1..1_000_000), Enum.to_list(1_000_001..2_000_000)) == 101 | Enum.to_list(1..2_000_000) 102 | end 103 | 104 | test "concat of empty list of lists" do 105 | assert L.concat([]) == [] 106 | end 107 | 108 | test "concat of normal list of lists" do 109 | assert L.concat([[1,2],[3],[],[4,5,6]]) == [1,2,3,4,5,6] 110 | end 111 | 112 | test "concat of huge list of small lists" do 113 | assert L.concat(Enum.map(1..1_000_000, &[&1])) == 114 | Enum.to_list(1..1_000_000) 115 | end 116 | 117 | test "concat of small list of huge lists" do 118 | assert L.concat(Enum.map(0..9, &Enum.to_list((&1*100_000+1)..((&1+1)*100_000)))) == 119 | Enum.to_list(1..1_000_000) 120 | end 121 | end 122 | -------------------------------------------------------------------------------- /nucleotide-count/README.md: -------------------------------------------------------------------------------- 1 | # Nucleotide Count 2 | 3 | Given a DNA string, compute how many times each nucleotide occurs in the string. 4 | 5 | DNA is represented by an alphabet of the following symbols: 'A', 'C', 6 | 'G', and 'T'. 7 | 8 | Each symbol represents a nucleotide, which is a fancy name for the 9 | particular molecules that happen to make up a large part of DNA. 10 | 11 | Shortest intro to biochemistry EVAR: 12 | 13 | - twigs are to birds nests as 14 | - nucleotides are to DNA and RNA as 15 | - amino acids are to proteins as 16 | - sugar is to starch as 17 | - oh crap lipids 18 | 19 | I'm not going to talk about lipids because they're crazy complex. 20 | 21 | So back to nucleotides. 22 | 23 | DNA contains four types of them: adenine (`A`), cytosine (`C`), guanine 24 | (`G`), and thymine (`T`). 25 | 26 | RNA contains a slightly different set of nucleotides, but we don't care 27 | about that for now. 28 | 29 | ## Running tests 30 | 31 | ```bash 32 | $ elixir bob_test.exs 33 | ``` 34 | 35 | (Replace `bob_test.exs` with the name of the test file.) 36 | 37 | ## Source 38 | 39 | The Calculating DNA Nucleotides_problem at Rosalind [view source](http://rosalind.info/problems/dna/) 40 | -------------------------------------------------------------------------------- /nucleotide-count/dna.exs: -------------------------------------------------------------------------------- 1 | defmodule DNA do 2 | @nucleotides [?A, ?C, ?G, ?T] 3 | 4 | def count('', _), do: 0 5 | 6 | @spec count([char], char) :: non_neg_integer 7 | def count(strand, nucleotide) do 8 | Enum.count(strand, &(&1 == nucleotide)) 9 | end 10 | 11 | @spec nucleotide_counts([char]) :: Dict.t 12 | def nucleotide_counts(strand) do 13 | Enum.map(@nucleotides, &{&1, count(strand, &1)}) |> Enum.into(%{}) 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /nucleotide-count/nucleotide_count_test.exs: -------------------------------------------------------------------------------- 1 | if System.get_env("EXERCISM_TEST_EXAMPLES") do 2 | Code.load_file("example.exs") 3 | else 4 | Code.load_file("dna.exs") 5 | end 6 | 7 | ExUnit.start 8 | 9 | defmodule DNATest do 10 | use ExUnit.Case, async: true 11 | 12 | test "empty dna string has no adenosine" do 13 | assert DNA.count('', ?A) == 0 14 | end 15 | 16 | test "empty dna string has no nucleotides" do 17 | expected = %{?A => 0, ?T => 0, ?C => 0, ?G => 0} 18 | assert DNA.nucleotide_counts('') == expected 19 | end 20 | 21 | test "repetitive cytidine gets counted" do 22 | assert DNA.count('CCCCC', ?C) == 5 23 | end 24 | 25 | test "repetitive sequence has only guanosine" do 26 | expected = %{?A => 0, ?T => 0, ?C => 0, ?G => 8} 27 | assert DNA.nucleotide_counts('GGGGGGGG') == expected 28 | end 29 | 30 | test "counts only thymidine" do 31 | assert DNA.count('GGGGGTAACCCGG', ?T) == 1 32 | end 33 | 34 | test "dna has no uracil" do 35 | assert DNA.count('GATTACA', ?U) == 0 36 | end 37 | 38 | test "counts all nucleotides" do 39 | s = 'AGCTTTTCATTCTGACTGCAACGGGCAATATGTCTCTGTGTGGATTAAAAAAAGAGTGTCTGATAGCAGC' 40 | expected = %{?A => 20, ?T => 21, ?C => 12, ?G => 17} 41 | assert DNA.nucleotide_counts(s) == expected 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /phone-number/README.md: -------------------------------------------------------------------------------- 1 | # Phone Number 2 | 3 | Write a program that cleans up user-entered phone numbers so that they can be sent SMS messages. 4 | 5 | The rules are as follows: 6 | 7 | - If the phone number is less than 10 digits assume that it is bad 8 | number 9 | - If the phone number is 10 digits assume that it is good 10 | - If the phone number is 11 digits and the first number is 1, trim the 1 11 | and use the last 10 digits 12 | - If the phone number is 11 digits and the first number is not 1, then 13 | it is a bad number 14 | - If the phone number is more than 11 digits assume that it is a bad 15 | number 16 | 17 | We've provided tests, now make them pass. 18 | 19 | Hint: Only make one test pass at a time. Disable the others, then flip 20 | each on in turn after you get the current failing one to pass. 21 | 22 | ## Running tests 23 | 24 | Execute the tests with: 25 | 26 | ```bash 27 | $ elixir bob_test.exs 28 | ``` 29 | 30 | (Replace `bob_test.exs` with the name of the test file.) 31 | 32 | 33 | ### Pending tests 34 | 35 | In the test suites, all but the first test have been skipped. 36 | 37 | Once you get a test passing, you can unskip the next one by 38 | commenting out the relevant `@tag :pending` with a `#` symbol. 39 | 40 | For example: 41 | 42 | ```elixir 43 | # @tag :pending 44 | test "shouting" do 45 | assert Bob.hey("WATCH OUT!") == "Whoa, chill out!" 46 | end 47 | ``` 48 | 49 | Or, you can enable all the tests by commenting out the 50 | `ExUnit.configure` line in the test suite. 51 | 52 | ```elixir 53 | # ExUnit.configure exclude: :pending, trace: true 54 | ``` 55 | 56 | For more detailed information about the Elixir track, please 57 | see the [help page](http://exercism.io/languages/elixir). 58 | 59 | ## Source 60 | 61 | Event Manager by JumpstartLab [http://tutorials.jumpstartlab.com/projects/eventmanager.html](http://tutorials.jumpstartlab.com/projects/eventmanager.html) 62 | -------------------------------------------------------------------------------- /phone-number/phone_number.exs: -------------------------------------------------------------------------------- 1 | defmodule Phone do 2 | 3 | @bad_number "0000000000" 4 | 5 | @doc """ 6 | Remove formatting from a phone number. 7 | 8 | Returns "0000000000" if phone number is not valid 9 | (10 digits or "1" followed by 10 digits) 10 | 11 | ## Examples 12 | 13 | iex> Phone.number("123-456-7890") 14 | "1234567890" 15 | 16 | iex> Phone.number("+1 (303) 555-1212") 17 | "3035551212" 18 | 19 | iex> Phone.number("867.5309") 20 | "0000000000" 21 | """ 22 | @spec number(String.t) :: String.t 23 | def number(raw) do 24 | raw 25 | |> match 26 | |> to_string 27 | end 28 | 29 | defp match(raw) do 30 | Regex.run(~r/^\D*1?(\d{3})\D*(\d{3})\D*(\d{4})$/, raw, [capture: :all_but_first]) || ["000", "000", "0000"] 31 | end 32 | 33 | @doc """ 34 | Extract the area code from a phone number 35 | 36 | Returns the first three digits from a phone number, 37 | ignoring long distance indicator 38 | 39 | ## Examples 40 | 41 | iex> Phone.area_code("123-456-7890") 42 | "123" 43 | 44 | iex> Phone.area_code("+1 (303) 555-1212") 45 | "303" 46 | 47 | iex> Phone.area_code("867.5309") 48 | "000" 49 | """ 50 | @spec area_code(String.t) :: String.t 51 | def area_code(raw) do 52 | raw 53 | |> match 54 | |> List.first 55 | end 56 | 57 | @doc """ 58 | Pretty print a phone number 59 | 60 | Wraps the area code in parentheses and separates 61 | exchange and subscriber number with a dash. 62 | 63 | ## Examples 64 | 65 | iex> Phone.pretty("123-456-7890") 66 | "(123) 456-7890" 67 | 68 | iex> Phone.pretty("+1 (303) 555-1212") 69 | "(303) 555-1212" 70 | 71 | iex> Phone.pretty("867.5309") 72 | "(000) 000-0000" 73 | """ 74 | @spec pretty(String.t) :: String.t 75 | def pretty(raw) do 76 | matched = match(raw) 77 | 78 | "(#{Enum.at(matched, 0)}) #{Enum.at(matched, 1)}-#{Enum.at(matched, 2)}" 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /phone-number/phone_number_test.exs: -------------------------------------------------------------------------------- 1 | if !System.get_env("EXERCISM_TEST_EXAMPLES") do 2 | Code.load_file("phone_number.exs") 3 | end 4 | 5 | ExUnit.start 6 | ExUnit.configure exclude: :pending, trace: true 7 | 8 | defmodule PhoneTest do 9 | use ExUnit.Case 10 | 11 | test "cleans number" do 12 | assert Phone.number("(123) 456-7890") == "1234567890" 13 | end 14 | 15 | test "cleans number with dots" do 16 | assert Phone.number("123.456.7890") == "1234567890" 17 | end 18 | 19 | test "valid when 11 digits and first is 1" do 20 | assert Phone.number("11234567890") == "1234567890" 21 | end 22 | 23 | test "invalid when 11 digits" do 24 | assert Phone.number("21234567890") == "0000000000" 25 | end 26 | 27 | test "invalid when 9 digits" do 28 | assert Phone.number("123456789") == "0000000000" 29 | end 30 | 31 | test "invalid when proper number of digits but letters mixed in" do 32 | assert Phone.number("1a2a3a4a5a6a7a8a9a0a") == "0000000000" 33 | end 34 | 35 | test "area code" do 36 | assert Phone.area_code("1234567890") == "123" 37 | end 38 | 39 | test "area code with full US phone number" do 40 | assert Phone.area_code("11234567890") == "123" 41 | end 42 | 43 | test "pretty print" do 44 | assert Phone.pretty("1234567890") == "(123) 456-7890" 45 | end 46 | 47 | test "pretty print with full US phone number" do 48 | assert Phone.pretty("11234567890") == "(123) 456-7890" 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /rna-transcription/README.md: -------------------------------------------------------------------------------- 1 | # Rna Transcription 2 | 3 | Write a program that, given a DNA strand, returns its RNA complement (per RNA transcription). 4 | 5 | Both DNA and RNA strands are a sequence of nucleotides. 6 | 7 | The four nucleotides found in DNA are adenine (**A**), cytosine (**C**), 8 | guanine (**G**) and thymine (**T**). 9 | 10 | The four nucleotides found in RNA are adenine (**A**), cytosine (**C**), 11 | guanine (**G**) and uracil (**U**). 12 | 13 | Given a DNA strand, its transcribed RNA strand is formed by replacing 14 | each nucleotide with its complement: 15 | 16 | * `G` -> `C` 17 | * `C` -> `G` 18 | * `T` -> `A` 19 | * `A` -> `U` 20 | 21 | ## Running tests 22 | 23 | Execute the tests with: 24 | 25 | ```bash 26 | $ elixir bob_test.exs 27 | ``` 28 | 29 | (Replace `bob_test.exs` with the name of the test file.) 30 | 31 | 32 | ### Pending tests 33 | 34 | In the test suites, all but the first test have been skipped. 35 | 36 | Once you get a test passing, you can unskip the next one by 37 | commenting out the relevant `@tag :pending` with a `#` symbol. 38 | 39 | For example: 40 | 41 | ```elixir 42 | # @tag :pending 43 | test "shouting" do 44 | assert Bob.hey("WATCH OUT!") == "Whoa, chill out!" 45 | end 46 | ``` 47 | 48 | Or, you can enable all the tests by commenting out the 49 | `ExUnit.configure` line in the test suite. 50 | 51 | ```elixir 52 | # ExUnit.configure exclude: :pending, trace: true 53 | ``` 54 | 55 | For more detailed information about the Elixir track, please 56 | see the [help page](http://exercism.io/languages/elixir). 57 | 58 | ## Source 59 | 60 | Rosalind [http://rosalind.info/problems/rna](http://rosalind.info/problems/rna) 61 | -------------------------------------------------------------------------------- /rna-transcription/dna.exs: -------------------------------------------------------------------------------- 1 | defmodule DNA do 2 | @dna_to_rna %{ 3 | ?G => ?C, 4 | ?C => ?G, 5 | ?T => ?A, 6 | ?A => ?U 7 | } 8 | 9 | @doc """ 10 | Transcribes a character list representing DNA nucleotides to RNA 11 | 12 | ## Examples 13 | 14 | iex> DNA.to_rna('ACTG') 15 | 'UGAC' 16 | """ 17 | @spec to_rna([char]) :: [char] 18 | def to_rna(dna) do 19 | Enum.map(dna, &(@dna_to_rna[&1])) 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /rna-transcription/rna_transcription_test.exs: -------------------------------------------------------------------------------- 1 | if !System.get_env("EXERCISM_TEST_EXAMPLES") do 2 | Code.load_file("dna.exs") 3 | end 4 | 5 | ExUnit.start 6 | ExUnit.configure exclude: :pending, trace: true 7 | 8 | defmodule DNATest do 9 | use ExUnit.Case 10 | 11 | test "transcribes guanine to cytosine" do 12 | assert DNA.to_rna('G') == 'C' 13 | end 14 | 15 | test "transcribes cytosine to guanine" do 16 | assert DNA.to_rna('C') == 'G' 17 | end 18 | 19 | test "transcribes thymidine to adenine" do 20 | assert DNA.to_rna('T') == 'A' 21 | end 22 | 23 | test "transcribes adenine to uracil" do 24 | assert DNA.to_rna('A') == 'U' 25 | end 26 | 27 | test "it transcribes all dna nucleotides to rna equivalents" do 28 | assert DNA.to_rna('ACGTGGTCTTAA') == 'UGCACCAGAAUU' 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /run-length-encoding/README.md: -------------------------------------------------------------------------------- 1 | # Run Length Encoding 2 | 3 | Implement run-length encoding and decoding. 4 | 5 | Run-length encoding (RLE) is a simple form of data compression, where runs 6 | (consecutive data elements) are replaced by just one data value and count. 7 | 8 | For example we can represent the original 53 characters with only 13. 9 | 10 | ``` 11 | "WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB" -> "12WB12W3B24WB" 12 | ``` 13 | 14 | RLE allows the original data to be perfectly reconstructed from 15 | the compressed data, which makes it a lossless data compression. 16 | 17 | ``` 18 | "AABCCCDEEEE" -> "2AB3CD4E" -> "AABCCCDEEEE" 19 | ``` 20 | 21 | ## Running tests 22 | 23 | Execute the tests with: 24 | 25 | ```bash 26 | $ elixir bob_test.exs 27 | ``` 28 | 29 | (Replace `bob_test.exs` with the name of the test file.) 30 | 31 | 32 | ### Pending tests 33 | 34 | In the test suites, all but the first test have been skipped. 35 | 36 | Once you get a test passing, you can unskip the next one by 37 | commenting out the relevant `@tag :pending` with a `#` symbol. 38 | 39 | For example: 40 | 41 | ```elixir 42 | # @tag :pending 43 | test "shouting" do 44 | assert Bob.hey("WATCH OUT!") == "Whoa, chill out!" 45 | end 46 | ``` 47 | 48 | Or, you can enable all the tests by commenting out the 49 | `ExUnit.configure` line in the test suite. 50 | 51 | ```elixir 52 | # ExUnit.configure exclude: :pending, trace: true 53 | ``` 54 | 55 | For more detailed information about the Elixir track, please 56 | see the [help page](http://exercism.io/languages/elixir). 57 | 58 | ## Source 59 | 60 | Wikipedia [https://en.wikipedia.org/wiki/Run-length_encoding](https://en.wikipedia.org/wiki/Run-length_encoding) 61 | -------------------------------------------------------------------------------- /run-length-encoding/rle.exs: -------------------------------------------------------------------------------- 1 | defmodule RunLengthEncoder do 2 | @doc """ 3 | Generates a string where consecutive elements are represented as a data value and count. 4 | "HORSE" => "1H1O1R1S1E" 5 | For this example, assume all input are strings, that are all uppercase letters. 6 | It should also be able to reconstruct the data into its original form. 7 | "1H1O1R1S1E" => "HORSE" 8 | """ 9 | @spec encode(String.t) :: String.t 10 | def encode(string) do 11 | string 12 | |> match_string_to_encode 13 | |> do_encode 14 | end 15 | 16 | @spec decode(String.t) :: String.t 17 | def decode(string) do 18 | string 19 | |> match_string_to_decode 20 | |> do_decode 21 | end 22 | 23 | defp match_string_to_encode(string) do 24 | Regex.scan(~r/([A-Z])\1*/, string) 25 | end 26 | 27 | defp do_encode([]), do: "" 28 | defp do_encode(match_list) do 29 | Enum.map_join(match_list, fn ([sequence, char]) -> "#{String.length(sequence)}#{char}" end) 30 | end 31 | 32 | defp match_string_to_decode(string) do 33 | Regex.scan(~r/(\d+)(.)/, string) 34 | end 35 | 36 | defp do_decode(match_list) do 37 | Enum.map_join(match_list, fn ([_, count, char]) -> String.duplicate(char, String.to_integer(count)) end) 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /run-length-encoding/rle_test.exs: -------------------------------------------------------------------------------- 1 | if !System.get_env("EXERCISM_TEST_EXAMPLES") do 2 | Code.load_file("rle.exs") 3 | end 4 | 5 | ExUnit.start 6 | ExUnit.configure exclude: :pending, trace: true 7 | 8 | defmodule RunLengthEncoderTest do 9 | use ExUnit.Case 10 | 11 | test "empty string returns empty" do 12 | assert RunLengthEncoder.encode("") === "" 13 | end 14 | 15 | test "simple string gets encoded" do 16 | assert RunLengthEncoder.encode("AAA") === "3A" 17 | end 18 | 19 | test "more complicated string" do 20 | assert RunLengthEncoder.encode("HORSE") == "1H1O1R1S1E" 21 | end 22 | 23 | test "an even more complex string" do 24 | assert RunLengthEncoder.encode("WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB") === "12W1B12W3B24W1B" 25 | end 26 | 27 | test "it decodes an encoded simple string" do 28 | assert RunLengthEncoder.decode("3A") === "AAA" 29 | end 30 | 31 | test "it decodes a more complicated string" do 32 | assert RunLengthEncoder.decode("12W1B12W3B24W1B") === "WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB" 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /space-age/README.md: -------------------------------------------------------------------------------- 1 | # Space Age 2 | 3 | Write a program that, given an age in seconds, calculates how old someone is in terms of a given planet's solar years. 4 | 5 | Given an age in seconds, calculate how old someone would be on: 6 | 7 | - Earth: orbital period 365.25 Earth days, or 31557600 seconds 8 | - Mercury: orbital period 0.2408467 Earth years 9 | - Venus: orbital period 0.61519726 Earth years 10 | - Mars: orbital period 1.8808158 Earth years 11 | - Jupiter: orbital period 11.862615 Earth years 12 | - Saturn: orbital period 29.447498 Earth years 13 | - Uranus: orbital period 84.016846 Earth years 14 | - Neptune: orbital period 164.79132 Earth years 15 | 16 | So if you were told someone were 1,000,000,000 seconds old, you should 17 | be able to say that they're 31 Earth-years old. 18 | 19 | If you're wondering why Pluto didn't make the cut, go watch [this 20 | youtube video](http://www.youtube.com/watch?v=Z_2gbGXzFbs). 21 | 22 | ## Running tests 23 | 24 | Execute the tests with: 25 | 26 | ```bash 27 | $ elixir bob_test.exs 28 | ``` 29 | 30 | (Replace `bob_test.exs` with the name of the test file.) 31 | 32 | 33 | ### Pending tests 34 | 35 | In the test suites, all but the first test have been skipped. 36 | 37 | Once you get a test passing, you can unskip the next one by 38 | commenting out the relevant `@tag :pending` with a `#` symbol. 39 | 40 | For example: 41 | 42 | ```elixir 43 | # @tag :pending 44 | test "shouting" do 45 | assert Bob.hey("WATCH OUT!") == "Whoa, chill out!" 46 | end 47 | ``` 48 | 49 | Or, you can enable all the tests by commenting out the 50 | `ExUnit.configure` line in the test suite. 51 | 52 | ```elixir 53 | # ExUnit.configure exclude: :pending, trace: true 54 | ``` 55 | 56 | For more detailed information about the Elixir track, please 57 | see the [help page](http://exercism.io/languages/elixir). 58 | 59 | ## Source 60 | 61 | Partially inspired by Chapter 1 in Chris Pine's online Learn to Program tutorial. [http://pine.fm/LearnToProgram/?Chapter=01](http://pine.fm/LearnToProgram/?Chapter=01) 62 | 63 | ## Submitting Incomplete Problems 64 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 65 | 66 | -------------------------------------------------------------------------------- /space-age/space_age.exs: -------------------------------------------------------------------------------- 1 | defmodule SpaceAge do 2 | @type planet :: :mercury | :venus | :earth | :mars | :jupiter 3 | | :saturn | :neptune | :uranus 4 | 5 | @orbital_period %{ 6 | :mercury => 0.2408467, 7 | :venus => 0.61519726, 8 | :mars => 1.8808158, 9 | :jupiter => 11.862615, 10 | :saturn => 29.447498, 11 | :uranus => 84.016846, 12 | :neptune => 164.79132 13 | } 14 | 15 | @doc """ 16 | Return the number of years a person that has lived for 'seconds' seconds is 17 | aged on 'planet'. 18 | """ 19 | @spec age_on(planet, pos_integer) :: float 20 | def age_on(planet, seconds) do 21 | seconds 22 | |> in_earth_years 23 | |> in_planet_solar_years(planet) 24 | end 25 | 26 | defp in_earth_years(seconds), do: seconds / 60 / 60 / 24 / 365.25 27 | defp in_planet_solar_years(earth_years, :earth), do: earth_years 28 | defp in_planet_solar_years(earth_years, planet) do 29 | earth_years / @orbital_period[planet] 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /space-age/space_age_test.exs: -------------------------------------------------------------------------------- 1 | if !System.get_env("EXERCISM_TEST_EXAMPLES") do 2 | Code.load_file("space_age.exs", __DIR__) 3 | end 4 | 5 | ExUnit.start 6 | ExUnit.configure exclude: :pending, trace: true 7 | 8 | # You need to define a SpaceAge module containing a function age_on that given a 9 | # planet (:earth, :saturn, etc) and a number of seconds returns the age in years 10 | # on that planet as a floating point number. 11 | 12 | defmodule SpageAgeTest do 13 | use ExUnit.Case 14 | 15 | test "age on Earth" do 16 | input = 1_000_000_000 17 | assert_in_delta 31.69, SpaceAge.age_on(:earth, input), 0.005 18 | end 19 | 20 | test "age on Mercury" do 21 | input = 2_134_835_688 22 | assert_in_delta 67.65, SpaceAge.age_on(:earth, input), 0.005 23 | assert_in_delta 280.88, SpaceAge.age_on(:mercury, input), 0.005 24 | end 25 | 26 | test "age on Venus" do 27 | input = 189_839_836 28 | assert_in_delta 6.02, SpaceAge.age_on(:earth, input), 0.005 29 | assert_in_delta 9.78, SpaceAge.age_on(:venus, input), 0.005 30 | end 31 | 32 | test "age on Mars" do 33 | input = 2_329_871_239 34 | assert_in_delta 73.83, SpaceAge.age_on(:earth, input), 0.005 35 | assert_in_delta 39.25, SpaceAge.age_on(:mars, input), 0.005 36 | end 37 | 38 | test "age on Jupiter" do 39 | input = 901_876_382 40 | assert_in_delta 28.58, SpaceAge.age_on(:earth, input), 0.005 41 | assert_in_delta 2.41, SpaceAge.age_on(:jupiter, input), 0.005 42 | end 43 | 44 | test "age on Saturn" do 45 | input = 3_000_000_000 46 | assert_in_delta 95.06, SpaceAge.age_on(:earth, input), 0.005 47 | assert_in_delta 3.23, SpaceAge.age_on(:saturn, input), 0.005 48 | end 49 | 50 | test "age on Uranus" do 51 | input = 3_210_123_456 52 | assert_in_delta 101.72, SpaceAge.age_on(:earth, input), 0.005 53 | assert_in_delta 1.21, SpaceAge.age_on(:uranus, input), 0.005 54 | end 55 | 56 | test "age on Neptune" do 57 | input = 8_210_123_456 58 | assert_in_delta 260.16, SpaceAge.age_on(:earth, input), 0.005 59 | assert_in_delta 1.58, SpaceAge.age_on(:neptune, input), 0.005 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /sublist/README.md: -------------------------------------------------------------------------------- 1 | # Sublist 2 | 3 | Write a function to determine if a list is a sublist of another list. 4 | 5 | Write a function that given two lists determines if the first list is 6 | contained within the second list, if the second list is contained within 7 | the first list, if both lists are contained within each other or if none 8 | of these are true. 9 | 10 | Specifically, a list A is a sublist of list B if by dropping 0 or more elements 11 | from the front of B and 0 or more elements from the back of B you get a list 12 | that's completely equal to A. 13 | 14 | Examples: 15 | 16 | * A = [1, 2, 3], B = [1, 2, 3, 4, 5], A is a sublist of B 17 | * A = [3, 4, 5], B = [1, 2, 3, 4, 5], A is a sublist of B 18 | * A = [3, 4], B = [1, 2, 3, 4, 5], A is a sublist of B 19 | * A = [1, 2, 3], B = [1, 2, 3], A is equal to B 20 | * A = [1, 2, 3, 4, 5], B = [2, 3, 4], A is a superlist of B 21 | * A = [1, 2, 4], B = [1, 2, 3, 4, 5], A is not a superlist of, sublist of or equal to B 22 | 23 | ## Running tests 24 | 25 | ```bash 26 | $ elixir bob_test.exs 27 | ``` 28 | 29 | (Replace `bob_test.exs` with the name of the test file.) 30 | 31 | ## Source 32 | 33 | [view source]() 34 | -------------------------------------------------------------------------------- /sublist/sublist.exs: -------------------------------------------------------------------------------- 1 | defmodule Sublist do 2 | @doc """ 3 | Returns whether the first list is a sublist or a superlist of the second list 4 | and if not whether it is equal or unequal to the second list. 5 | """ 6 | def compare(a, b) do 7 | cond do 8 | a === b -> :equal 9 | sublist?(a, b) -> :sublist 10 | sublist?(b, a) -> :superlist 11 | true -> :unequal 12 | end 13 | end 14 | 15 | defp sublist?(a, b) when length(a) <= length(b) do 16 | cond do 17 | a === Enum.take(b, length(a)) -> true 18 | true -> sublist?(a, tl(b)) 19 | end 20 | end 21 | defp sublist?(_, _), do: false 22 | 23 | end 24 | -------------------------------------------------------------------------------- /sublist/sublist_test.exs: -------------------------------------------------------------------------------- 1 | if System.get_env("EXERCISM_TEST_EXAMPLES") do 2 | Code.load_file("example.exs") 3 | else 4 | Code.load_file("sublist.exs") 5 | end 6 | 7 | ExUnit.start 8 | 9 | defmodule SublistTest do 10 | use ExUnit.Case, async: true 11 | 12 | test "empty equals empty" do 13 | assert Sublist.compare([], []) == :equal 14 | end 15 | 16 | test "empty is a sublist of anything" do 17 | assert Sublist.compare([], [nil]) == :sublist 18 | end 19 | 20 | test "anything is a superlist of empty" do 21 | assert Sublist.compare([nil], []) == :superlist 22 | end 23 | 24 | test "1 is not 2" do 25 | assert Sublist.compare([1], [2]) == :unequal 26 | end 27 | 28 | test "comparing massive equal lists" do 29 | l = Enum.to_list(1..1_000_000) 30 | assert Sublist.compare(l, l) == :equal 31 | end 32 | 33 | test "sublist at start" do 34 | assert Sublist.compare([1,2,3],[1,2,3,4,5]) == :sublist 35 | end 36 | 37 | test "sublist in middle" do 38 | assert Sublist.compare([4,3,2],[5,4,3,2,1]) == :sublist 39 | end 40 | 41 | test "sublist at end" do 42 | assert Sublist.compare([3,4,5],[1,2,3,4,5]) == :sublist 43 | end 44 | 45 | test "partially matching sublist at start" do 46 | assert Sublist.compare([1,1,2], [1,1,1,2]) == :sublist 47 | end 48 | 49 | test "sublist early in huge list" do 50 | assert Sublist.compare([3,4,5], Enum.to_list(1..1_000_000)) == :sublist 51 | end 52 | 53 | test "huge sublist not in huge list" do 54 | assert Sublist.compare(Enum.to_list(10..1_000_001), 55 | Enum.to_list(1..1_000_000)) 56 | == :unequal 57 | end 58 | 59 | test "superlist at start" do 60 | assert Sublist.compare([1,2,3,4,5],[1,2,3]) == :superlist 61 | end 62 | 63 | test "superlist in middle" do 64 | assert Sublist.compare([5,4,3,2,1],[4,3,2]) == :superlist 65 | end 66 | 67 | test "superlist at end" do 68 | assert Sublist.compare([1,2,3,4,5],[3,4,5]) == :superlist 69 | end 70 | 71 | test "partially matching superlist at start" do 72 | assert Sublist.compare([1,1,1,2], [1,1,2]) == :superlist 73 | end 74 | 75 | test "superlist early in huge list" do 76 | assert Sublist.compare(Enum.to_list(1..1_000_000), [3,4,5]) == :superlist 77 | end 78 | 79 | test "strict equality needed" do 80 | assert Sublist.compare([1], [1.0, 2]) == :unequal 81 | end 82 | 83 | test "recurring values sublist" do 84 | assert Sublist.compare([1,2,1,2,3], [1,2,3,1,2,1,2,3,2,1]) == :sublist 85 | end 86 | 87 | test "recurring values unequal" do 88 | assert Sublist.compare([1,2,1,2,3], [1,2,3,1,2,3,2,3,2,1]) == :unequal 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /word-count/README.md: -------------------------------------------------------------------------------- 1 | # Word Count 2 | 3 | Write a program that given a phrase can count the occurrences of each word in that phrase. 4 | 5 | For example for the input `"olly olly in come free"` 6 | 7 | ```plain 8 | olly: 2 9 | in: 1 10 | come: 1 11 | free: 1 12 | ``` 13 | 14 | 15 | ## Running tests 16 | 17 | ```bash 18 | $ elixir bob_test.exs 19 | ``` 20 | 21 | (Replace `bob_test.exs` with the name of the test file.) 22 | 23 | ## Source 24 | 25 | The golang tour [view source](http://tour.golang.org) 26 | -------------------------------------------------------------------------------- /word-count/word_count.exs: -------------------------------------------------------------------------------- 1 | defmodule Words do 2 | @doc """ 3 | Count the number of words in the sentence. 4 | 5 | Words are compared case-insensitively. 6 | """ 7 | @spec count(String.t) :: map() 8 | def count(sentence) do 9 | sentence |> 10 | String.downcase |> 11 | remove_punctuation |> 12 | String.split |> 13 | word_counts 14 | end 15 | 16 | defp remove_punctuation(sentence) do 17 | String.replace(sentence, ~r/[!@,:%&\$\^_]/ , " ") 18 | end 19 | 20 | defp word_counts(words) do 21 | 22 | Enum.reduce(words, %{}, fn(word, map) -> 23 | Dict.update(map, word, 1, &(&1 + 1)) 24 | end) 25 | 26 | end 27 | 28 | end 29 | -------------------------------------------------------------------------------- /word-count/word_count_test.exs: -------------------------------------------------------------------------------- 1 | if System.get_env("EXERCISM_TEST_EXAMPLES") do 2 | Code.load_file("example.exs") 3 | else 4 | Code.load_file("word_count.exs") 5 | end 6 | 7 | ExUnit.start 8 | 9 | defmodule WordsTest do 10 | use ExUnit.Case 11 | 12 | test "count one word" do 13 | assert Words.count("word") == %{ "word" => 1 } 14 | end 15 | 16 | test "count one of each" do 17 | expected = %{ "one" => 1 , "of" => 1 , "each" => 1 } 18 | assert Words.count("one of each") == expected 19 | end 20 | 21 | test "count multiple occurrences" do 22 | expected = %{ "one" => 1 , "fish" => 4 , "two" => 1 , "red" => 1 , "blue" => 1 } 23 | assert Words.count("one fish two fish red fish blue fish") == expected 24 | end 25 | 26 | test "ignore punctuation" do 27 | expected = %{"car" => 1, "carpet" => 1, "as" => 1, "java" => 1, "javascript" => 1} 28 | assert Words.count("car : carpet as java : javascript!!&@$%^&") == expected 29 | end 30 | 31 | test "include numbers" do 32 | expected = %{"testing" => 2, "1" => 1, "2" => 1} 33 | assert Words.count("testing, 1, 2 testing") == expected 34 | end 35 | 36 | test "hyphens" do 37 | expected = %{"co-operative" => 1} 38 | assert Words.count("co-operative") == expected 39 | end 40 | 41 | test "ignore underscores" do 42 | expected = %{"two" => 1, "words" => 1} 43 | assert Words.count("two_words") == expected 44 | end 45 | 46 | test "German" do 47 | expected = %{"götterfunken" => 1, "schöner" => 1, "freude" => 1} 48 | assert Words.count("Freude schöner Götterfunken") == expected 49 | end 50 | 51 | test "normalize case" do 52 | expected = %{"go" => 3} 53 | assert Words.count("go Go GO") == expected 54 | end 55 | end 56 | --------------------------------------------------------------------------------