lunchtime elixir

This commit is contained in:
Ben Harris 2018-03-08 13:52:10 -05:00
parent 6864077c0a
commit 035b8a2561
14 changed files with 776 additions and 19 deletions

50
elixir/pangram/README.md Normal file
View File

@ -0,0 +1,50 @@
# Pangram
Determine if a sentence is a pangram. A pangram (Greek: παν γράμμα, pan gramma,
"every letter") is a sentence using every letter of the alphabet at least once.
The best known English pangram is:
> The quick brown fox jumps over the lazy dog.
The alphabet used consists of ASCII letters `a` to `z`, inclusive, and is case
insensitive. Input will not contain non-ASCII symbols.
## Running tests
Execute the tests with:
```bash
$ elixir pangram_test.exs
```
### Pending tests
In the test suites, all but the first test have been skipped.
Once you get a test passing, you can unskip the next one by
commenting out the relevant `@tag :pending` with a `#` symbol.
For example:
```elixir
# @tag :pending
test "shouting" do
assert Bob.hey("WATCH OUT!") == "Whoa, chill out!"
end
```
Or, you can enable all the tests by commenting out the
`ExUnit.configure` line in the test suite.
```elixir
# ExUnit.configure exclude: :pending, trace: true
```
For more detailed information about the Elixir track, please
see the [help page](http://exercism.io/languages/elixir).
## Source
Wikipedia [https://en.wikipedia.org/wiki/Pangram](https://en.wikipedia.org/wiki/Pangram)
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@ -0,0 +1,19 @@
defmodule Pangram do
@doc """
Determines if a word or sentence is a pangram.
A pangram is a sentence using every letter of the alphabet at least once.
Returns a boolean.
## Examples
iex> Pangram.pangram?("the quick brown fox jumps over the lazy dog")
true
"""
@spec pangram?(String.t()) :: boolean
def pangram?(sentence) do
Enum.all?(?a..?z, fn c -> c in String.to_charlist(String.downcase(sentence)) end)
end
end

View File

@ -0,0 +1,62 @@
if !System.get_env("EXERCISM_TEST_EXAMPLES") do
Code.load_file("pangram.exs", __DIR__)
end
ExUnit.start()
ExUnit.configure(trace: true)
defmodule PangramTest do
use ExUnit.Case
# @tag :pending
test "empty sentence" do
refute Pangram.pangram?("")
end
@tag :pending
test "pangram with only lower case" do
assert Pangram.pangram?("the quick brown fox jumps over the lazy dog")
end
@tag :pending
test "missing character 'x'" do
refute Pangram.pangram?("a quick movement of the enemy will jeopardize five gunboats")
end
@tag :pending
test "another missing character 'x'" do
refute Pangram.pangram?("the quick brown fish jumps over the lazy dog")
end
@tag :pending
test "pangram with underscores" do
assert Pangram.pangram?("the_quick_brown_fox_jumps_over_the_lazy_dog")
end
@tag :pending
test "pangram with numbers" do
assert Pangram.pangram?("the 1 quick brown fox jumps over the 2 lazy dogs")
end
@tag :pending
test "missing letters replaced by numbers" do
refute Pangram.pangram?("7h3 qu1ck brown fox jumps ov3r 7h3 lazy dog")
end
@tag :pending
test "pangram with mixed case and punctuation" do
assert Pangram.pangram?("Five quacking Zephyrs jolt my wax bed.")
end
@tag :pending
test "pangram with non ascii characters" do
assert Pangram.pangram?("Victor jagt zwölf Boxkämpfer quer über den großen Sylter Deich.")
end
@tag :pending
test "pangram in alphabet other than ASCII" do
refute Pangram.pangram?(
"Широкая электрификация южных губерний даст мощный толчок подъёму сельского хозяйства."
)
end
end

View File

@ -0,0 +1,81 @@
# Scrabble Score
Given a word, compute the scrabble score for that word.
## Letter Values
You'll need these:
```text
Letter Value
A, E, I, O, U, L, N, R, S, T 1
D, G 2
B, C, M, P 3
F, H, V, W, Y 4
K 5
J, X 8
Q, Z 10
```
## Examples
"cabbage" should be scored as worth 14 points:
- 3 points for C
- 1 point for A, twice
- 3 points for B, twice
- 2 points for G
- 1 point for E
And to total:
- `3 + 2*1 + 2*3 + 2 + 1`
- = `3 + 2 + 6 + 3`
- = `5 + 9`
- = 14
## Extensions
- You can play a double or a triple letter.
- You can play a double or a triple word.
## Running tests
Execute the tests with:
```bash
$ elixir scrabble_score_test.exs
```
### Pending tests
In the test suites, all but the first test have been skipped.
Once you get a test passing, you can unskip the next one by
commenting out the relevant `@tag :pending` with a `#` symbol.
For example:
```elixir
# @tag :pending
test "shouting" do
assert Bob.hey("WATCH OUT!") == "Whoa, chill out!"
end
```
Or, you can enable all the tests by commenting out the
`ExUnit.configure` line in the test suite.
```elixir
# ExUnit.configure exclude: :pending, trace: true
```
For more detailed information about the Elixir track, please
see the [help page](http://exercism.io/languages/elixir).
## Source
Inspired by the Extreme Startup game [https://github.com/rchatley/extreme_startup](https://github.com/rchatley/extreme_startup)
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@ -0,0 +1,40 @@
defmodule Scrabble do
@doc """
Calculate the scrabble score for the word.
"""
@spec score(String.t()) :: non_neg_integer
def score(word) do
word
|> String.upcase()
|> String.to_charlist()
|> Enum.reduce(0, fn c, acc -> acc + letter_score(c) end)
end
defp letter_score(letter) do
case letter do
c when c in [?A, ?E, ?I, ?O, ?U, ?L, ?N, ?R, ?S, ?T] ->
1
c when c in [?D, ?G] ->
2
c when c in [?B, ?C, ?M, ?P] ->
3
c when c in [?F, ?H, ?V, ?W, ?Y] ->
4
c when c in [?K] ->
5
c when c in [?J, ?X] ->
8
c when c in [?Q, ?Z] ->
10
_ ->
0
end
end
end

View File

@ -0,0 +1,50 @@
if !System.get_env("EXERCISM_TEST_EXAMPLES") do
Code.load_file("scrabble.exs", __DIR__)
end
ExUnit.start()
ExUnit.configure(trace: true)
defmodule ScrabbleScoreTest do
use ExUnit.Case
# @tag :pending
test "empty word scores zero" do
assert Scrabble.score("") == 0
end
@tag :pending
test "whitespace scores zero" do
assert Scrabble.score(" \t\n") == 0
end
@tag :pending
test "scores very short word" do
assert Scrabble.score("a") == 1
end
@tag :pending
test "scores other very short word" do
assert Scrabble.score("f") == 4
end
@tag :pending
test "simple word scores the number of letters" do
assert Scrabble.score("street") == 6
end
@tag :pending
test "complicated word scores more" do
assert Scrabble.score("quirky") == 22
end
@tag :pending
test "scores are case insensitive" do
assert Scrabble.score("OXYPHENBUTAZONE") == 41
end
@tag :pending
test "convenient scoring" do
assert Scrabble.score("alacrity") == 13
end
end

View File

@ -0,0 +1,63 @@
# Simple Linked List
Write a simple linked list implementation that uses Elements and a List.
The linked list is a fundamental data structure in computer science,
often used in the implementation of other data structures. They're
pervasive in functional programming languages, such as Clojure, Erlang,
or Haskell, but far less common in imperative languages such as Ruby or
Python.
The simplest kind of linked list is a singly linked list. Each element in the
list contains data and a "next" field pointing to the next element in the list
of elements.
This variant of linked lists is often used to represent sequences or
push-down stacks (also called a LIFO stack; Last In, First Out).
As a first take, lets create a singly linked list to contain the range (1..10),
and provide functions to reverse a linked list and convert to and from arrays.
When implementing this in a language with built-in linked lists,
implement your own abstract data type.
## Running tests
Execute the tests with:
```bash
$ elixir simple_linked_list_test.exs
```
### Pending tests
In the test suites, all but the first test have been skipped.
Once you get a test passing, you can unskip the next one by
commenting out the relevant `@tag :pending` with a `#` symbol.
For example:
```elixir
# @tag :pending
test "shouting" do
assert Bob.hey("WATCH OUT!") == "Whoa, chill out!"
end
```
Or, you can enable all the tests by commenting out the
`ExUnit.configure` line in the test suite.
```elixir
# ExUnit.configure exclude: :pending, trace: true
```
For more detailed information about the Elixir track, please
see the [help page](http://exercism.io/languages/elixir).
## Source
Inspired by 'Data Structures and Algorithms with Object-Oriented Design Patterns in Ruby', singly linked-lists. [http://www.brpreiss.com/books/opus8/html/page96.html#SECTION004300000000000000000](http://www.brpreiss.com/books/opus8/html/page96.html#SECTION004300000000000000000)
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@ -0,0 +1,83 @@
defmodule LinkedList do
@opaque t :: tuple()
@doc """
Construct a new LinkedList
"""
@spec new() :: t
def new() do
# Your implementation here...
end
@doc """
Push an item onto a LinkedList
"""
@spec push(t, any()) :: t
def push(list, elem) do
# Your implementation here...
end
@doc """
Calculate the length of a LinkedList
"""
@spec length(t) :: non_neg_integer()
def length(list) do
# Your implementation here...
end
@doc """
Determine if a LinkedList is empty
"""
@spec empty?(t) :: boolean()
def empty?(list) do
# Your implementation here...
end
@doc """
Get the value of a head of the LinkedList
"""
@spec peek(t) :: {:ok, any()} | {:error, :empty_list}
def peek(list) do
# Your implementation here...
end
@doc """
Get tail of a LinkedList
"""
@spec tail(t) :: {:ok, t} | {:error, :empty_list}
def tail(list) do
# Your implementation here...
end
@doc """
Remove the head from a LinkedList
"""
@spec pop(t) :: {:ok, any(), t} | {:error, :empty_list}
def pop(list) do
# Your implementation here...
end
@doc """
Construct a LinkedList from a stdlib List
"""
@spec from_list(list()) :: t
def from_list(list) do
# Your implementation here...
end
@doc """
Construct a stdlib List LinkedList from a LinkedList
"""
@spec to_list(t) :: list()
def to_list(list) do
# Your implementation here...
end
@doc """
Reverse a LinkedList
"""
@spec reverse(t) :: t
def reverse(list) do
# Your implementation here...
end
end

View File

@ -0,0 +1,157 @@
if !System.get_env("EXERCISM_TEST_EXAMPLES") do
Code.load_file("linked_list.exs", __DIR__)
end
ExUnit.start()
ExUnit.configure(exclude: :pending, trace: true)
defmodule LinkedListTest do
use ExUnit.Case
test "length/1 of new list" do
list = LinkedList.new()
assert LinkedList.length(list) == 0
end
@tag :pending
test "empty?/1 of new list" do
list = LinkedList.new()
assert LinkedList.empty?(list)
end
@tag :pending
test "length/1 of list of 1 datum" do
list = LinkedList.new() |> LinkedList.push(10)
assert LinkedList.length(list) == 1
end
@tag :pending
test "empty?/1 of list of 1 datum" do
list = LinkedList.new() |> LinkedList.push(20)
refute LinkedList.empty?(list)
end
@tag :pending
test "peek/1 of list of 1 datum" do
list = LinkedList.new() |> LinkedList.push(20)
assert LinkedList.peek(list) == {:ok, 20}
end
@tag :pending
test "peek/1 of list of empty list" do
list = LinkedList.new()
assert LinkedList.peek(list) == {:error, :empty_list}
end
@tag :pending
test "tail/1 of empty list" do
list = LinkedList.new()
assert {:error, :empty_list} = LinkedList.tail(list)
end
@tag :pending
test "tail/1 of list of 1 datum" do
list = LinkedList.new() |> LinkedList.push(:hello)
assert {:ok, tail} = LinkedList.tail(list)
assert LinkedList.peek(tail) == {:error, :empty_list}
end
@tag :pending
test "pushed items are stacked" do
list =
LinkedList.new()
|> LinkedList.push(:a)
|> LinkedList.push(:b)
assert LinkedList.peek(list) == {:ok, :b}
assert {:ok, list} = LinkedList.tail(list)
assert LinkedList.peek(list) == {:ok, :a}
assert {:ok, list} = LinkedList.tail(list)
assert LinkedList.peek(list) == {:error, :empty_list}
end
@tag :pending
test "push 10 times" do
list = Enum.reduce(1..10, LinkedList.new(), &LinkedList.push(&2, &1))
assert LinkedList.peek(list) == {:ok, 10}
assert LinkedList.length(list) == 10
end
@tag :pending
test "pop/1 of list of 1 datum" do
list = LinkedList.new() |> LinkedList.push(:a)
assert {:ok, :a, tail} = LinkedList.pop(list)
assert LinkedList.length(tail) == 0
end
@tag :pending
test "popping frenzy" do
list = Enum.reduce(11..20, LinkedList.new(), &LinkedList.push(&2, &1))
assert LinkedList.length(list) == 10
assert {:ok, 20, list} = LinkedList.pop(list)
assert {:ok, 19, list} = LinkedList.pop(list)
assert {:ok, 18, list} = LinkedList.pop(list)
assert {:ok, 17, list} = LinkedList.pop(list)
assert {:ok, 16, list} = LinkedList.pop(list)
assert {:ok, 15} = LinkedList.peek(list)
assert LinkedList.length(list) == 5
end
@tag :pending
test "from_list/1 of empty list" do
list = LinkedList.from_list([])
assert LinkedList.length(list) == 0
end
@tag :pending
test "from_list/1 of 2 element list" do
list = LinkedList.from_list([:a, :b])
assert LinkedList.length(list) == 2
assert {:ok, :a, list} = LinkedList.pop(list)
assert {:ok, :b, list} = LinkedList.pop(list)
assert {:error, :empty_list} = LinkedList.pop(list)
end
@tag :pending
test "to_list/1 of empty list" do
list = LinkedList.new()
assert LinkedList.to_list(list) == []
end
@tag :pending
test "to_list/1 of list of 1 datum" do
list = LinkedList.from_list([:mon])
assert LinkedList.to_list(list) == [:mon]
end
@tag :pending
test "to_list/1 of list of 2 datum" do
list = LinkedList.from_list([:mon, :tues])
assert LinkedList.to_list(list) == [:mon, :tues]
end
@tag :pending
test "reverse/1 of list of 2 datum" do
list = LinkedList.from_list([1, 2, 3]) |> LinkedList.reverse()
assert LinkedList.to_list(list) == [3, 2, 1]
end
@tag :pending
test "reverse/1 of list of 200 datum" do
list = Enum.to_list(1..200)
linked_list = LinkedList.from_list(list) |> LinkedList.reverse()
assert LinkedList.to_list(linked_list) == Enum.reverse(list)
end
@tag :pending
test "reverse/1 round trip" do
list = Enum.to_list(1..200)
linked_list =
LinkedList.from_list(list)
|> LinkedList.reverse()
|> LinkedList.reverse()
assert LinkedList.to_list(linked_list) == list
end
end

View File

@ -4,5 +4,32 @@ defmodule Sublist do
and if not whether it is equal or unequal to the second list.
"""
def compare(a, b) do
cond do
a === b ->
:equal
length(a) == length(b) && a !== b ->
:unequal
is_sublist?(a, b) ->
:superlist
is_sublist?(b, a) ->
:sublist
true ->
:unequal
end
end
defp is_sublist?(_, []), do: true
defp is_sublist?([], _), do: false
defp is_sublist?(a = [head | rest], b) do
if head === hd(b) && Enum.take(a, Enum.count(b)) == b do
true
else
is_sublist?(rest, b)
end
end
end

View File

@ -12,98 +12,98 @@ defmodule SublistTest do
assert Sublist.compare([], []) == :equal
end
@tag :pending
# @tag :pending
test "empty is a sublist of anything" do
assert Sublist.compare([], [nil]) == :sublist
end
@tag :pending
# @tag :pending
test "anything is a superlist of empty" do
assert Sublist.compare([nil], []) == :superlist
end
@tag :pending
# @tag :pending
test "1 is not 2" do
assert Sublist.compare([1], [2]) == :unequal
end
@tag :pending
# @tag :pending
test "comparing massive equal lists" do
l = Enum.to_list(1..1_000_000)
assert Sublist.compare(l, l) == :equal
end
@tag :pending
# @tag :pending
test "sublist at start" do
assert Sublist.compare([1, 2, 3], [1, 2, 3, 4, 5]) == :sublist
end
@tag :pending
# @tag :pending
test "sublist in middle" do
assert Sublist.compare([4, 3, 2], [5, 4, 3, 2, 1]) == :sublist
end
@tag :pending
# @tag :pending
test "sublist at end" do
assert Sublist.compare([3, 4, 5], [1, 2, 3, 4, 5]) == :sublist
end
@tag :pending
# @tag :pending
test "partially matching sublist at start" do
assert Sublist.compare([1, 1, 2], [1, 1, 1, 2]) == :sublist
end
@tag :pending
# @tag :pending
test "sublist early in huge list" do
assert Sublist.compare([3, 4, 5], Enum.to_list(1..1_000_000)) == :sublist
end
@tag :pending
# @tag :pending
test "huge sublist not in huge list" do
assert Sublist.compare(Enum.to_list(10..1_000_001), Enum.to_list(1..1_000_000)) == :unequal
end
@tag :pending
# @tag :pending
test "superlist at start" do
assert Sublist.compare([1, 2, 3, 4, 5], [1, 2, 3]) == :superlist
end
@tag :pending
# @tag :pending
test "superlist in middle" do
assert Sublist.compare([5, 4, 3, 2, 1], [4, 3, 2]) == :superlist
end
@tag :pending
# @tag :pending
test "superlist at end" do
assert Sublist.compare([1, 2, 3, 4, 5], [3, 4, 5]) == :superlist
end
@tag :pending
# @tag :pending
test "1 and 2 does not contain 3" do
assert Sublist.compare([1, 2], [3]) == :unequal
end
@tag :pending
# @tag :pending
test "partially matching superlist at start" do
assert Sublist.compare([1, 1, 1, 2], [1, 1, 2]) == :superlist
end
@tag :pending
# @tag :pending
test "superlist early in huge list" do
assert Sublist.compare(Enum.to_list(1..1_000_000), [3, 4, 5]) == :superlist
end
@tag :pending
# @tag :pending
test "strict equality needed" do
assert Sublist.compare([1], [1.0, 2]) == :unequal
end
@tag :pending
# @tag :pending
test "recurring values sublist" do
assert Sublist.compare([1, 2, 1, 2, 3], [1, 2, 3, 1, 2, 1, 2, 3, 2, 1]) == :sublist
end
@tag :pending
# @tag :pending
test "recurring values unequal" do
assert Sublist.compare([1, 2, 1, 2, 3], [1, 2, 3, 1, 2, 3, 2, 3, 2, 1]) == :unequal
end

View File

@ -0,0 +1,50 @@
# Sum Of Multiples
Given a number, find the sum of all the unique multiples of particular numbers up to
but not including that number.
If we list all the natural numbers below 20 that are multiples of 3 or 5,
we get 3, 5, 6, 9, 10, 12, 15, and 18.
The sum of these multiples is 78.
## Running tests
Execute the tests with:
```bash
$ elixir sum_of_multiples_test.exs
```
### Pending tests
In the test suites, all but the first test have been skipped.
Once you get a test passing, you can unskip the next one by
commenting out the relevant `@tag :pending` with a `#` symbol.
For example:
```elixir
# @tag :pending
test "shouting" do
assert Bob.hey("WATCH OUT!") == "Whoa, chill out!"
end
```
Or, you can enable all the tests by commenting out the
`ExUnit.configure` line in the test suite.
```elixir
# ExUnit.configure exclude: :pending, trace: true
```
For more detailed information about the Elixir track, please
see the [help page](http://exercism.io/languages/elixir).
## Source
A variation on Problem 1 at Project Euler [http://projecteuler.net/problem=1](http://projecteuler.net/problem=1)
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@ -0,0 +1,11 @@
defmodule SumOfMultiples do
@doc """
Adds up all numbers from 1 to a given end number that are multiples of the factors provided.
"""
@spec to(non_neg_integer, [non_neg_integer]) :: non_neg_integer
def to(limit, factors) do
0..(limit - 1)
|> Enum.filter(fn c -> Enum.any?(factors, fn m -> rem(c, m) == 0 end) end)
|> Enum.sum()
end
end

View File

@ -0,0 +1,64 @@
if !System.get_env("EXERCISM_TEST_EXAMPLES") do
Code.load_file("sum_of_multiples.exs", __DIR__)
end
ExUnit.start()
ExUnit.configure(trace: true)
defmodule SumOfMultiplesTest do
use ExUnit.Case
# @tag :pending
test "sum to 1" do
assert SumOfMultiples.to(1, [3, 5]) == 0
end
@tag :pending
test "sum to 3" do
assert SumOfMultiples.to(4, [3, 5]) == 3
end
@tag :pending
test "sum to 10" do
assert SumOfMultiples.to(10, [3, 5]) == 23
end
@tag :pending
test "sum to 20" do
assert SumOfMultiples.to(20, [3, 5]) == 78
end
@tag :pending
test "sum to 100" do
assert SumOfMultiples.to(100, [3, 5]) == 2318
end
@tag :pending
test "sum to 1000" do
assert SumOfMultiples.to(1000, [3, 5]) == 233_168
end
@tag :pending
test "configurable 7, 13, 17 to 20" do
multiples = [7, 13, 17]
assert SumOfMultiples.to(20, multiples) == 51
end
@tag :pending
test "configurable 4, 6 to 15" do
multiples = [4, 6]
assert SumOfMultiples.to(15, multiples) == 30
end
@tag :pending
test "configurable 5, 6, 8 to 150" do
multiples = [5, 6, 8]
assert SumOfMultiples.to(150, multiples) == 4419
end
@tag :pending
test "configurable 43, 47 to 10000" do
multiples = [43, 47]
assert SumOfMultiples.to(10000, multiples) == 2_203_160
end
end