lunchtime practice
This commit is contained in:
parent
9df0f3b5f1
commit
c6720da38d
|
@ -0,0 +1,52 @@
|
|||
# Flatten Array
|
||||
|
||||
Take a nested list and return a single flattened list with all values except nil/null.
|
||||
|
||||
The challenge is to write a function that accepts an arbitrarily-deep nested list-like structure and returns a flattened structure without any nil/null values.
|
||||
|
||||
For Example
|
||||
|
||||
input: [1,[2,3,null,4],[null],5]
|
||||
|
||||
output: [1,2,3,4,5]
|
||||
|
||||
## Running tests
|
||||
|
||||
Execute the tests with:
|
||||
|
||||
```bash
|
||||
$ elixir flatten_array_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
|
||||
|
||||
Interview Question [https://reference.wolfram.com/language/ref/Flatten.html](https://reference.wolfram.com/language/ref/Flatten.html)
|
||||
|
||||
## Submitting Incomplete Solutions
|
||||
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
|
|
@ -0,0 +1,23 @@
|
|||
defmodule FlattenArray do
|
||||
@doc """
|
||||
Accept a list and return the list flattened without nil values.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> FlattenArray.flatten([1, [2], 3, nil])
|
||||
[1,2,3]
|
||||
|
||||
iex> FlattenArray.flatten([nil, nil])
|
||||
[]
|
||||
|
||||
"""
|
||||
@spec flatten(list) :: list
|
||||
def flatten(list) do
|
||||
flat(list, [])
|
||||
end
|
||||
|
||||
defp flat([], rest), do: rest
|
||||
defp flat([head | tail], rest) when is_list(head), do: flat(tail, rest ++ flat(head, []))
|
||||
defp flat([head | tail], rest) when is_nil(head), do: flat(tail, rest)
|
||||
defp flat([head | tail], rest), do: flat(tail, rest ++ [head])
|
||||
end
|
|
@ -0,0 +1,39 @@
|
|||
if !System.get_env("EXERCISM_TEST_EXAMPLES") do
|
||||
Code.load_file("flatten_array.exs", __DIR__)
|
||||
end
|
||||
|
||||
ExUnit.start()
|
||||
ExUnit.configure(trace: true)
|
||||
|
||||
defmodule FlattenArrayTest do
|
||||
use ExUnit.Case
|
||||
|
||||
test "returns original list if there is nothing to flatten" do
|
||||
assert FlattenArray.flatten([1, 2, 3]) == [1, 2, 3]
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "flattens an empty nested list" do
|
||||
assert FlattenArray.flatten([[]]) == []
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "flattens a nested list" do
|
||||
assert FlattenArray.flatten([1, [2, [3], 4], 5, [6, [7, 8]]]) == [1, 2, 3, 4, 5, 6, 7, 8]
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "removes nil from list" do
|
||||
assert FlattenArray.flatten([1, nil, 2]) == [1, 2]
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "removes nil from a nested list" do
|
||||
assert FlattenArray.flatten([1, [2, nil, 4], 5]) == [1, 2, 4, 5]
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "returns an empty list if all values in nested list are nil" do
|
||||
assert FlattenArray.flatten([nil, [nil], [nil, [nil]]]) == []
|
||||
end
|
||||
end
|
|
@ -10,6 +10,13 @@ defmodule School do
|
|||
"""
|
||||
@spec add(map, String.t(), integer) :: map
|
||||
def add(db, name, grade) do
|
||||
update_in(db, [grade], fn i ->
|
||||
case i do
|
||||
nil -> [name]
|
||||
_ -> [name | i]
|
||||
end
|
||||
|> Enum.sort()
|
||||
end)
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
@ -17,6 +24,7 @@ defmodule School do
|
|||
"""
|
||||
@spec grade(map, integer) :: [String.t()]
|
||||
def grade(db, grade) do
|
||||
db[grade] || []
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
@ -24,5 +32,6 @@ defmodule School do
|
|||
"""
|
||||
@spec sort(map) :: [{integer, [String.t()]}]
|
||||
def sort(db) do
|
||||
Enum.sort(db)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
# Hamming
|
||||
|
||||
Calculate the Hamming difference between two DNA strands.
|
||||
|
||||
A mutation is simply a mistake that occurs during the creation or
|
||||
copying of a nucleic acid, in particular DNA. Because nucleic acids are
|
||||
vital to cellular functions, mutations tend to cause a ripple effect
|
||||
throughout the cell. Although mutations are technically mistakes, a very
|
||||
rare mutation may equip the cell with a beneficial attribute. In fact,
|
||||
the macro effects of evolution are attributable by the accumulated
|
||||
result of beneficial microscopic mutations over many generations.
|
||||
|
||||
The simplest and most common type of nucleic acid mutation is a point
|
||||
mutation, which replaces one base with another at a single nucleotide.
|
||||
|
||||
By counting the number of differences between two homologous DNA strands
|
||||
taken from different genomes with a common ancestor, we get a measure of
|
||||
the minimum number of point mutations that could have occurred on the
|
||||
evolutionary path between the two strands.
|
||||
|
||||
This is called the 'Hamming distance'.
|
||||
|
||||
It is found by comparing two DNA strands and counting how many of the
|
||||
nucleotides are different from their equivalent in the other string.
|
||||
|
||||
GAGCCTACTAACGGGAT
|
||||
CATCGTAATGACGGCCT
|
||||
^ ^ ^ ^ ^ ^^
|
||||
|
||||
The Hamming distance between these two DNA strands is 7.
|
||||
|
||||
# Implementation notes
|
||||
|
||||
The Hamming distance is only defined for sequences of equal length. This means
|
||||
that based on the definition, each language could deal with getting sequences
|
||||
of equal length differently.
|
||||
|
||||
## Running tests
|
||||
|
||||
Execute the tests with:
|
||||
|
||||
```bash
|
||||
$ elixir hamming_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
|
||||
|
||||
The Calculating Point Mutations problem at Rosalind [http://rosalind.info/problems/hamm/](http://rosalind.info/problems/hamm/)
|
||||
|
||||
## Submitting Incomplete Solutions
|
||||
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
|
|
@ -0,0 +1,18 @@
|
|||
defmodule Hamming do
|
||||
@doc """
|
||||
Returns number of differences between two strands of DNA, known as the Hamming Distance.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Hamming.hamming_distance('AAGTCATA', 'TAGCGATC')
|
||||
{:ok, 4}
|
||||
"""
|
||||
@spec hamming_distance([char], [char]) :: non_neg_integer
|
||||
def hamming_distance(s1, s2) when length(s1) != length(s2) do
|
||||
{:error, "Lists must be the same length"}
|
||||
end
|
||||
|
||||
def hamming_distance(s1, s2) do
|
||||
{:ok, Enum.count(0..length(s1), &(Enum.at(s1, &1) != Enum.at(s2, &1)))}
|
||||
end
|
||||
end
|
|
@ -0,0 +1,43 @@
|
|||
if !System.get_env("EXERCISM_TEST_EXAMPLES") do
|
||||
Code.load_file("hamming.exs", __DIR__)
|
||||
end
|
||||
|
||||
ExUnit.start()
|
||||
ExUnit.configure(trace: true)
|
||||
|
||||
defmodule HammingTest do
|
||||
use ExUnit.Case
|
||||
|
||||
test "no difference between empty strands" do
|
||||
assert Hamming.hamming_distance('', '') == {:ok, 0}
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "no difference between identical strands" do
|
||||
assert Hamming.hamming_distance('GGACTGA', 'GGACTGA') == {:ok, 0}
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "small hamming distance in middle somewhere" do
|
||||
assert Hamming.hamming_distance('GGACG', 'GGTCG') == {:ok, 1}
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "distance with same nucleotides in different locations" do
|
||||
assert Hamming.hamming_distance('TAG', 'GAT') == {:ok, 2}
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "larger distance" do
|
||||
assert Hamming.hamming_distance('ACCAGGG', 'ACTATGG') == {:ok, 2}
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "hamming distance is undefined for strands of different lengths" do
|
||||
assert {:error, "Lists must be the same length"} =
|
||||
Hamming.hamming_distance('AAAC', 'TAGGGGAGGCTAGCGGTAGGAC')
|
||||
|
||||
assert {:error, "Lists must be the same length"} =
|
||||
Hamming.hamming_distance('GACTACGGACAGGACACC', 'GACATCGC')
|
||||
end
|
||||
end
|
|
@ -0,0 +1,101 @@
|
|||
# Kindergarten Garden
|
||||
|
||||
Given a diagram, determine which plants each child in the kindergarten class is
|
||||
responsible for.
|
||||
|
||||
The kindergarten class is learning about growing plants. The teacher
|
||||
thought it would be a good idea to give them actual seeds, plant them in
|
||||
actual dirt, and grow actual plants.
|
||||
|
||||
They've chosen to grow grass, clover, radishes, and violets.
|
||||
|
||||
To this end, the children have put little cups along the window sills, and
|
||||
planted one type of plant in each cup, choosing randomly from the available
|
||||
types of seeds.
|
||||
|
||||
```text
|
||||
[window][window][window]
|
||||
........................ # each dot represents a cup
|
||||
........................
|
||||
```
|
||||
|
||||
There are 12 children in the class:
|
||||
|
||||
- Alice, Bob, Charlie, David,
|
||||
- Eve, Fred, Ginny, Harriet,
|
||||
- Ileana, Joseph, Kincaid, and Larry.
|
||||
|
||||
Each child gets 4 cups, two on each row. Their teacher assigns cups to
|
||||
the children alphabetically by their names.
|
||||
|
||||
The following diagram represents Alice's plants:
|
||||
|
||||
```text
|
||||
[window][window][window]
|
||||
VR......................
|
||||
RG......................
|
||||
```
|
||||
|
||||
In the first row, nearest the windows, she has a violet and a radish. In the
|
||||
second row she has a radish and some grass.
|
||||
|
||||
Your program will be given the plants from left-to-right starting with
|
||||
the row nearest the windows. From this, it should be able to determine
|
||||
which plants belong to each student.
|
||||
|
||||
For example, if it's told that the garden looks like so:
|
||||
|
||||
```text
|
||||
[window][window][window]
|
||||
VRCGVVRVCGGCCGVRGCVCGCGV
|
||||
VRCCCGCRRGVCGCRVVCVGCGCV
|
||||
```
|
||||
|
||||
Then if asked for Alice's plants, it should provide:
|
||||
|
||||
- Violets, radishes, violets, radishes
|
||||
|
||||
While asking for Bob's plants would yield:
|
||||
|
||||
- Clover, grass, clover, clover
|
||||
|
||||
## Running tests
|
||||
|
||||
Execute the tests with:
|
||||
|
||||
```bash
|
||||
$ elixir kindergarten_garden_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
|
||||
|
||||
Random musings during airplane trip. [http://jumpstartlab.com](http://jumpstartlab.com)
|
||||
|
||||
## Submitting Incomplete Solutions
|
||||
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
|
|
@ -0,0 +1,14 @@
|
|||
defmodule Garden do
|
||||
@doc """
|
||||
Accepts a string representing the arrangement of cups on a windowsill and a
|
||||
list with names of students in the class. The student names list does not
|
||||
have to be in alphabetical order.
|
||||
|
||||
It decodes that string into the various gardens for each student and returns
|
||||
that information in a map.
|
||||
"""
|
||||
|
||||
@spec info(String.t(), list) :: map
|
||||
def info(info_string, student_names) do
|
||||
end
|
||||
end
|
|
@ -0,0 +1,90 @@
|
|||
if !System.get_env("EXERCISM_TEST_EXAMPLES") do
|
||||
Code.load_file("garden.exs", __DIR__)
|
||||
end
|
||||
|
||||
ExUnit.start()
|
||||
ExUnit.configure(exclude: :pending, trace: true)
|
||||
|
||||
defmodule GardenTest do
|
||||
use ExUnit.Case
|
||||
|
||||
test "gets the garden for Alice with just her plants" do
|
||||
garden_info = Garden.info("RC\nGG")
|
||||
assert garden_info.alice == {:radishes, :clover, :grass, :grass}
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "gets another garden for Alice with just her plants" do
|
||||
garden_info = Garden.info("VC\nRC")
|
||||
assert garden_info.alice == {:violets, :clover, :radishes, :clover}
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "returns an empty tuple if the child has no plants" do
|
||||
garden_info = Garden.info("VC\nRC")
|
||||
assert garden_info.bob == {}
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "gets the garden for Bob" do
|
||||
garden_info = Garden.info("VVCG\nVVRC")
|
||||
assert garden_info.bob == {:clover, :grass, :radishes, :clover}
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "gets the garden for all students" do
|
||||
garden_info = Garden.info("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV")
|
||||
assert garden_info.alice == {:violets, :radishes, :violets, :radishes}
|
||||
assert garden_info.bob == {:clover, :grass, :clover, :clover}
|
||||
assert garden_info.charlie == {:violets, :violets, :clover, :grass}
|
||||
assert garden_info.david == {:radishes, :violets, :clover, :radishes}
|
||||
assert garden_info.eve == {:clover, :grass, :radishes, :grass}
|
||||
assert garden_info.fred == {:grass, :clover, :violets, :clover}
|
||||
assert garden_info.ginny == {:clover, :grass, :grass, :clover}
|
||||
assert garden_info.harriet == {:violets, :radishes, :radishes, :violets}
|
||||
assert garden_info.ileana == {:grass, :clover, :violets, :clover}
|
||||
assert garden_info.joseph == {:violets, :clover, :violets, :grass}
|
||||
assert garden_info.kincaid == {:grass, :clover, :clover, :grass}
|
||||
assert garden_info.larry == {:grass, :violets, :clover, :violets}
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "accepts custom child names" do
|
||||
garden_info = Garden.info("VC\nRC", [:nate, :maggie])
|
||||
assert garden_info.maggie == {:violets, :clover, :radishes, :clover}
|
||||
assert garden_info.nate == {}
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "gets the garden for all students with custom child names" do
|
||||
names = [
|
||||
:maggie,
|
||||
:nate,
|
||||
:xander,
|
||||
:ophelia,
|
||||
:pete,
|
||||
:reggie,
|
||||
:sylvia,
|
||||
:tanner,
|
||||
:ursula,
|
||||
:victor,
|
||||
:winnie,
|
||||
:ynold
|
||||
]
|
||||
|
||||
garden_string = "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV"
|
||||
garden_info = Garden.info(garden_string, names)
|
||||
assert garden_info.maggie == {:violets, :radishes, :violets, :radishes}
|
||||
assert garden_info.nate == {:clover, :grass, :clover, :clover}
|
||||
assert garden_info.ophelia == {:violets, :violets, :clover, :grass}
|
||||
assert garden_info.pete == {:radishes, :violets, :clover, :radishes}
|
||||
assert garden_info.reggie == {:clover, :grass, :radishes, :grass}
|
||||
assert garden_info.sylvia == {:grass, :clover, :violets, :clover}
|
||||
assert garden_info.tanner == {:clover, :grass, :grass, :clover}
|
||||
assert garden_info.ursula == {:violets, :radishes, :radishes, :violets}
|
||||
assert garden_info.victor == {:grass, :clover, :violets, :clover}
|
||||
assert garden_info.winnie == {:violets, :clover, :violets, :grass}
|
||||
assert garden_info.xander == {:grass, :clover, :clover, :grass}
|
||||
assert garden_info.ynold == {:grass, :violets, :clover, :violets}
|
||||
end
|
||||
end
|
|
@ -0,0 +1,68 @@
|
|||
# Leap
|
||||
|
||||
Given a year, report if it is a leap year.
|
||||
|
||||
The tricky thing here is that a leap year in the Gregorian calendar occurs:
|
||||
|
||||
```text
|
||||
on every year that is evenly divisible by 4
|
||||
except every year that is evenly divisible by 100
|
||||
unless the year is also evenly divisible by 400
|
||||
```
|
||||
|
||||
For example, 1997 is not a leap year, but 1996 is. 1900 is not a leap
|
||||
year, but 2000 is.
|
||||
|
||||
If your language provides a method in the standard library that does
|
||||
this look-up, pretend it doesn't exist and implement it yourself.
|
||||
|
||||
## Notes
|
||||
|
||||
Though our exercise adopts some very simple rules, there is more to
|
||||
learn!
|
||||
|
||||
For a delightful, four minute explanation of the whole leap year
|
||||
phenomenon, go watch [this youtube video][video].
|
||||
|
||||
[video]: http://www.youtube.com/watch?v=xX96xng7sAE
|
||||
|
||||
## Running tests
|
||||
|
||||
Execute the tests with:
|
||||
|
||||
```bash
|
||||
$ elixir leap_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
|
||||
|
||||
JavaRanch Cattle Drive, exercise 3 [http://www.javaranch.com/leap.jsp](http://www.javaranch.com/leap.jsp)
|
||||
|
||||
## Submitting Incomplete Solutions
|
||||
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
|
|
@ -0,0 +1,15 @@
|
|||
defmodule Year do
|
||||
@doc """
|
||||
Returns whether 'year' is a leap year.
|
||||
|
||||
A leap year occurs:
|
||||
|
||||
on every year that is evenly divisible by 4
|
||||
except every year that is evenly divisible by 100
|
||||
unless the year is also evenly divisible by 400
|
||||
"""
|
||||
@spec leap_year?(non_neg_integer) :: boolean
|
||||
def leap_year?(year) do
|
||||
rem(year, 400) == 0 || (rem(year, 4) == 0 && rem(year, 100) != 0)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,30 @@
|
|||
if !System.get_env("EXERCISM_TEST_EXAMPLES") do
|
||||
Code.load_file("leap.exs", __DIR__)
|
||||
end
|
||||
|
||||
ExUnit.start()
|
||||
ExUnit.configure(trace: true)
|
||||
|
||||
defmodule LeapTest do
|
||||
use ExUnit.Case
|
||||
|
||||
# @tag :pending
|
||||
test "vanilla leap year" do
|
||||
assert Year.leap_year?(1996)
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "any old year" do
|
||||
refute Year.leap_year?(1997), "1997 is not a leap year."
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "century" do
|
||||
refute Year.leap_year?(1900), "1900 is not a leap year."
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "exceptional century" do
|
||||
assert Year.leap_year?(2400)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,44 @@
|
|||
# List Ops
|
||||
|
||||
Implement basic list operations.
|
||||
|
||||
In functional languages list operations like `length`, `map`, and
|
||||
`reduce` are very common. Implement a series of basic list operations,
|
||||
without using existing functions.
|
||||
|
||||
## Running tests
|
||||
|
||||
Execute the tests with:
|
||||
|
||||
```bash
|
||||
$ elixir list_ops_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).
|
||||
|
||||
## Submitting Incomplete Solutions
|
||||
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
|
|
@ -0,0 +1,42 @@
|
|||
defmodule ListOps do
|
||||
@doc """
|
||||
so apparently you can build the rest of these
|
||||
functions with *just* `reduce`... neat!
|
||||
"""
|
||||
@type acc :: any
|
||||
@spec reduce(list, acc, (any, acc -> acc)) :: acc
|
||||
def reduce([], acc, _f), do: acc
|
||||
def reduce([h | t], acc, f), do: reduce(t, f.(h, acc), f)
|
||||
|
||||
@spec count(list) :: non_neg_integer
|
||||
def count(l) do
|
||||
reduce(l, 0, fn _, acc -> acc + 1 end)
|
||||
end
|
||||
|
||||
@spec reverse(list) :: list
|
||||
def reverse(l) do
|
||||
reduce(l, [], &[&1 | &2])
|
||||
end
|
||||
|
||||
@spec map(list, (any -> any)) :: list
|
||||
def map(l, f) do
|
||||
reduce(reverse(l), [], &[f.(&1) | &2])
|
||||
end
|
||||
|
||||
@spec filter(list, (any -> as_boolean(term))) :: list
|
||||
def filter(l, f) do
|
||||
reduce(reverse(l), [], fn x, acc ->
|
||||
if f.(x), do: [x | acc], else: acc
|
||||
end)
|
||||
end
|
||||
|
||||
@spec append(list, list) :: list
|
||||
def append(a, b) do
|
||||
reduce(reverse(a), b, &[&1 | &2])
|
||||
end
|
||||
|
||||
@spec concat([[any]]) :: [any]
|
||||
def concat(ll) do
|
||||
reduce(reverse(ll), [], &append(&1, &2))
|
||||
end
|
||||
end
|
|
@ -0,0 +1,142 @@
|
|||
if !System.get_env("EXERCISM_TEST_EXAMPLES") do
|
||||
Code.load_file("list_ops.exs", __DIR__)
|
||||
end
|
||||
|
||||
ExUnit.start()
|
||||
ExUnit.configure(trace: true)
|
||||
|
||||
defmodule ListOpsTest do
|
||||
alias ListOps, as: L
|
||||
|
||||
use ExUnit.Case
|
||||
|
||||
defp odd?(n), do: rem(n, 2) == 1
|
||||
|
||||
# @tag :pending
|
||||
test "count of empty list" do
|
||||
assert L.count([]) == 0
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "count of normal list" do
|
||||
assert L.count([1, 3, 5, 7]) == 4
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "count of huge list" do
|
||||
assert L.count(Enum.to_list(1..1_000_000)) == 1_000_000
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "reverse of empty list" do
|
||||
assert L.reverse([]) == []
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "reverse of normal list" do
|
||||
assert L.reverse([1, 3, 5, 7]) == [7, 5, 3, 1]
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "reverse of huge list" do
|
||||
assert L.reverse(Enum.to_list(1..1_000_000)) == Enum.to_list(1_000_000..1)
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "map of empty list" do
|
||||
assert L.map([], &(&1 + 1)) == []
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "map of normal list" do
|
||||
assert L.map([1, 3, 5, 7], &(&1 + 1)) == [2, 4, 6, 8]
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "map of huge list" do
|
||||
assert L.map(Enum.to_list(1..1_000_000), &(&1 + 1)) == Enum.to_list(2..1_000_001)
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "filter of empty list" do
|
||||
assert L.filter([], &odd?/1) == []
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "filter of normal list" do
|
||||
assert L.filter([1, 2, 3, 4], &odd?/1) == [1, 3]
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "filter of huge list" do
|
||||
assert L.filter(Enum.to_list(1..1_000_000), &odd?/1) == Enum.map(1..500_000, &(&1 * 2 - 1))
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "reduce of empty list" do
|
||||
assert L.reduce([], 0, &(&1 + &2)) == 0
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "reduce of normal list" do
|
||||
assert L.reduce([1, 2, 3, 4], -3, &(&1 + &2)) == 7
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "reduce of huge list" do
|
||||
assert L.reduce(Enum.to_list(1..1_000_000), 0, &(&1 + &2)) ==
|
||||
Enum.reduce(1..1_000_000, 0, &(&1 + &2))
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "reduce with non-commutative function" do
|
||||
assert L.reduce([1, 2, 3, 4], 10, fn x, acc -> acc - x end) == 0
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "append of empty lists" do
|
||||
assert L.append([], []) == []
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "append of empty and non-empty list" do
|
||||
assert L.append([], [1, 2, 3, 4]) == [1, 2, 3, 4]
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "append of non-empty and empty list" do
|
||||
assert L.append([1, 2, 3, 4], []) == [1, 2, 3, 4]
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "append of non-empty lists" do
|
||||
assert L.append([1, 2, 3], [4, 5]) == [1, 2, 3, 4, 5]
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "append of huge lists" do
|
||||
assert L.append(Enum.to_list(1..1_000_000), Enum.to_list(1_000_001..2_000_000)) ==
|
||||
Enum.to_list(1..2_000_000)
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "concat of empty list of lists" do
|
||||
assert L.concat([]) == []
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "concat of normal list of lists" do
|
||||
assert L.concat([[1, 2], [3], [], [4, 5, 6]]) == [1, 2, 3, 4, 5, 6]
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "concat of huge list of small lists" do
|
||||
assert L.concat(Enum.map(1..1_000_000, &[&1])) == Enum.to_list(1..1_000_000)
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "concat of small list of huge lists" do
|
||||
assert L.concat(Enum.map(0..9, &Enum.to_list((&1 * 100_000 + 1)..((&1 + 1) * 100_000)))) ==
|
||||
Enum.to_list(1..1_000_000)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,107 @@
|
|||
# Tournament
|
||||
|
||||
Tally the results of a small football competition.
|
||||
|
||||
Based on an input file containing which team played against which and what the
|
||||
outcome was, create a file with a table like this:
|
||||
|
||||
```text
|
||||
Team | MP | W | D | L | P
|
||||
Devastating Donkeys | 3 | 2 | 1 | 0 | 7
|
||||
Allegoric Alaskans | 3 | 2 | 0 | 1 | 6
|
||||
Blithering Badgers | 3 | 1 | 0 | 2 | 3
|
||||
Courageous Californians | 3 | 0 | 1 | 2 | 1
|
||||
```
|
||||
|
||||
What do those abbreviations mean?
|
||||
|
||||
- MP: Matches Played
|
||||
- W: Matches Won
|
||||
- D: Matches Drawn (Tied)
|
||||
- L: Matches Lost
|
||||
- P: Points
|
||||
|
||||
A win earns a team 3 points. A draw earns 1. A loss earns 0.
|
||||
|
||||
The outcome should be ordered by points, descending. In case of a tie, teams are ordered alphabetically.
|
||||
|
||||
###
|
||||
|
||||
Input
|
||||
|
||||
Your tallying program will receive input that looks like:
|
||||
|
||||
```text
|
||||
Allegoric Alaskans;Blithering Badgers;win
|
||||
Devastating Donkeys;Courageous Californians;draw
|
||||
Devastating Donkeys;Allegoric Alaskans;win
|
||||
Courageous Californians;Blithering Badgers;loss
|
||||
Blithering Badgers;Devastating Donkeys;loss
|
||||
Allegoric Alaskans;Courageous Californians;win
|
||||
```
|
||||
|
||||
The result of the match refers to the first team listed. So this line
|
||||
|
||||
```text
|
||||
Allegoric Alaskans;Blithering Badgers;win
|
||||
```
|
||||
|
||||
Means that the Allegoric Alaskans beat the Blithering Badgers.
|
||||
|
||||
This line:
|
||||
|
||||
```text
|
||||
Courageous Californians;Blithering Badgers;loss
|
||||
```
|
||||
|
||||
Means that the Blithering Badgers beat the Courageous Californians.
|
||||
|
||||
And this line:
|
||||
|
||||
```text
|
||||
Devastating Donkeys;Courageous Californians;draw
|
||||
```
|
||||
|
||||
Means that the Devastating Donkeys and Courageous Californians tied.
|
||||
|
||||
Formatting the output is easy with `String`'s padding functions. All number
|
||||
columns can be left-padded with spaces to a width of 2 characters, while the
|
||||
team name column can be right-padded with spaces to a width of 30.
|
||||
|
||||
|
||||
## Running tests
|
||||
|
||||
Execute the tests with:
|
||||
|
||||
```bash
|
||||
$ elixir tournament_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).
|
||||
|
||||
## Submitting Incomplete Solutions
|
||||
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
|
|
@ -0,0 +1,58 @@
|
|||
defmodule Tournament do
|
||||
defmodule Stat do
|
||||
defstruct mp: 0, w: 0, d: 0, l: 0, p: 0
|
||||
end
|
||||
|
||||
@doc """
|
||||
Given `input` lines representing two teams and whether the first of them won,
|
||||
lost, or reached a draw, separated by semicolons, calculate the statistics
|
||||
for each team's number of games played, won, drawn, lost, and total points
|
||||
for the season, and return a nicely-formatted string table.
|
||||
|
||||
A win earns a team 3 points, a draw earns 1 point, and a loss earns nothing.
|
||||
|
||||
Order the outcome by most total points for the season, and settle ties by
|
||||
listing the teams in alphabetical order.
|
||||
"""
|
||||
@spec tally(input :: list(String.t())) :: String.t()
|
||||
def tally(input) do
|
||||
import String, only: [pad_trailing: 2, pad_leading: 2]
|
||||
|
||||
input
|
||||
|> Enum.map(&String.split(&1, ";"))
|
||||
|> Enum.filter(&(length(&1) == 3 && Enum.at(&1, 2) in ["win", "loss", "draw"]))
|
||||
|> Enum.reduce(%{}, fn [team1, team2, outcome], acc ->
|
||||
acc
|
||||
|> Map.put(team1, stats(Map.get(acc, team1, %Stat{}), outcome))
|
||||
|> Map.put(team2, stats(Map.get(acc, team2, %Stat{}), opposite_outcome(outcome)))
|
||||
end)
|
||||
|> Enum.sort_by(fn {name, hist} -> {-hist.p, name} end)
|
||||
|> Enum.reduce("Team | MP | W | D | L | P", fn {name, hist}, acc ->
|
||||
acc <>
|
||||
"\n" <>
|
||||
Enum.join(
|
||||
[
|
||||
pad_trailing(name, 30),
|
||||
pad_leading(to_string(hist.mp), 2),
|
||||
pad_leading(to_string(hist.w), 2),
|
||||
pad_leading(to_string(hist.d), 2),
|
||||
pad_leading(to_string(hist.l), 2),
|
||||
pad_leading(to_string(hist.p), 2)
|
||||
],
|
||||
" | "
|
||||
)
|
||||
end)
|
||||
end
|
||||
|
||||
defp stats(team, "win"), do: %{team | mp: team.mp + 1, w: team.w + 1, p: team.p + 3}
|
||||
defp stats(team, "loss"), do: %{team | mp: team.mp + 1, l: team.l + 1}
|
||||
defp stats(team, "draw"), do: %{team | mp: team.mp + 1, d: team.d + 1, p: team.p + 1}
|
||||
|
||||
defp opposite_outcome(outcome) do
|
||||
case outcome do
|
||||
"win" -> "loss"
|
||||
"loss" -> "win"
|
||||
"draw" -> "draw"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,103 @@
|
|||
if !System.get_env("EXERCISM_TEST_EXAMPLES") do
|
||||
Code.load_file("tournament.exs", __DIR__)
|
||||
end
|
||||
|
||||
ExUnit.start()
|
||||
ExUnit.configure(trace: true)
|
||||
|
||||
defmodule TournamentTest do
|
||||
use ExUnit.Case
|
||||
|
||||
# @tag :pending
|
||||
test "typical input" do
|
||||
input = [
|
||||
"Allegoric Alaskans;Blithering Badgers;win",
|
||||
"Devastating Donkeys;Courageous Californians;draw",
|
||||
"Devastating Donkeys;Allegoric Alaskans;win",
|
||||
"Courageous Californians;Blithering Badgers;loss",
|
||||
"Blithering Badgers;Devastating Donkeys;loss",
|
||||
"Allegoric Alaskans;Courageous Californians;win"
|
||||
]
|
||||
|
||||
expected =
|
||||
"""
|
||||
Team | MP | W | D | L | P
|
||||
Devastating Donkeys | 3 | 2 | 1 | 0 | 7
|
||||
Allegoric Alaskans | 3 | 2 | 0 | 1 | 6
|
||||
Blithering Badgers | 3 | 1 | 0 | 2 | 3
|
||||
Courageous Californians | 3 | 0 | 1 | 2 | 1
|
||||
"""
|
||||
|> String.trim()
|
||||
|
||||
assert Tournament.tally(input) == expected
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "incomplete competition (not all pairs have played)" do
|
||||
input = [
|
||||
"Allegoric Alaskans;Blithering Badgers;loss",
|
||||
"Devastating Donkeys;Allegoric Alaskans;loss",
|
||||
"Courageous Californians;Blithering Badgers;draw",
|
||||
"Allegoric Alaskans;Courageous Californians;win"
|
||||
]
|
||||
|
||||
expected =
|
||||
"""
|
||||
Team | MP | W | D | L | P
|
||||
Allegoric Alaskans | 3 | 2 | 0 | 1 | 6
|
||||
Blithering Badgers | 2 | 1 | 1 | 0 | 4
|
||||
Courageous Californians | 2 | 0 | 1 | 1 | 1
|
||||
Devastating Donkeys | 1 | 0 | 0 | 1 | 0
|
||||
"""
|
||||
|> String.trim()
|
||||
|
||||
assert Tournament.tally(input) == expected
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "ties broken alphabetically" do
|
||||
input = [
|
||||
"Courageous Californians;Devastating Donkeys;win",
|
||||
"Allegoric Alaskans;Blithering Badgers;win",
|
||||
"Devastating Donkeys;Allegoric Alaskans;loss",
|
||||
"Courageous Californians;Blithering Badgers;win",
|
||||
"Blithering Badgers;Devastating Donkeys;draw",
|
||||
"Allegoric Alaskans;Courageous Californians;draw"
|
||||
]
|
||||
|
||||
expected =
|
||||
"""
|
||||
Team | MP | W | D | L | P
|
||||
Allegoric Alaskans | 3 | 2 | 1 | 0 | 7
|
||||
Courageous Californians | 3 | 2 | 1 | 0 | 7
|
||||
Blithering Badgers | 3 | 0 | 1 | 2 | 1
|
||||
Devastating Donkeys | 3 | 0 | 1 | 2 | 1
|
||||
"""
|
||||
|> String.trim()
|
||||
|
||||
assert Tournament.tally(input) == expected
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "mostly invalid lines" do
|
||||
# Invalid input lines in an otherwise-valid game still results in valid
|
||||
# output.
|
||||
input = [
|
||||
"",
|
||||
"Allegoric Alaskans@Blithering Badgers;draw",
|
||||
"Blithering Badgers;Devastating Donkeys;loss",
|
||||
"Devastating Donkeys;Courageous Californians;win;5",
|
||||
"Courageous Californians;Allegoric Alaskans;los"
|
||||
]
|
||||
|
||||
expected =
|
||||
"""
|
||||
Team | MP | W | D | L | P
|
||||
Devastating Donkeys | 1 | 1 | 0 | 0 | 3
|
||||
Blithering Badgers | 1 | 0 | 0 | 1 | 0
|
||||
"""
|
||||
|> String.trim()
|
||||
|
||||
assert Tournament.tally(input) == expected
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue