mirror of
https://github.com/rjNemo/exercism-elixir
synced 2026-06-06 02:16:48 +00:00
repo init
This commit is contained in:
commit
d0f60e4a91
174 changed files with 5361 additions and 0 deletions
19
darts/.exercism/config.json
Normal file
19
darts/.exercism/config.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"authors": ["jiegillet"],
|
||||
"contributors": [
|
||||
"angelikatyborska"
|
||||
],
|
||||
"files": {
|
||||
"example": [
|
||||
".meta/example.ex"
|
||||
],
|
||||
"solution": [
|
||||
"lib/darts.ex"
|
||||
],
|
||||
"test": [
|
||||
"test/darts_test.exs"
|
||||
]
|
||||
},
|
||||
"blurb": "Write a function that returns the earned points in a single toss of a Darts game.",
|
||||
"source": "Inspired by an exercise created by a professor Della Paolera in Argentina"
|
||||
}
|
||||
1
darts/.exercism/metadata.json
Normal file
1
darts/.exercism/metadata.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"track":"elixir","exercise":"darts","id":"b980033b8c73461ca0633fa55a13cbf6","url":"https://exercism.org/tracks/elixir/exercises/darts","handle":"rjNemo","is_requester":true,"auto_approve":false}
|
||||
4
darts/.formatter.exs
Normal file
4
darts/.formatter.exs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Used by "mix format"
|
||||
[
|
||||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||
]
|
||||
75
darts/HELP.md
Normal file
75
darts/HELP.md
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
# Help
|
||||
|
||||
## Running the tests
|
||||
|
||||
From the terminal, change to the base directory of the exercise then execute the tests with:
|
||||
|
||||
```bash
|
||||
$ mix test
|
||||
```
|
||||
|
||||
This will execute the test file found in the `test` subfolder -- a file ending in `_test.exs`
|
||||
|
||||
Documentation:
|
||||
|
||||
* [`mix test` - Elixir's test execution tool](https://hexdocs.pm/mix/Mix.Tasks.Test.html)
|
||||
* [`ExUnit` - Elixir's unit test library](https://hexdocs.pm/ex_unit/ExUnit.html)
|
||||
|
||||
## Pending tests
|
||||
|
||||
In test suites of practice exercises, all but the first test have been tagged to be 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
|
||||
```
|
||||
|
||||
If you wish to run all tests at once, you can include all skipped test by using the `--include` flag on the `mix test` command:
|
||||
|
||||
```bash
|
||||
$ mix test --include pending
|
||||
```
|
||||
|
||||
Or, you can enable all the tests by commenting out the `ExUnit.configure` line in the file `test/test_helper.exs`.
|
||||
|
||||
```elixir
|
||||
# ExUnit.configure(exclude: :pending, trace: true)
|
||||
```
|
||||
|
||||
## Useful `mix test` options
|
||||
|
||||
* `test/<FILE>.exs:LINENUM` - runs only a single test, the test from `<FILE>.exs` whose definition is on line `LINENUM`
|
||||
* `--failed` - runs only tests that failed the last time they ran
|
||||
* `--max-failures` - the suite stops evaluating tests when this number of test failures
|
||||
is reached
|
||||
* `--seed 0` - disables randomization so the tests in a single file will always be ran
|
||||
in the same order they were defined in
|
||||
|
||||
## Submitting your solution
|
||||
|
||||
You can submit your solution using the `exercism submit lib/darts.ex` command.
|
||||
This command will upload your solution to the Exercism website and print the solution page's URL.
|
||||
|
||||
It's possible to submit an incomplete solution which allows you to:
|
||||
|
||||
- See how others have completed the exercise
|
||||
- Request help from a mentor
|
||||
|
||||
## Need to get help?
|
||||
|
||||
If you'd like help solving the exercise, check the following pages:
|
||||
|
||||
- The [Elixir track's documentation](https://exercism.org/docs/tracks/elixir)
|
||||
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
|
||||
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
|
||||
|
||||
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
|
||||
|
||||
If you're stuck on something, it may help to look at some of the [available resources](https://exercism.org/docs/tracks/elixir/resources) out there where answers might be found.
|
||||
If you can't find what you're looking for in the documentation, feel free to ask help in the Exercism's BEAM [gitter channel](https://gitter.im/exercism/xerlang).
|
||||
7
darts/HINTS.md
Normal file
7
darts/HINTS.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Hints
|
||||
|
||||
## General
|
||||
|
||||
- The distance of a point `(x, y)` from the center of the target `(0, 0)` can be calculated with `sqrt(x*x + y *y)`
|
||||
|
||||
- You can calculate the square root of `x` by raising it to the power of `1/2`. In other words, `sqrt(x) == pow(x, 0.5)`
|
||||
37
darts/README.md
Normal file
37
darts/README.md
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# Darts
|
||||
|
||||
Welcome to Darts on Exercism's Elixir Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :)
|
||||
|
||||
## Instructions
|
||||
|
||||
Write a function that returns the earned points in a single toss of a Darts game.
|
||||
|
||||
[Darts](https://en.wikipedia.org/wiki/Darts) is a game where players
|
||||
throw darts to a [target](https://en.wikipedia.org/wiki/Darts#/media/File:Darts_in_a_dartboard.jpg).
|
||||
|
||||
In our particular instance of the game, the target rewards with 4 different amounts of points, depending on where the dart lands:
|
||||
|
||||
* If the dart lands outside the target, player earns no points (0 points).
|
||||
* If the dart lands in the outer circle of the target, player earns 1 point.
|
||||
* If the dart lands in the middle circle of the target, player earns 5 points.
|
||||
* If the dart lands in the inner circle of the target, player earns 10 points.
|
||||
|
||||
The outer circle has a radius of 10 units (This is equivalent to the total radius for the entire target), the middle circle a radius of 5 units, and the inner circle a radius of 1. Of course, they are all centered to the same point (That is, the circles are [concentric](http://mathworld.wolfram.com/ConcentricCircles.html)) defined by the coordinates (0, 0).
|
||||
|
||||
Write a function that given a point in the target (defined by its `real` cartesian coordinates `x` and `y`), returns the correct amount earned by a dart landing in that point.
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @jiegillet
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @angelikatyborska
|
||||
|
||||
### Based on
|
||||
|
||||
Inspired by an exercise created by a professor Della Paolera in Argentina
|
||||
BIN
darts/_build/test/lib/darts/.mix/.mix_test_failures
Normal file
BIN
darts/_build/test/lib/darts/.mix/.mix_test_failures
Normal file
Binary file not shown.
BIN
darts/_build/test/lib/darts/.mix/compile.app_tracer
Normal file
BIN
darts/_build/test/lib/darts/.mix/compile.app_tracer
Normal file
Binary file not shown.
BIN
darts/_build/test/lib/darts/.mix/compile.elixir
Normal file
BIN
darts/_build/test/lib/darts/.mix/compile.elixir
Normal file
Binary file not shown.
BIN
darts/_build/test/lib/darts/.mix/compile.elixir_scm
Normal file
BIN
darts/_build/test/lib/darts/.mix/compile.elixir_scm
Normal file
Binary file not shown.
BIN
darts/_build/test/lib/darts/.mix/compile.protocols
Normal file
BIN
darts/_build/test/lib/darts/.mix/compile.protocols
Normal file
Binary file not shown.
BIN
darts/_build/test/lib/darts/consolidated/Elixir.Collectable.beam
Normal file
BIN
darts/_build/test/lib/darts/consolidated/Elixir.Collectable.beam
Normal file
Binary file not shown.
BIN
darts/_build/test/lib/darts/consolidated/Elixir.Enumerable.beam
Normal file
BIN
darts/_build/test/lib/darts/consolidated/Elixir.Enumerable.beam
Normal file
Binary file not shown.
BIN
darts/_build/test/lib/darts/consolidated/Elixir.IEx.Info.beam
Normal file
BIN
darts/_build/test/lib/darts/consolidated/Elixir.IEx.Info.beam
Normal file
Binary file not shown.
BIN
darts/_build/test/lib/darts/consolidated/Elixir.Inspect.beam
Normal file
BIN
darts/_build/test/lib/darts/consolidated/Elixir.Inspect.beam
Normal file
Binary file not shown.
BIN
darts/_build/test/lib/darts/consolidated/Elixir.List.Chars.beam
Normal file
BIN
darts/_build/test/lib/darts/consolidated/Elixir.List.Chars.beam
Normal file
Binary file not shown.
Binary file not shown.
BIN
darts/_build/test/lib/darts/ebin/Elixir.Darts.beam
Normal file
BIN
darts/_build/test/lib/darts/ebin/Elixir.Darts.beam
Normal file
Binary file not shown.
6
darts/_build/test/lib/darts/ebin/darts.app
Normal file
6
darts/_build/test/lib/darts/ebin/darts.app
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{application,darts,
|
||||
[{applications,[kernel,stdlib,elixir,logger]},
|
||||
{description,"darts"},
|
||||
{modules,['Elixir.Darts']},
|
||||
{registered,[]},
|
||||
{vsn,"0.1.0"}]}.
|
||||
20
darts/lib/darts.ex
Normal file
20
darts/lib/darts.ex
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
defmodule Darts do
|
||||
@type position :: {number, number}
|
||||
|
||||
@doc """
|
||||
Calculate the score of a single dart hitting a target
|
||||
"""
|
||||
@spec score(position :: position) :: integer
|
||||
def score({x, y}) when {x, y} == {0, 0}, do: 10
|
||||
|
||||
def score({x, y}) do
|
||||
cond do
|
||||
distance_to_center({x, y}) <= 1 -> 10
|
||||
distance_to_center({x, y}) <= 5 -> 5
|
||||
distance_to_center({x, y}) <= 10 -> 1
|
||||
distance_to_center({x, y}) > 10 -> 0
|
||||
end
|
||||
end
|
||||
|
||||
defp distance_to_center({x, y}), do: :math.sqrt(x ** 2 + y ** 2)
|
||||
end
|
||||
28
darts/mix.exs
Normal file
28
darts/mix.exs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
defmodule Darts.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :darts,
|
||||
version: "0.1.0",
|
||||
# elixir: "~> 1.8",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps()
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help compile.app" to learn about applications.
|
||||
def application do
|
||||
[
|
||||
extra_applications: [:logger]
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help deps" to learn about dependencies.
|
||||
defp deps do
|
||||
[
|
||||
# {:dep_from_hexpm, "~> 0.3.0"},
|
||||
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
|
||||
]
|
||||
end
|
||||
end
|
||||
55
darts/test/darts_test.exs
Normal file
55
darts/test/darts_test.exs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
defmodule DartsTest do
|
||||
use ExUnit.Case
|
||||
|
||||
test "Missed target" do
|
||||
assert Darts.score({-9, 9}) == 0
|
||||
end
|
||||
|
||||
test "On the outer circle" do
|
||||
assert Darts.score({0, 10}) == 1
|
||||
end
|
||||
|
||||
test "On the middle circle" do
|
||||
assert Darts.score({-5, 0}) == 5
|
||||
end
|
||||
|
||||
test "On the inner circle" do
|
||||
assert Darts.score({0, -1}) == 10
|
||||
end
|
||||
|
||||
test "Exactly on centre" do
|
||||
assert Darts.score({0, 0}) == 10
|
||||
end
|
||||
|
||||
test "Near the centre" do
|
||||
assert Darts.score({-0.1, -0.1}) == 10
|
||||
end
|
||||
|
||||
test "Just within the inner circle" do
|
||||
assert Darts.score({0.7, 0.7}) == 10
|
||||
end
|
||||
|
||||
test "Just outside the inner circle" do
|
||||
assert Darts.score({0.8, -0.8}) == 5
|
||||
end
|
||||
|
||||
test "Just within the middle circle" do
|
||||
assert Darts.score({-3.5, 3.5}) == 5
|
||||
end
|
||||
|
||||
test "Just outside the middle circle" do
|
||||
assert Darts.score({-3.6, -3.6}) == 1
|
||||
end
|
||||
|
||||
test "Just within the outer circle" do
|
||||
assert Darts.score({-7.0, 7.0}) == 1
|
||||
end
|
||||
|
||||
test "Just outside the outer circle" do
|
||||
assert Darts.score({7.1, -7.1}) == 0
|
||||
end
|
||||
|
||||
test "Asymmetric position between the inner and middle circles" do
|
||||
assert Darts.score({0.5, -4}) == 5
|
||||
end
|
||||
end
|
||||
2
darts/test/test_helper.exs
Normal file
2
darts/test/test_helper.exs
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
ExUnit.start()
|
||||
ExUnit.configure(exclude: :pending, trace: true)
|
||||
24
freelancer-rates/.exercism/config.json
Normal file
24
freelancer-rates/.exercism/config.json
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"blurb": "Learn about integers and floating point numbers by helping a freelancer communicate with a project manager about billing.",
|
||||
"authors": [
|
||||
"angelikatyborska"
|
||||
],
|
||||
"contributors": [
|
||||
"neenjaw"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"lib/freelancer_rates.ex"
|
||||
],
|
||||
"test": [
|
||||
"test/freelancer_rates_test.exs"
|
||||
],
|
||||
"exemplar": [
|
||||
".meta/exemplar.ex"
|
||||
]
|
||||
},
|
||||
"forked_from": [
|
||||
"javascript/freelancer-rates"
|
||||
],
|
||||
"language_versions": ">=1.10"
|
||||
}
|
||||
1
freelancer-rates/.exercism/metadata.json
Normal file
1
freelancer-rates/.exercism/metadata.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"track":"elixir","exercise":"freelancer-rates","id":"25d0092c368847429fd683c52ca243d7","url":"https://exercism.org/tracks/elixir/exercises/freelancer-rates","handle":"rjNemo","is_requester":true,"auto_approve":false}
|
||||
4
freelancer-rates/.formatter.exs
Normal file
4
freelancer-rates/.formatter.exs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Used by "mix format"
|
||||
[
|
||||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||
]
|
||||
24
freelancer-rates/.gitignore
vendored
Normal file
24
freelancer-rates/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# The directory Mix will write compiled artifacts to.
|
||||
/_build/
|
||||
|
||||
# If you run "mix test --cover", coverage assets end up here.
|
||||
/cover/
|
||||
|
||||
# The directory Mix downloads your dependencies sources to.
|
||||
/deps/
|
||||
|
||||
# Where third-party dependencies like ExDoc output generated docs.
|
||||
/doc/
|
||||
|
||||
# Ignore .fetch files in case you like to edit your project deps locally.
|
||||
/.fetch
|
||||
|
||||
# If the VM crashes, it generates a dump, let's ignore it too.
|
||||
erl_crash.dump
|
||||
|
||||
# Also ignore archive artifacts (built via "mix archive.build").
|
||||
*.ez
|
||||
|
||||
# Ignore package tarball (built via "mix hex.build").
|
||||
numbers-*.tar
|
||||
|
||||
75
freelancer-rates/HELP.md
Normal file
75
freelancer-rates/HELP.md
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
# Help
|
||||
|
||||
## Running the tests
|
||||
|
||||
From the terminal, change to the base directory of the exercise then execute the tests with:
|
||||
|
||||
```bash
|
||||
$ mix test
|
||||
```
|
||||
|
||||
This will execute the test file found in the `test` subfolder -- a file ending in `_test.exs`
|
||||
|
||||
Documentation:
|
||||
|
||||
* [`mix test` - Elixir's test execution tool](https://hexdocs.pm/mix/Mix.Tasks.Test.html)
|
||||
* [`ExUnit` - Elixir's unit test library](https://hexdocs.pm/ex_unit/ExUnit.html)
|
||||
|
||||
## Pending tests
|
||||
|
||||
In test suites of practice exercises, all but the first test have been tagged to be 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
|
||||
```
|
||||
|
||||
If you wish to run all tests at once, you can include all skipped test by using the `--include` flag on the `mix test` command:
|
||||
|
||||
```bash
|
||||
$ mix test --include pending
|
||||
```
|
||||
|
||||
Or, you can enable all the tests by commenting out the `ExUnit.configure` line in the file `test/test_helper.exs`.
|
||||
|
||||
```elixir
|
||||
# ExUnit.configure(exclude: :pending, trace: true)
|
||||
```
|
||||
|
||||
## Useful `mix test` options
|
||||
|
||||
* `test/<FILE>.exs:LINENUM` - runs only a single test, the test from `<FILE>.exs` whose definition is on line `LINENUM`
|
||||
* `--failed` - runs only tests that failed the last time they ran
|
||||
* `--max-failures` - the suite stops evaluating tests when this number of test failures
|
||||
is reached
|
||||
* `--seed 0` - disables randomization so the tests in a single file will always be ran
|
||||
in the same order they were defined in
|
||||
|
||||
## Submitting your solution
|
||||
|
||||
You can submit your solution using the `exercism submit lib/freelancer_rates.ex` command.
|
||||
This command will upload your solution to the Exercism website and print the solution page's URL.
|
||||
|
||||
It's possible to submit an incomplete solution which allows you to:
|
||||
|
||||
- See how others have completed the exercise
|
||||
- Request help from a mentor
|
||||
|
||||
## Need to get help?
|
||||
|
||||
If you'd like help solving the exercise, check the following pages:
|
||||
|
||||
- The [Elixir track's documentation](https://exercism.org/docs/tracks/elixir)
|
||||
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
|
||||
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
|
||||
|
||||
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
|
||||
|
||||
If you're stuck on something, it may help to look at some of the [available resources](https://exercism.org/docs/tracks/elixir/resources) out there where answers might be found.
|
||||
If you can't find what you're looking for in the documentation, feel free to ask help in the Exercism's BEAM [gitter channel](https://gitter.im/exercism/xerlang).
|
||||
30
freelancer-rates/HINTS.md
Normal file
30
freelancer-rates/HINTS.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# Hints
|
||||
|
||||
## General
|
||||
|
||||
- Read about basic arithmetic in the official [Getting Started guide][getting-started-basic-arithmetic].
|
||||
- Browse the [`Float`][float-functions] and [`Kernel`][kernel-arithmetic-operators] modules to learn about functions and operators that work with floats.
|
||||
|
||||
## 1. Calculate the daily rate given an hourly rate
|
||||
|
||||
- [Basic arithmetic operations][kernel-arithmetic-operators] where one argument is an integer, and the other is a float, will return a float.
|
||||
|
||||
## 2. Calculate a discounted price
|
||||
|
||||
- [Basic arithmetic operations][kernel-arithmetic-operators] where one argument is an integer, and the other is a float, will return a float.
|
||||
|
||||
## 3. Calculate the monthly rate, given an hourly rate and a discount
|
||||
|
||||
- There is a [built-in function][kernel-trunc] for changing floats to integers.
|
||||
- There is a [built-in function][float-ceil] for rounding floats up.
|
||||
|
||||
## 4. Calculate the number of workdays given a budget, hourly rate and discount
|
||||
|
||||
- There is a [built-in function][float-floor] for rounding floats down with desired precision.
|
||||
|
||||
[getting-started-basic-arithmetic]: https://elixir-lang.org/getting-started/basic-types.html#basic-arithmetic
|
||||
[kernel-arithmetic-operators]: https://hexdocs.pm/elixir/Kernel.html#*/2
|
||||
[kernel-trunc]: https://hexdocs.pm/elixir/Kernel.html#trunc/1
|
||||
[float-functions]: https://hexdocs.pm/elixir/Float.html#functions
|
||||
[float-ceil]: https://hexdocs.pm/elixir/Float.html#ceil/2
|
||||
[float-floor]: https://hexdocs.pm/elixir/Float.html#floor/2
|
||||
125
freelancer-rates/README.md
Normal file
125
freelancer-rates/README.md
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
# Freelancer Rates
|
||||
|
||||
Welcome to Freelancer Rates on Exercism's Elixir Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :)
|
||||
|
||||
## Introduction
|
||||
|
||||
## Integers
|
||||
|
||||
There are two different kinds of numbers in Elixir - integers and floats.
|
||||
|
||||
Integers are whole numbers.
|
||||
|
||||
```elixir
|
||||
integer = 3
|
||||
# => 3
|
||||
```
|
||||
|
||||
## Floating Point Numbers
|
||||
|
||||
Floats are numbers with one or more digits behind the decimal separator. They use the 64-bit double precision floating-point format.
|
||||
|
||||
```elixir
|
||||
float = 3.45
|
||||
# => 3.45
|
||||
```
|
||||
|
||||
### Working with numbers
|
||||
|
||||
In the [`Integer`][integer-functions] and [`Float`][float-functions] modules you can find some useful functions for working with those types. Basic arithmetic operators are defined in the [`Kernel`][kernel-arithmetic-operators] module.
|
||||
|
||||
### Conversion
|
||||
|
||||
Integers and floats can be mixed together in a single arithmetic expression. Using a float in an expression ensures the result will be a float too.
|
||||
|
||||
```elixir
|
||||
2 * 3
|
||||
# => 6
|
||||
|
||||
2 * 3.0
|
||||
# => 6.0
|
||||
```
|
||||
|
||||
However, when doing division, the result will always be a float, even if only integers are used.
|
||||
|
||||
```elixir
|
||||
6 / 2
|
||||
# => 3.0
|
||||
```
|
||||
|
||||
To convert a float to an integer, you can discard the decimal part with [`trunc/1`][trunc-1].
|
||||
|
||||
[integer-functions]: https://hexdocs.pm/elixir/Integer.html#functions
|
||||
[float-functions]: https://hexdocs.pm/elixir/Float.html#functions
|
||||
[kernel-arithmetic-operators]: https://hexdocs.pm/elixir/Kernel.html#*/2
|
||||
[trunc-1]: https://hexdocs.pm/elixir/Kernel.html#trunc/1
|
||||
|
||||
## Instructions
|
||||
|
||||
In this exercise you'll be writing code to help a freelancer communicate with a project manager by providing a few utilities to quickly calculate daily and
|
||||
monthly rates, optionally with a given discount.
|
||||
|
||||
We first establish a few rules between the freelancer and the project manager:
|
||||
|
||||
- The daily rate is 8 times the hourly rate.
|
||||
- A month has 22 billable days.
|
||||
|
||||
The freelancer is offering to apply a discount if the project manager chooses to let the freelancer bill per month, which can come in handy if there is a certain budget the project manager has to work with.
|
||||
|
||||
Discounts are modeled as fractional numbers representing percentage, for example `25.0` (25%).
|
||||
|
||||
## 1. Calculate the daily rate given an hourly rate
|
||||
|
||||
Implement a function to calculate the daily rate given an hourly rate:
|
||||
|
||||
```elixir
|
||||
FreelancerRates.daily_rate(60)
|
||||
# => 480.0
|
||||
```
|
||||
|
||||
The returned daily rate should be a float.
|
||||
|
||||
## 2. Calculate a discounted price
|
||||
|
||||
Implement a function to calculate the price after a discount.
|
||||
|
||||
```elixir
|
||||
FreelancerRates.apply_discount(150, 10)
|
||||
# => 135.0
|
||||
```
|
||||
|
||||
The returned value should always be a float, not rounded in any way.
|
||||
|
||||
## 3. Calculate the monthly rate, given an hourly rate and a discount
|
||||
|
||||
Implement a function to calculate the monthly rate, and apply a discount:
|
||||
|
||||
```elixir
|
||||
FreelancerRates.monthly_rate(77, 10.5)
|
||||
# => 12130
|
||||
```
|
||||
|
||||
The returned monthly rate should be rounded up (take the ceiling) to the nearest integer.
|
||||
|
||||
## 4. Calculate the number of workdays given a budget, hourly rate and discount
|
||||
|
||||
Implement a function that takes a budget, a hourly rate, and a discount, and calculates how many days of work that covers.
|
||||
|
||||
```elixir
|
||||
FreelancerRates.days_in_budget(20000, 80, 11.0)
|
||||
# => 35.1
|
||||
```
|
||||
|
||||
The returned number of days should be rounded down (take the floor) to one decimal place.
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @angelikatyborska
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @neenjaw
|
||||
11
freelancer-rates/lib/freelancer_rates.ex
Normal file
11
freelancer-rates/lib/freelancer_rates.ex
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
defmodule FreelancerRates do
|
||||
def daily_rate(hourly_rate), do: 8.0 * hourly_rate
|
||||
|
||||
def apply_discount(before_discount, discount), do: before_discount * (100 - discount) / 100
|
||||
|
||||
def monthly_rate(hourly_rate, discount),
|
||||
do: (22 * daily_rate(hourly_rate)) |> apply_discount(discount) |> Float.ceil() |> trunc()
|
||||
|
||||
def days_in_budget(budget, hourly_rate, discount),
|
||||
do: (budget / (daily_rate(hourly_rate) |> apply_discount(discount))) |> Float.floor(1)
|
||||
end
|
||||
28
freelancer-rates/mix.exs
Normal file
28
freelancer-rates/mix.exs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
defmodule FreelancerRates.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :freelancer_rates,
|
||||
version: "0.1.0",
|
||||
# elixir: "~> 1.10",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps()
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help compile.app" to learn about applications.
|
||||
def application do
|
||||
[
|
||||
extra_applications: [:logger]
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help deps" to learn about dependencies.
|
||||
defp deps do
|
||||
[
|
||||
# {:dep_from_hexpm, "~> 0.3.0"},
|
||||
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
|
||||
]
|
||||
end
|
||||
end
|
||||
89
freelancer-rates/test/freelancer_rates_test.exs
Normal file
89
freelancer-rates/test/freelancer_rates_test.exs
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
defmodule FreelancerRatesTest do
|
||||
use ExUnit.Case
|
||||
|
||||
describe "daily_rate/1" do
|
||||
@tag task_id: 1
|
||||
test "it's the hourly_rate times 8" do
|
||||
assert FreelancerRates.daily_rate(50) == 400.0
|
||||
end
|
||||
|
||||
@tag task_id: 1
|
||||
test "it always returns a float" do
|
||||
assert FreelancerRates.daily_rate(60) === 480.0
|
||||
end
|
||||
|
||||
@tag task_id: 1
|
||||
test "it does not round" do
|
||||
assert FreelancerRates.daily_rate(55.1) == 440.8
|
||||
end
|
||||
end
|
||||
|
||||
describe "apply_discount/2" do
|
||||
@tag task_id: 2
|
||||
test "a discount of 10% leaves 90% of the original price" do
|
||||
assert FreelancerRates.apply_discount(140.0, 10) == 126.0
|
||||
end
|
||||
|
||||
@tag task_id: 2
|
||||
test "it always returns a float" do
|
||||
assert FreelancerRates.apply_discount(100, 10) == 90.0
|
||||
end
|
||||
|
||||
@tag task_id: 2
|
||||
test "it doesn't round" do
|
||||
assert_in_delta FreelancerRates.apply_discount(111.11, 13.5), 96.11015, 0.000001
|
||||
end
|
||||
end
|
||||
|
||||
describe "monthly_rate/2" do
|
||||
@tag task_id: 3
|
||||
test "it's the daily_rate times 22" do
|
||||
assert FreelancerRates.monthly_rate(62, 0.0) == 10_912
|
||||
end
|
||||
|
||||
@tag task_id: 3
|
||||
test "it always returns an integer" do
|
||||
assert FreelancerRates.monthly_rate(70, 0.0) === 12_320
|
||||
end
|
||||
|
||||
@tag task_id: 3
|
||||
test "the result is rounded up" do
|
||||
# 11_052.8
|
||||
assert FreelancerRates.monthly_rate(62.8, 0.0) == 11_053
|
||||
# 11_475.2
|
||||
assert FreelancerRates.monthly_rate(65.2, 0.0) == 11_476
|
||||
end
|
||||
|
||||
@tag task_id: 3
|
||||
test "gives a discount" do
|
||||
# 11_792 - 12% * 11_792 = 10_376.96
|
||||
assert FreelancerRates.monthly_rate(67, 12.0) == 10_377
|
||||
end
|
||||
end
|
||||
|
||||
describe "days_in_budget/3" do
|
||||
@tag task_id: 4
|
||||
test "it's the budget divided by the daily rate" do
|
||||
assert FreelancerRates.days_in_budget(1_600, 50, 0.0) == 4
|
||||
end
|
||||
|
||||
@tag task_id: 4
|
||||
test "it always returns a float" do
|
||||
assert FreelancerRates.days_in_budget(520, 65, 0.0) === 1.0
|
||||
end
|
||||
|
||||
@tag task_id: 4
|
||||
test "it rounds down to one decimal place" do
|
||||
# 10.02273
|
||||
assert FreelancerRates.days_in_budget(4_410, 55, 0.0) == 10.0
|
||||
# 10.18182
|
||||
assert FreelancerRates.days_in_budget(4_480, 55, 0.0) == 10.1
|
||||
end
|
||||
|
||||
@tag task_id: 4
|
||||
test "it applies the discount" do
|
||||
# 1.25
|
||||
assert FreelancerRates.days_in_budget(480, 60, 20) == 1.2
|
||||
end
|
||||
end
|
||||
end
|
||||
2
freelancer-rates/test/test_helper.exs
Normal file
2
freelancer-rates/test/test_helper.exs
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
ExUnit.start()
|
||||
ExUnit.configure(exclude: :pending, trace: true, seed: 0)
|
||||
24
guessing-game/.exercism/config.json
Normal file
24
guessing-game/.exercism/config.json
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"blurb": "Learn about multiple clause functions, guards, and default arguments by implementing a simple game in which the player needs to guess a secret number.",
|
||||
"authors": [
|
||||
"neenjaw"
|
||||
],
|
||||
"contributors": [
|
||||
"angelikatyborska"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"lib/guessing_game.ex"
|
||||
],
|
||||
"test": [
|
||||
"test/guessing_game_test.exs"
|
||||
],
|
||||
"exemplar": [
|
||||
".meta/exemplar.ex"
|
||||
]
|
||||
},
|
||||
"forked_from": [
|
||||
"fsharp/guessing-game"
|
||||
],
|
||||
"language_versions": ">=1.10"
|
||||
}
|
||||
1
guessing-game/.exercism/metadata.json
Normal file
1
guessing-game/.exercism/metadata.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"track":"elixir","exercise":"guessing-game","id":"606d4f0377e144889bc9446988f1af64","url":"https://exercism.org/tracks/elixir/exercises/guessing-game","handle":"rjNemo","is_requester":true,"auto_approve":false}
|
||||
4
guessing-game/.formatter.exs
Normal file
4
guessing-game/.formatter.exs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Used by "mix format"
|
||||
[
|
||||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||
]
|
||||
24
guessing-game/.gitignore
vendored
Normal file
24
guessing-game/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# The directory Mix will write compiled artifacts to.
|
||||
/_build/
|
||||
|
||||
# If you run "mix test --cover", coverage assets end up here.
|
||||
/cover/
|
||||
|
||||
# The directory Mix downloads your dependencies sources to.
|
||||
/deps/
|
||||
|
||||
# Where third-party dependencies like ExDoc output generated docs.
|
||||
/doc/
|
||||
|
||||
# Ignore .fetch files in case you like to edit your project deps locally.
|
||||
/.fetch
|
||||
|
||||
# If the VM crashes, it generates a dump, let's ignore it too.
|
||||
erl_crash.dump
|
||||
|
||||
# Also ignore archive artifacts (built via "mix archive.build").
|
||||
*.ez
|
||||
|
||||
# Ignore package tarball (built via "mix hex.build").
|
||||
multiple_clause_functions-*.tar
|
||||
|
||||
75
guessing-game/HELP.md
Normal file
75
guessing-game/HELP.md
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
# Help
|
||||
|
||||
## Running the tests
|
||||
|
||||
From the terminal, change to the base directory of the exercise then execute the tests with:
|
||||
|
||||
```bash
|
||||
$ mix test
|
||||
```
|
||||
|
||||
This will execute the test file found in the `test` subfolder -- a file ending in `_test.exs`
|
||||
|
||||
Documentation:
|
||||
|
||||
* [`mix test` - Elixir's test execution tool](https://hexdocs.pm/mix/Mix.Tasks.Test.html)
|
||||
* [`ExUnit` - Elixir's unit test library](https://hexdocs.pm/ex_unit/ExUnit.html)
|
||||
|
||||
## Pending tests
|
||||
|
||||
In test suites of practice exercises, all but the first test have been tagged to be 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
|
||||
```
|
||||
|
||||
If you wish to run all tests at once, you can include all skipped test by using the `--include` flag on the `mix test` command:
|
||||
|
||||
```bash
|
||||
$ mix test --include pending
|
||||
```
|
||||
|
||||
Or, you can enable all the tests by commenting out the `ExUnit.configure` line in the file `test/test_helper.exs`.
|
||||
|
||||
```elixir
|
||||
# ExUnit.configure(exclude: :pending, trace: true)
|
||||
```
|
||||
|
||||
## Useful `mix test` options
|
||||
|
||||
* `test/<FILE>.exs:LINENUM` - runs only a single test, the test from `<FILE>.exs` whose definition is on line `LINENUM`
|
||||
* `--failed` - runs only tests that failed the last time they ran
|
||||
* `--max-failures` - the suite stops evaluating tests when this number of test failures
|
||||
is reached
|
||||
* `--seed 0` - disables randomization so the tests in a single file will always be ran
|
||||
in the same order they were defined in
|
||||
|
||||
## Submitting your solution
|
||||
|
||||
You can submit your solution using the `exercism submit lib/guessing_game.ex` command.
|
||||
This command will upload your solution to the Exercism website and print the solution page's URL.
|
||||
|
||||
It's possible to submit an incomplete solution which allows you to:
|
||||
|
||||
- See how others have completed the exercise
|
||||
- Request help from a mentor
|
||||
|
||||
## Need to get help?
|
||||
|
||||
If you'd like help solving the exercise, check the following pages:
|
||||
|
||||
- The [Elixir track's documentation](https://exercism.org/docs/tracks/elixir)
|
||||
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
|
||||
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
|
||||
|
||||
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
|
||||
|
||||
If you're stuck on something, it may help to look at some of the [available resources](https://exercism.org/docs/tracks/elixir/resources) out there where answers might be found.
|
||||
If you can't find what you're looking for in the documentation, feel free to ask help in the Exercism's BEAM [gitter channel](https://gitter.im/exercism/xerlang).
|
||||
33
guessing-game/HINTS.md
Normal file
33
guessing-game/HINTS.md
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# Hints
|
||||
|
||||
## General
|
||||
|
||||
- In Elixir's ['Getting Started Guide'][guide] there is a nice refresher about named functions.
|
||||
|
||||
## 1. Make the response when the guess matches the secret number
|
||||
|
||||
- You can use a [guard][guard] to check if the numbers are the same with `===/2`.
|
||||
|
||||
## 2. Make the response when the guess is greater than the secret number
|
||||
|
||||
- You can add a [function clause][multiple-fn-clauses] and [guards][guard] to check if the guess is greater `>/2` than the secret number.
|
||||
|
||||
## 3. Make the response when the guess is less than the secret number
|
||||
|
||||
- You can add a [function clause][multiple-fn-clauses] and [guards][guard] to check if the guess is less than `</2` the secret number.
|
||||
|
||||
## 4. Make the responses when the guess is one more or one less than the secret number
|
||||
|
||||
- You can add a [function clause][multiple-fn-clauses] and [guards][guard] to check if the guess is one less or one more than the secret number.
|
||||
- Guards expressions can use `and/2`, or `or/2` to combine boolean expressions.
|
||||
- Pay attention to the order of the function clauses.
|
||||
|
||||
## 5. Make the response when there is no guess
|
||||
|
||||
- You can make use of a [default argument][default-arg] for a guess. The default value does not have to be an integer.
|
||||
- Use a function header before all the other function clauses to define the default argument.
|
||||
|
||||
[default-arg]: https://elixir-lang.org/getting-started/modules-and-functions.html#default-arguments
|
||||
[guard]: https://hexdocs.pm/elixir/master/Kernel.html#guards
|
||||
[guide]: https://elixir-lang.org/getting-started/modules-and-functions.html#named-functions
|
||||
[multiple-fn-clauses]: https://elixir-lang.org/getting-started/modules-and-functions.html#named-functions
|
||||
131
guessing-game/README.md
Normal file
131
guessing-game/README.md
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
# Guessing Game
|
||||
|
||||
Welcome to Guessing Game on Exercism's Elixir Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :)
|
||||
|
||||
## Introduction
|
||||
|
||||
## Multiple Clause Functions
|
||||
|
||||
Elixir facilitates **Open-Close Principle** practices by allowing functions to have multiple clauses, so instead of sprawling and hard-coded control-logic, pointed functions can be written to add/remove behavior easily.
|
||||
|
||||
Elixir offers _multiple function clauses_ and _guards_ to write:
|
||||
|
||||
```elixir
|
||||
def number(n) when n == 7 do
|
||||
"Awesome, that's my favorite"
|
||||
end
|
||||
def number(_n) do
|
||||
"That's not my favorite"
|
||||
end
|
||||
```
|
||||
|
||||
At run-time, Elixir will test, from top to bottom of the source file, which function clause to invoke.
|
||||
|
||||
Variables that are unused should be prefixed with an underscore.
|
||||
|
||||
## Guards
|
||||
|
||||
Guards are used to prevent Elixir from invoking functions based on evaluation of the arguments by guard functions. Guards begin with the `when` keyword, followed by a boolean expression. Guard functions are special functions which:
|
||||
|
||||
- Must be pure and not mutate any global states.
|
||||
- Must return strict `true` or `false` values.
|
||||
|
||||
A list of common guards can be found in the [Elixir documentation][kernel-guards]. It includes type checks, basic arithmetic, comparisons, and strictly boolean operators.
|
||||
|
||||
## Default Arguments
|
||||
|
||||
Functions may declare default values for one or more arguments. Let's consider this function:
|
||||
|
||||
```elixir
|
||||
def number(n \\ 13), do: "That's not my favorite"
|
||||
```
|
||||
|
||||
When compiled, Elixir creates a function definition for `number/0` (no arguments), and `number/1` (one argument).
|
||||
|
||||
If more than one argument has default values, the default values will be applied to the function from left to right to fill in for missing arguments.
|
||||
|
||||
If the function has more than one clause, the default arguments should be defined in a function header (a function without a body) before the function clauses:
|
||||
|
||||
```elixir
|
||||
def number(n \\ 13)
|
||||
def number(n) when n < 10, do: "Dream bigger!"
|
||||
def number(n) when n > 100, do: "Not that big..."
|
||||
```
|
||||
|
||||
[kernel-guards]: https://hexdocs.pm/elixir/master/Kernel.html#guards
|
||||
|
||||
## Instructions
|
||||
|
||||
You are creating a trivial online game where a friend can guess a secret number. You want to give some feedback, but not give away the answer with a guess. You need to devise a function to provide different responses depending on how the guess relates to the secret number.
|
||||
|
||||
| Condition | Response |
|
||||
| ------------------------------------------------------------- | -------------- |
|
||||
| When the guess matches the secret number | "Correct" |
|
||||
| When the guess is one more or one less than the secret number | "So close" |
|
||||
| When the guess is greater than the secret number | "Too high" |
|
||||
| When the guess is less than the secret number | "Too low" |
|
||||
| When a guess isn't made | "Make a guess" |
|
||||
|
||||
All guesses and secret numbers are integer numbers.
|
||||
|
||||
## 1. Make the response when the guess matches the secret number
|
||||
|
||||
Implement the `compare/2` function which takes two arguments, `secret_number` and `guess`, which are both integers.
|
||||
|
||||
```elixir
|
||||
GuessingGame.compare(5, 5)
|
||||
# => "Correct"
|
||||
```
|
||||
|
||||
## 2. Make the response when the guess is greater than the secret number
|
||||
|
||||
Modify the `compare` function to respond to guesses that are higher than the secret number.
|
||||
|
||||
```elixir
|
||||
GuessingGame.compare(5, 8)
|
||||
# => "Too high"
|
||||
```
|
||||
|
||||
## 3. Make the response when the guess is less than the secret number
|
||||
|
||||
Modify the `compare` function to respond to guesses that are lower than the secret number.
|
||||
|
||||
```elixir
|
||||
GuessingGame.compare(5, 2)
|
||||
# => "Too low"
|
||||
```
|
||||
|
||||
## 4. Make the responses when the guess is one more or one less than the secret number
|
||||
|
||||
Modify the `compare` function to respond to guesses that are close to the secret number.
|
||||
|
||||
```elixir
|
||||
GuessingGame.compare(5, 6)
|
||||
# => "So close"
|
||||
GuessingGame.compare(5, 4)
|
||||
# => "So close"
|
||||
```
|
||||
|
||||
## 5. Make the response when there is no guess
|
||||
|
||||
Modify the `compare` function to respond to a lack of guess.
|
||||
|
||||
```elixir
|
||||
GuessingGame.compare(5)
|
||||
# => "Make a guess"
|
||||
|
||||
GuessingGame.compare(5, :no_guess)
|
||||
# => "Make a guess"
|
||||
```
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @neenjaw
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @angelikatyborska
|
||||
8
guessing-game/lib/guessing_game.ex
Normal file
8
guessing-game/lib/guessing_game.ex
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
defmodule GuessingGame do
|
||||
def compare(secret_number, guess \\ :no_guess)
|
||||
def compare(_, guess) when guess == :no_guess, do: "Make a guess"
|
||||
def compare(secret_number, guess) when guess == secret_number, do: "Correct"
|
||||
def compare(secret_number, guess) when abs(guess - secret_number) == 1, do: "So close"
|
||||
def compare(secret_number, guess) when guess > secret_number, do: "Too high"
|
||||
def compare(secret_number, guess) when guess < secret_number, do: "Too low"
|
||||
end
|
||||
28
guessing-game/mix.exs
Normal file
28
guessing-game/mix.exs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
defmodule GuessingGame.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :guessing_game,
|
||||
version: "0.1.0",
|
||||
# elixir: "~> 1.10",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps()
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help compile.app" to learn about applications.
|
||||
def application do
|
||||
[
|
||||
extra_applications: [:logger]
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help deps" to learn about dependencies.
|
||||
defp deps do
|
||||
[
|
||||
# {:dep_from_hexpm, "~> 0.3.0"},
|
||||
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
|
||||
]
|
||||
end
|
||||
end
|
||||
38
guessing-game/test/guessing_game_test.exs
Normal file
38
guessing-game/test/guessing_game_test.exs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
defmodule GuessingGameTest do
|
||||
use ExUnit.Case
|
||||
|
||||
@tag task_id: 1
|
||||
test "correct when the guessed number equals secret" do
|
||||
assert GuessingGame.compare(7, 7) == "Correct"
|
||||
end
|
||||
|
||||
@tag task_id: 2
|
||||
test "too high when guessed number is greater than the secret" do
|
||||
assert GuessingGame.compare(9, 18) == "Too high"
|
||||
end
|
||||
|
||||
@tag task_id: 3
|
||||
test "too low when guessed number is less than the secret" do
|
||||
# assert GuessingGame.compare(42, 30) == "Too low"
|
||||
end
|
||||
|
||||
@tag task_id: 4
|
||||
test "so close when guess differs from secret by -1" do
|
||||
assert GuessingGame.compare(64, 63) == "So close"
|
||||
end
|
||||
|
||||
@tag task_id: 4
|
||||
test "so close when guess differs from secret by +1" do
|
||||
assert GuessingGame.compare(52, 53) == "So close"
|
||||
end
|
||||
|
||||
@tag task_id: 5
|
||||
test "when no guess is supplied, ask the player to make a guess" do
|
||||
assert GuessingGame.compare(15) == "Make a guess"
|
||||
end
|
||||
|
||||
@tag task_id: 5
|
||||
test "when the atom :no_guess is supplied, ask the player to make a guess" do
|
||||
assert GuessingGame.compare(16, :no_guess) == "Make a guess"
|
||||
end
|
||||
end
|
||||
2
guessing-game/test/test_helper.exs
Normal file
2
guessing-game/test/test_helper.exs
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
ExUnit.start()
|
||||
ExUnit.configure(exclude: :pending, trace: true, seed: 0)
|
||||
34
hello-world/.exercism/config.json
Normal file
34
hello-world/.exercism/config.json
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"blurb": "The classical introductory exercise. Just say \"Hello, World!\".",
|
||||
"authors": [],
|
||||
"contributors": [
|
||||
"angelikatyborska",
|
||||
"Cohen-Carlisle",
|
||||
"dalexj",
|
||||
"devonestes",
|
||||
"George-Hudson",
|
||||
"hanmd82",
|
||||
"lpil",
|
||||
"neenjaw",
|
||||
"parkerl",
|
||||
"rawkode",
|
||||
"rubysolo",
|
||||
"sotojuan",
|
||||
"Teapane",
|
||||
"Thrillberg",
|
||||
"waiting-for-dev"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"lib/hello_world.ex"
|
||||
],
|
||||
"test": [
|
||||
"test/hello_world_test.exs"
|
||||
],
|
||||
"example": [
|
||||
".meta/example.ex"
|
||||
]
|
||||
},
|
||||
"source": "This is an exercise to introduce users to using Exercism",
|
||||
"source_url": "http://en.wikipedia.org/wiki/%22Hello,_world!%22_program"
|
||||
}
|
||||
1
hello-world/.exercism/metadata.json
Normal file
1
hello-world/.exercism/metadata.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"track":"elixir","exercise":"hello-world","id":"cbb9fdd6743a44c0b201ec81bec9e8c8","url":"https://exercism.org/tracks/elixir/exercises/hello-world","handle":"rjNemo","is_requester":true,"auto_approve":false}
|
||||
4
hello-world/.formatter.exs
Normal file
4
hello-world/.formatter.exs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Used by "mix format"
|
||||
[
|
||||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||
]
|
||||
24
hello-world/.gitignore
vendored
Normal file
24
hello-world/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# The directory Mix will write compiled artifacts to.
|
||||
/_build/
|
||||
|
||||
# If you run "mix test --cover", coverage assets end up here.
|
||||
/cover/
|
||||
|
||||
# The directory Mix downloads your dependencies sources to.
|
||||
/deps/
|
||||
|
||||
# Where third-party dependencies like ExDoc output generated docs.
|
||||
/doc/
|
||||
|
||||
# Ignore .fetch files in case you like to edit your project deps locally.
|
||||
/.fetch
|
||||
|
||||
# If the VM crashes, it generates a dump, let's ignore it too.
|
||||
erl_crash.dump
|
||||
|
||||
# Also ignore archive artifacts (built via "mix archive.build").
|
||||
*.ez
|
||||
|
||||
# Ignore package tarball (built via "mix hex.build").
|
||||
hello_world-*.tar
|
||||
|
||||
75
hello-world/HELP.md
Normal file
75
hello-world/HELP.md
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
# Help
|
||||
|
||||
## Running the tests
|
||||
|
||||
From the terminal, change to the base directory of the exercise then execute the tests with:
|
||||
|
||||
```bash
|
||||
$ mix test
|
||||
```
|
||||
|
||||
This will execute the test file found in the `test` subfolder -- a file ending in `_test.exs`
|
||||
|
||||
Documentation:
|
||||
|
||||
* [`mix test` - Elixir's test execution tool](https://hexdocs.pm/mix/Mix.Tasks.Test.html)
|
||||
* [`ExUnit` - Elixir's unit test library](https://hexdocs.pm/ex_unit/ExUnit.html)
|
||||
|
||||
## Pending tests
|
||||
|
||||
In test suites of practice exercises, all but the first test have been tagged to be 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
|
||||
```
|
||||
|
||||
If you wish to run all tests at once, you can include all skipped test by using the `--include` flag on the `mix test` command:
|
||||
|
||||
```bash
|
||||
$ mix test --include pending
|
||||
```
|
||||
|
||||
Or, you can enable all the tests by commenting out the `ExUnit.configure` line in the file `test/test_helper.exs`.
|
||||
|
||||
```elixir
|
||||
# ExUnit.configure(exclude: :pending, trace: true)
|
||||
```
|
||||
|
||||
## Useful `mix test` options
|
||||
|
||||
* `test/<FILE>.exs:LINENUM` - runs only a single test, the test from `<FILE>.exs` whose definition is on line `LINENUM`
|
||||
* `--failed` - runs only tests that failed the last time they ran
|
||||
* `--max-failures` - the suite stops evaluating tests when this number of test failures
|
||||
is reached
|
||||
* `--seed 0` - disables randomization so the tests in a single file will always be ran
|
||||
in the same order they were defined in
|
||||
|
||||
## Submitting your solution
|
||||
|
||||
You can submit your solution using the `exercism submit lib/hello_world.ex` command.
|
||||
This command will upload your solution to the Exercism website and print the solution page's URL.
|
||||
|
||||
It's possible to submit an incomplete solution which allows you to:
|
||||
|
||||
- See how others have completed the exercise
|
||||
- Request help from a mentor
|
||||
|
||||
## Need to get help?
|
||||
|
||||
If you'd like help solving the exercise, check the following pages:
|
||||
|
||||
- The [Elixir track's documentation](https://exercism.org/docs/tracks/elixir)
|
||||
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
|
||||
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
|
||||
|
||||
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
|
||||
|
||||
If you're stuck on something, it may help to look at some of the [available resources](https://exercism.org/docs/tracks/elixir/resources) out there where answers might be found.
|
||||
If you can't find what you're looking for in the documentation, feel free to ask help in the Exercism's BEAM [gitter channel](https://gitter.im/exercism/xerlang).
|
||||
44
hello-world/README.md
Normal file
44
hello-world/README.md
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# Hello World
|
||||
|
||||
Welcome to Hello World on Exercism's Elixir Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
|
||||
## Instructions
|
||||
|
||||
The classical introductory exercise. Just say "Hello, World!".
|
||||
|
||||
["Hello, World!"](http://en.wikipedia.org/wiki/%22Hello,_world!%22_program) is
|
||||
the traditional first program for beginning programming in a new language
|
||||
or environment.
|
||||
|
||||
The objectives are simple:
|
||||
|
||||
- Write a function that returns the string "Hello, World!".
|
||||
- Run the test suite and make sure that it succeeds.
|
||||
- Submit your solution and check it at the website.
|
||||
|
||||
If everything goes well, you will be ready to fetch your first real exercise.
|
||||
|
||||
## Source
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @angelikatyborska
|
||||
- @Cohen-Carlisle
|
||||
- @dalexj
|
||||
- @devonestes
|
||||
- @George-Hudson
|
||||
- @hanmd82
|
||||
- @lpil
|
||||
- @neenjaw
|
||||
- @parkerl
|
||||
- @rawkode
|
||||
- @rubysolo
|
||||
- @sotojuan
|
||||
- @Teapane
|
||||
- @Thrillberg
|
||||
- @waiting-for-dev
|
||||
|
||||
### Based on
|
||||
|
||||
This is an exercise to introduce users to using Exercism - http://en.wikipedia.org/wiki/%22Hello,_world!%22_program
|
||||
9
hello-world/lib/hello_world.ex
Normal file
9
hello-world/lib/hello_world.ex
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
defmodule HelloWorld do
|
||||
@doc """
|
||||
Simply returns "Hello, World!"
|
||||
"""
|
||||
@spec hello :: String.t()
|
||||
def hello do
|
||||
"Hello, World!"
|
||||
end
|
||||
end
|
||||
28
hello-world/mix.exs
Normal file
28
hello-world/mix.exs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
defmodule HelloWorld.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :hello_world,
|
||||
version: "0.1.0",
|
||||
# elixir: "~> 1.8",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps()
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help compile.app" to learn about applications.
|
||||
def application do
|
||||
[
|
||||
extra_applications: [:logger]
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help deps" to learn about dependencies.
|
||||
defp deps do
|
||||
[
|
||||
# {:dep_from_hexpm, "~> 0.3.0"},
|
||||
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
|
||||
]
|
||||
end
|
||||
end
|
||||
7
hello-world/test/hello_world_test.exs
Normal file
7
hello-world/test/hello_world_test.exs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
defmodule HelloWorldTest do
|
||||
use ExUnit.Case
|
||||
|
||||
test "says 'Hello, World!'" do
|
||||
assert HelloWorld.hello() == "Hello, World!"
|
||||
end
|
||||
end
|
||||
2
hello-world/test/test_helper.exs
Normal file
2
hello-world/test/test_helper.exs
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
ExUnit.start()
|
||||
ExUnit.configure(exclude: :pending, trace: true)
|
||||
22
high-school-sweetheart/.exercism/config.json
Normal file
22
high-school-sweetheart/.exercism/config.json
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"blurb": "Learn about strings and the pipe operator by helping high school sweethearts profess their love on social media via ASCII art.",
|
||||
"icon": "high-school-sweethearts",
|
||||
"authors": [
|
||||
"angelikatyborska"
|
||||
],
|
||||
"contributors": [
|
||||
"neenjaw"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"lib/high_school_sweetheart.ex"
|
||||
],
|
||||
"test": [
|
||||
"test/high_school_sweetheart_test.exs"
|
||||
],
|
||||
"exemplar": [
|
||||
".meta/exemplar.ex"
|
||||
]
|
||||
},
|
||||
"language_versions": ">=1.10"
|
||||
}
|
||||
1
high-school-sweetheart/.exercism/metadata.json
Normal file
1
high-school-sweetheart/.exercism/metadata.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"track":"elixir","exercise":"high-school-sweetheart","id":"acda69ee2e3d40428e468fcab711d067","url":"https://exercism.org/tracks/elixir/exercises/high-school-sweetheart","handle":"rjNemo","is_requester":true,"auto_approve":false}
|
||||
4
high-school-sweetheart/.formatter.exs
Normal file
4
high-school-sweetheart/.formatter.exs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Used by "mix format"
|
||||
[
|
||||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||
]
|
||||
24
high-school-sweetheart/.gitignore
vendored
Normal file
24
high-school-sweetheart/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# The directory Mix will write compiled artifacts to.
|
||||
/_build/
|
||||
|
||||
# If you run "mix test --cover", coverage assets end up here.
|
||||
/cover/
|
||||
|
||||
# The directory Mix downloads your dependencies sources to.
|
||||
/deps/
|
||||
|
||||
# Where third-party dependencies like ExDoc output generated docs.
|
||||
/doc/
|
||||
|
||||
# Ignore .fetch files in case you like to edit your project deps locally.
|
||||
/.fetch
|
||||
|
||||
# If the VM crashes, it generates a dump, let's ignore it too.
|
||||
erl_crash.dump
|
||||
|
||||
# Also ignore archive artifacts (built via "mix archive.build").
|
||||
*.ez
|
||||
|
||||
# Ignore package tarball (built via "mix hex.build").
|
||||
basic_strings-*.tar
|
||||
|
||||
75
high-school-sweetheart/HELP.md
Normal file
75
high-school-sweetheart/HELP.md
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
# Help
|
||||
|
||||
## Running the tests
|
||||
|
||||
From the terminal, change to the base directory of the exercise then execute the tests with:
|
||||
|
||||
```bash
|
||||
$ mix test
|
||||
```
|
||||
|
||||
This will execute the test file found in the `test` subfolder -- a file ending in `_test.exs`
|
||||
|
||||
Documentation:
|
||||
|
||||
* [`mix test` - Elixir's test execution tool](https://hexdocs.pm/mix/Mix.Tasks.Test.html)
|
||||
* [`ExUnit` - Elixir's unit test library](https://hexdocs.pm/ex_unit/ExUnit.html)
|
||||
|
||||
## Pending tests
|
||||
|
||||
In test suites of practice exercises, all but the first test have been tagged to be 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
|
||||
```
|
||||
|
||||
If you wish to run all tests at once, you can include all skipped test by using the `--include` flag on the `mix test` command:
|
||||
|
||||
```bash
|
||||
$ mix test --include pending
|
||||
```
|
||||
|
||||
Or, you can enable all the tests by commenting out the `ExUnit.configure` line in the file `test/test_helper.exs`.
|
||||
|
||||
```elixir
|
||||
# ExUnit.configure(exclude: :pending, trace: true)
|
||||
```
|
||||
|
||||
## Useful `mix test` options
|
||||
|
||||
* `test/<FILE>.exs:LINENUM` - runs only a single test, the test from `<FILE>.exs` whose definition is on line `LINENUM`
|
||||
* `--failed` - runs only tests that failed the last time they ran
|
||||
* `--max-failures` - the suite stops evaluating tests when this number of test failures
|
||||
is reached
|
||||
* `--seed 0` - disables randomization so the tests in a single file will always be ran
|
||||
in the same order they were defined in
|
||||
|
||||
## Submitting your solution
|
||||
|
||||
You can submit your solution using the `exercism submit lib/high_school_sweetheart.ex` command.
|
||||
This command will upload your solution to the Exercism website and print the solution page's URL.
|
||||
|
||||
It's possible to submit an incomplete solution which allows you to:
|
||||
|
||||
- See how others have completed the exercise
|
||||
- Request help from a mentor
|
||||
|
||||
## Need to get help?
|
||||
|
||||
If you'd like help solving the exercise, check the following pages:
|
||||
|
||||
- The [Elixir track's documentation](https://exercism.org/docs/tracks/elixir)
|
||||
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
|
||||
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
|
||||
|
||||
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
|
||||
|
||||
If you're stuck on something, it may help to look at some of the [available resources](https://exercism.org/docs/tracks/elixir/resources) out there where answers might be found.
|
||||
If you can't find what you're looking for in the documentation, feel free to ask help in the Exercism's BEAM [gitter channel](https://gitter.im/exercism/xerlang).
|
||||
36
high-school-sweetheart/HINTS.md
Normal file
36
high-school-sweetheart/HINTS.md
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# Hints
|
||||
|
||||
## General
|
||||
|
||||
- Read about strings in the official [Getting Started guide][getting-started-strings].
|
||||
- Browse the [functions available in the _String module_][string-module-functions] to discover which operations on strings Elixir's standard library offers.
|
||||
|
||||
## 1. Get the name's first letter
|
||||
|
||||
- There is a [built-in function][string-first] to get the first character from a string.
|
||||
- There are multiple [built-in functions][string-trim] to remove leading, trailing, or leading and trailing whitespaces from a string.
|
||||
|
||||
## 2. Format the first letter as an initial
|
||||
|
||||
- There is a [built-in function][string-upcase] to convert all characters in a string to their uppercase variant.
|
||||
- There is an [operator][kernel-concat] that concatenates two strings.
|
||||
|
||||
## 3. Split the full name into the first name and the last name
|
||||
|
||||
- There is a [built-in function][string-split] that splits a string on whitespace characters.
|
||||
- A few first elements of a list can be assigned to variables by pattern matching on the list.
|
||||
|
||||
## 4. Put the initials inside of the heart
|
||||
|
||||
- There is a special syntax for [interpolating][string-interpolation] an expression inside of a string.
|
||||
- There is a special syntax for writing [multiline strings][heredoc-syntax] without needing to escape newlines.
|
||||
|
||||
[getting-started-strings]: https://elixir-lang.org/getting-started/basic-types.html#strings
|
||||
[string-module-functions]: https://hexdocs.pm/elixir/String.html#functions
|
||||
[string-first]: https://hexdocs.pm/elixir/String.html#first/1
|
||||
[string-trim]: https://hexdocs.pm/elixir/String.html#trim/1
|
||||
[string-upcase]: https://hexdocs.pm/elixir/String.html#upcase/2
|
||||
[string-split]: https://hexdocs.pm/elixir/String.html#split/1
|
||||
[string-interpolation]: https://hexdocs.pm/elixir/String.html#module-interpolation
|
||||
[kernel-concat]: https://hexdocs.pm/elixir/Kernel.html#%3C%3E/2
|
||||
[heredoc-syntax]: https://elixir-examples.github.io/examples/multiline-strings-heredocs
|
||||
140
high-school-sweetheart/README.md
Normal file
140
high-school-sweetheart/README.md
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
# High School Sweetheart
|
||||
|
||||
Welcome to High School Sweetheart on Exercism's Elixir Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :)
|
||||
|
||||
## Introduction
|
||||
|
||||
## Strings
|
||||
|
||||
Strings in Elixir are delimited by double quotes, and they are encoded in UTF-8:
|
||||
|
||||
```elixir
|
||||
"Hi!"
|
||||
```
|
||||
|
||||
Strings can be concatenated using the `<>/2` operator:
|
||||
|
||||
```elixir
|
||||
"Welcome to" <> " " <> "New York"
|
||||
# => "Welcome to New York"
|
||||
```
|
||||
|
||||
Strings in Elixir support interpolation using the `#{}` syntax:
|
||||
|
||||
```elixir
|
||||
"6 * 7 = #{6 * 7}"
|
||||
# => "6 * 7 = 42"
|
||||
```
|
||||
|
||||
To put a newline character in a string, use the `\n` escape code:
|
||||
|
||||
```elixir
|
||||
"1\n2\n3\n"
|
||||
```
|
||||
|
||||
To comfortably work with texts with a lot of newlines, use the triple-double-quote heredoc syntax instead:
|
||||
|
||||
```elixir
|
||||
"""
|
||||
1
|
||||
2
|
||||
3
|
||||
"""
|
||||
```
|
||||
|
||||
Elixir provides many functions for working with strings in the `String` module.
|
||||
|
||||
## Pipe Operator
|
||||
|
||||
The `|>` operator is called the pipe operator. It can be used to chain function calls together in such a way that the value returned by the previous function call is passed as the first argument to the next function call.
|
||||
|
||||
```elixir
|
||||
"hello"
|
||||
|> String.upcase()
|
||||
|> Kernel.<>("?!")
|
||||
# => "HELLO?!"
|
||||
```
|
||||
|
||||
## Instructions
|
||||
|
||||
In this exercise, you are going to help high school sweethearts profess their love on social media by generating an ASCII heart with their initials:
|
||||
|
||||
```
|
||||
****** ******
|
||||
** ** ** **
|
||||
** ** ** **
|
||||
** * **
|
||||
** **
|
||||
** J. K. + M. B. **
|
||||
** **
|
||||
** **
|
||||
** **
|
||||
** **
|
||||
** **
|
||||
** **
|
||||
***
|
||||
*
|
||||
```
|
||||
|
||||
## 1. Get the name's first letter
|
||||
|
||||
Implement the `HighSchoolSweetheart.first_letter/1` function. It should take a name and return its first letter. It should clean up any unnecessary whitespace from the name.
|
||||
|
||||
```elixir
|
||||
HighSchoolSweetheart.first_letter("Jane")
|
||||
# => "J"
|
||||
```
|
||||
|
||||
## 2. Format the first letter as an initial
|
||||
|
||||
Implement the `HighSchoolSweetheart.initial/1` function. It should take a name and return its first letter, uppercase, followed by a dot. Make sure to reuse `HighSchoolSweetheart.first_letter/1` that you defined in the previous step.
|
||||
|
||||
```elixir
|
||||
HighSchoolSweetheart.initial("Robert")
|
||||
# => "R."
|
||||
```
|
||||
|
||||
## 3. Split the full name into the first name and the last name
|
||||
|
||||
Implement the `HighSchoolSweetheart.initials/1` function. It should take a full name, consisting of a first name and a last name separated by a space, and return the initials. Make sure to reuse `HighSchoolSweetheart.initial/1` that you defined in the previous step.
|
||||
|
||||
```elixir
|
||||
HighSchoolSweetheart.initials("Lance Green")
|
||||
# => "L. G."
|
||||
```
|
||||
|
||||
## 4. Put the initials inside of the heart
|
||||
|
||||
Implement the `HighSchoolSweetheart.pair/2` function. It should take two full names and return the initials. Make sure to reuse `HighSchoolSweetheart.initials/1` that you defined in the previous step.
|
||||
|
||||
```elixir
|
||||
HighSchoolSweetheart.pair("Blake Miller", "Riley Lewis")
|
||||
# => """
|
||||
# ****** ******
|
||||
# ** ** ** **
|
||||
# ** ** ** **
|
||||
# ** * **
|
||||
# ** **
|
||||
# ** B. M. + R. L. **
|
||||
# ** **
|
||||
# ** **
|
||||
# ** **
|
||||
# ** **
|
||||
# ** **
|
||||
# ** **
|
||||
# ***
|
||||
# *
|
||||
# """
|
||||
```
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @angelikatyborska
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @neenjaw
|
||||
29
high-school-sweetheart/lib/high_school_sweetheart.ex
Normal file
29
high-school-sweetheart/lib/high_school_sweetheart.ex
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
defmodule HighSchoolSweetheart do
|
||||
def first_letter(name), do: name |> String.trim() |> String.first()
|
||||
|
||||
def initial(name), do: "#{name |> first_letter |> String.upcase()}."
|
||||
|
||||
def initials(full_name) do
|
||||
[first, last] = full_name |> String.split()
|
||||
"#{initial(first)} #{initial(last)}"
|
||||
end
|
||||
|
||||
def pair(full_name1, full_name2) do
|
||||
"""
|
||||
****** ******
|
||||
** ** ** **
|
||||
** ** ** **
|
||||
** * **
|
||||
** **
|
||||
** #{initials(full_name1)} + #{initials(full_name2)} **
|
||||
** **
|
||||
** **
|
||||
** **
|
||||
** **
|
||||
** **
|
||||
** **
|
||||
***
|
||||
*
|
||||
"""
|
||||
end
|
||||
end
|
||||
28
high-school-sweetheart/mix.exs
Normal file
28
high-school-sweetheart/mix.exs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
defmodule HighSchoolSweetheart.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :high_school_sweetheart,
|
||||
version: "0.1.0",
|
||||
# elixir: "~> 1.10",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps()
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help compile.app" to learn about applications.
|
||||
def application do
|
||||
[
|
||||
extra_applications: [:logger]
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help deps" to learn about dependencies.
|
||||
defp deps do
|
||||
[
|
||||
# {:dep_from_hexpm, "~> 0.3.0"},
|
||||
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
|
||||
]
|
||||
end
|
||||
end
|
||||
62
high-school-sweetheart/test/high_school_sweetheart_test.exs
Normal file
62
high-school-sweetheart/test/high_school_sweetheart_test.exs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
defmodule HighSchoolSweetheartTest do
|
||||
use ExUnit.Case
|
||||
|
||||
describe "first_letter/1" do
|
||||
@tag task_id: 1
|
||||
test "it gets the first letter" do
|
||||
assert HighSchoolSweetheart.first_letter("Mary") == "M"
|
||||
end
|
||||
|
||||
@tag task_id: 1
|
||||
test "it doesn't change the letter's case" do
|
||||
assert HighSchoolSweetheart.first_letter("john") == "j"
|
||||
end
|
||||
|
||||
@tag task_id: 1
|
||||
test "it strips extra whitespace" do
|
||||
assert HighSchoolSweetheart.first_letter("\n\t Sarah ") == "S"
|
||||
end
|
||||
end
|
||||
|
||||
describe "initial/1" do
|
||||
@tag task_id: 2
|
||||
test "it gets the first letter and appends a dot" do
|
||||
assert HighSchoolSweetheart.initial("Betty") == "B."
|
||||
end
|
||||
|
||||
@tag task_id: 2
|
||||
test "it uppercases the first letter" do
|
||||
assert HighSchoolSweetheart.initial("james") == "J."
|
||||
end
|
||||
end
|
||||
|
||||
describe "initials/1" do
|
||||
@tag task_id: 3
|
||||
test "returns the initials for the first name and the last name" do
|
||||
assert HighSchoolSweetheart.initials("Linda Miller") == "L. M."
|
||||
end
|
||||
end
|
||||
|
||||
describe "pair/2" do
|
||||
@tag task_id: 4
|
||||
test "prints the pair's initials inside a heart" do
|
||||
assert HighSchoolSweetheart.pair("Avery Bryant", "Charlie Dixon") ==
|
||||
"""
|
||||
****** ******
|
||||
** ** ** **
|
||||
** ** ** **
|
||||
** * **
|
||||
** **
|
||||
** A. B. + C. D. **
|
||||
** **
|
||||
** **
|
||||
** **
|
||||
** **
|
||||
** **
|
||||
** **
|
||||
***
|
||||
*
|
||||
"""
|
||||
end
|
||||
end
|
||||
end
|
||||
2
high-school-sweetheart/test/test_helper.exs
Normal file
2
high-school-sweetheart/test/test_helper.exs
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
ExUnit.start()
|
||||
ExUnit.configure(exclude: :pending, trace: true, seed: 0)
|
||||
21
kitchen-calculator/.exercism/config.json
Normal file
21
kitchen-calculator/.exercism/config.json
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"blurb": "Learn about tuples and pattern matching by converting common US baking measurements to the metric system.",
|
||||
"authors": [
|
||||
"neenjaw"
|
||||
],
|
||||
"contributors": [
|
||||
"angelikatyborska"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"lib/kitchen_calculator.ex"
|
||||
],
|
||||
"test": [
|
||||
"test/kitchen_calculator_test.exs"
|
||||
],
|
||||
"exemplar": [
|
||||
".meta/exemplar.ex"
|
||||
]
|
||||
},
|
||||
"language_versions": ">=1.10"
|
||||
}
|
||||
1
kitchen-calculator/.exercism/metadata.json
Normal file
1
kitchen-calculator/.exercism/metadata.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"track":"elixir","exercise":"kitchen-calculator","id":"1e086899218a43efb738ee960db599ec","url":"https://exercism.org/tracks/elixir/exercises/kitchen-calculator","handle":"rjNemo","is_requester":true,"auto_approve":false}
|
||||
4
kitchen-calculator/.formatter.exs
Normal file
4
kitchen-calculator/.formatter.exs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Used by "mix format"
|
||||
[
|
||||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||
]
|
||||
24
kitchen-calculator/.gitignore
vendored
Normal file
24
kitchen-calculator/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# The directory Mix will write compiled artifacts to.
|
||||
/_build/
|
||||
|
||||
# If you run "mix test --cover", coverage assets end up here.
|
||||
/cover/
|
||||
|
||||
# The directory Mix downloads your dependencies sources to.
|
||||
/deps/
|
||||
|
||||
# Where third-party dependencies like ExDoc output generated docs.
|
||||
/doc/
|
||||
|
||||
# Ignore .fetch files in case you like to edit your project deps locally.
|
||||
/.fetch
|
||||
|
||||
# If the VM crashes, it generates a dump, let's ignore it too.
|
||||
erl_crash.dump
|
||||
|
||||
# Also ignore archive artifacts (built via "mix archive.build").
|
||||
*.ez
|
||||
|
||||
# Ignore package tarball (built via "mix hex.build").
|
||||
tuples-*.tar
|
||||
|
||||
75
kitchen-calculator/HELP.md
Normal file
75
kitchen-calculator/HELP.md
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
# Help
|
||||
|
||||
## Running the tests
|
||||
|
||||
From the terminal, change to the base directory of the exercise then execute the tests with:
|
||||
|
||||
```bash
|
||||
$ mix test
|
||||
```
|
||||
|
||||
This will execute the test file found in the `test` subfolder -- a file ending in `_test.exs`
|
||||
|
||||
Documentation:
|
||||
|
||||
* [`mix test` - Elixir's test execution tool](https://hexdocs.pm/mix/Mix.Tasks.Test.html)
|
||||
* [`ExUnit` - Elixir's unit test library](https://hexdocs.pm/ex_unit/ExUnit.html)
|
||||
|
||||
## Pending tests
|
||||
|
||||
In test suites of practice exercises, all but the first test have been tagged to be 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
|
||||
```
|
||||
|
||||
If you wish to run all tests at once, you can include all skipped test by using the `--include` flag on the `mix test` command:
|
||||
|
||||
```bash
|
||||
$ mix test --include pending
|
||||
```
|
||||
|
||||
Or, you can enable all the tests by commenting out the `ExUnit.configure` line in the file `test/test_helper.exs`.
|
||||
|
||||
```elixir
|
||||
# ExUnit.configure(exclude: :pending, trace: true)
|
||||
```
|
||||
|
||||
## Useful `mix test` options
|
||||
|
||||
* `test/<FILE>.exs:LINENUM` - runs only a single test, the test from `<FILE>.exs` whose definition is on line `LINENUM`
|
||||
* `--failed` - runs only tests that failed the last time they ran
|
||||
* `--max-failures` - the suite stops evaluating tests when this number of test failures
|
||||
is reached
|
||||
* `--seed 0` - disables randomization so the tests in a single file will always be ran
|
||||
in the same order they were defined in
|
||||
|
||||
## Submitting your solution
|
||||
|
||||
You can submit your solution using the `exercism submit lib/kitchen_calculator.ex` command.
|
||||
This command will upload your solution to the Exercism website and print the solution page's URL.
|
||||
|
||||
It's possible to submit an incomplete solution which allows you to:
|
||||
|
||||
- See how others have completed the exercise
|
||||
- Request help from a mentor
|
||||
|
||||
## Need to get help?
|
||||
|
||||
If you'd like help solving the exercise, check the following pages:
|
||||
|
||||
- The [Elixir track's documentation](https://exercism.org/docs/tracks/elixir)
|
||||
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
|
||||
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
|
||||
|
||||
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
|
||||
|
||||
If you're stuck on something, it may help to look at some of the [available resources](https://exercism.org/docs/tracks/elixir/resources) out there where answers might be found.
|
||||
If you can't find what you're looking for in the documentation, feel free to ask help in the Exercism's BEAM [gitter channel](https://gitter.im/exercism/xerlang).
|
||||
30
kitchen-calculator/HINTS.md
Normal file
30
kitchen-calculator/HINTS.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# Hints
|
||||
|
||||
## General
|
||||
|
||||
- [Tuples][tuple-module] are data structures which are arranged in contiguous memory and can hold any data-type.
|
||||
- Atoms may be used to denote finite states, as this exercise uses `:cup`, `:fluid_ounce`, `:teaspoon`, `:tablespoon`, `:milliliter` to denote the units used.
|
||||
- You may use [`Kernel`][elem] or [`Tuple`][tuple-module] functions or pattern matching to manipulate the tuples.
|
||||
|
||||
## 1. Get the numeric component from a volume-pair
|
||||
|
||||
- Consider using [a `Kernel` module function][elem] to return the volume-pair's numeric component.
|
||||
|
||||
## 2. Convert the volume-pair to milliliters
|
||||
|
||||
- Use [multiple clause functions][multi-clause] and [pattern matching][pattern-matching] to reduce conditional control flow logic.
|
||||
- Implement the function for all units to milliliters, including milliliters to milliliters.
|
||||
|
||||
## 3. Convert the milliliter volume-pair to another unit
|
||||
|
||||
- Use [multiple clause functions][multi-clause] and [pattern matching][pattern-matching] to reduce conditional control flow logic.
|
||||
- Implement the function for all units to milliliters, including milliliters to milliliters.
|
||||
|
||||
## 4. Convert from any unit to any unit
|
||||
|
||||
- Reuse the functions already created to perform the conversion in the `convert/2` function.
|
||||
|
||||
[elem]: https://hexdocs.pm/elixir/Kernel.html#elem/2
|
||||
[multi-clause]: https://elixir-lang.org/getting-started/modules-and-functions.html#named-functions
|
||||
[tuple-module]: https://hexdocs.pm/elixir/Tuple.html
|
||||
[pattern-matching]: https://medium.com/rebirth-delivery/how-to-use-elixir-pattern-matched-functions-arguments-a793733acc6d
|
||||
143
kitchen-calculator/README.md
Normal file
143
kitchen-calculator/README.md
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
# Kitchen Calculator
|
||||
|
||||
Welcome to Kitchen Calculator on Exercism's Elixir Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :)
|
||||
|
||||
## Introduction
|
||||
|
||||
## Tuples
|
||||
|
||||
In Elixir, a tuple is a data structure which organizes data, holding a fixed number of items of any type, but without explicit names for each element. Tuples are often used in Elixir for memory read-intensive operations, since read-access of an element is a constant-time operation. They are not usually used when elements may need to be added/removed dynamically because rather than modifying the existing tuple, a new tuple is created which requires memory to be allocated upfront.
|
||||
|
||||
In practice, tuples are created in Elixir using curly braces. Elements in a tuple can be individually accessed using the `elem/2` function using 0-based indexing:
|
||||
|
||||
```elixir
|
||||
empty_tuple = {}
|
||||
one_element_tuple = {1}
|
||||
multiple_element_tuple = {1, :a, "hello"}
|
||||
|
||||
elem(multiple_element_tuple, 2)
|
||||
# => "hello"
|
||||
```
|
||||
|
||||
### Tuples as grouped information
|
||||
|
||||
Tuples are often used in practice to represent grouped information.
|
||||
|
||||
```elixir
|
||||
Float.ratio(0.25)
|
||||
# => {1, 4} indicating the numerator and denominator of the fraction ¼
|
||||
```
|
||||
|
||||
## Pattern Matching
|
||||
|
||||
The use of pattern matching is dominant in assertive, idiomatic Elixir code. You might recall that `=/2` is described as a match operator rather than as an assignment operator. When using the match operator, if the pattern on the left matches the right, any variables on the left are bound, and the value of the right side is returned. A `MatchError` is raised if there is no match.
|
||||
|
||||
```elixir
|
||||
2 = 2
|
||||
# => 2
|
||||
# Literals can be matched if they are the same
|
||||
|
||||
2 = 3
|
||||
# => ** (MatchError) no match of right hand side value: 3
|
||||
|
||||
{_, denominator} = Float.ratio(0.25)
|
||||
# => {1, 4}
|
||||
# the variable `denominator` is bound to the value 4
|
||||
```
|
||||
|
||||
Remember, matches occur from the right side to the left side.
|
||||
|
||||
In the last example if we don't need a variable in a pattern match, we can discard it by referencing `_`. Any variable starting with an `_` is not tracked by the runtime.
|
||||
|
||||
### Pattern matching in named functions
|
||||
|
||||
Pattern matching is also a useful tool when creating multiple function clauses. Pattern matching can be used on the functions' arguments which then determines which function clause to invoke -- starting from the top of the file down until the first match. Variables may be bound in a function head and used in the function body.
|
||||
|
||||
```elixir
|
||||
defmodule Example do
|
||||
def named_function(:a = variable_a) do
|
||||
{variable_a, 1}
|
||||
end
|
||||
|
||||
def named_function(:b = variable_b) do
|
||||
{variable_b, 2}
|
||||
end
|
||||
end
|
||||
|
||||
Example.named_function(:a)
|
||||
# => {:a, 1}
|
||||
|
||||
Example.named_function(:b)
|
||||
# => {:b, 2}
|
||||
|
||||
Example.named_function(:c)
|
||||
# => ** (FunctionClauseError) no function clause matching in Example.named_function/1
|
||||
```
|
||||
|
||||
## Instructions
|
||||
|
||||
While preparing to bake cookies for your friends, you have found that you have to convert some of the measurements used in the recipe. Being only familiar with the metric system, you need to come up with a way to convert common US baking measurements to milliliters (mL) for your own ease.
|
||||
|
||||
Use this conversion chart for your solution:
|
||||
|
||||
| Unit to convert | volume | in milliliters (mL) |
|
||||
| --------------- | ------ | ------------------- |
|
||||
| mL | 1 | 1 |
|
||||
| US cup | 1 | 240 |
|
||||
| US fluid ounce | 1 | 30 |
|
||||
| US teaspoon | 1 | 5 |
|
||||
| US tablespoon | 1 | 15 |
|
||||
|
||||
Being a talented programmer in training, you decide to use milliliters as a transition unit to facilitate the conversion from any unit listed to any other (even itself).
|
||||
|
||||
## 1. Get the numeric component from a volume-pair
|
||||
|
||||
Implement the `KitchenCalculator.get_volume/1` function. Given a volume-pair tuple, it should return just the numeric component.
|
||||
|
||||
```elixir
|
||||
KitchenCalculator.get_volume({:cup, 2.0})
|
||||
# => 2.0
|
||||
```
|
||||
|
||||
## 2. Convert the volume-pair to milliliters
|
||||
|
||||
Implement the `KitchenCalculator.to_milliliter/1` function. Given a volume-pair tuple, it should convert the volume to milliliters using the conversion chart.
|
||||
|
||||
Use multiple function clauses and pattern matching to create the functions for each unit. The atoms used to denote each unit are: `:cup`, `:fluid_ounce`, `:teaspoon`, `:tablespoon`, `:milliliter`. Return the result of the conversion wrapped in a tuple.
|
||||
|
||||
```elixir
|
||||
KitchenCalculator.to_milliliter({:cup, 2.5})
|
||||
# => {:milliliter, 600.0}
|
||||
```
|
||||
|
||||
## 3. Convert the milliliter volume-pair to another unit
|
||||
|
||||
Implement the `KitchenCalculator.from_milliliter/2` function. Given a volume-pair tuple and the desired unit, it should convert the volume to the desired unit using the conversion chart.
|
||||
|
||||
Use multiple function clauses and pattern matching to create the functions for each unit. The atoms used to denote each unit are: `:cup`, `:fluid_ounce`, `:teaspoon`, `:tablespoon`, `:milliliter`
|
||||
|
||||
```elixir
|
||||
KitchenCalculator.from_milliliter({:milliliter, 1320.0}, :cup)
|
||||
# => {:cup, 5.5}
|
||||
```
|
||||
|
||||
## 4. Convert from any unit to any unit
|
||||
|
||||
Implement the `KitchenCalculator.convert/2` function. Given a volume-pair tuple and the desired unit, it should convert the given volume to the desired unit.
|
||||
|
||||
```elixir
|
||||
KitchenCalculator.convert({:teaspoon, 9.0}, :tablespoon)
|
||||
# => {:tablespoon, 3.0}
|
||||
```
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @neenjaw
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @angelikatyborska
|
||||
44
kitchen-calculator/lib/kitchen_calculator.ex
Normal file
44
kitchen-calculator/lib/kitchen_calculator.ex
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
defmodule KitchenCalculator do
|
||||
def get_volume(volume_pair) do
|
||||
case volume_pair do
|
||||
{:cup, 1} -> 1
|
||||
{:fluid_ounce, 2} -> 2
|
||||
{:teaspoon, 3} -> 3
|
||||
{:tablespoon, 4} -> 4
|
||||
{:milliliter, 5} -> 5
|
||||
end
|
||||
end
|
||||
|
||||
def to_milliliter(volume_pair) do
|
||||
case volume_pair do
|
||||
{:milliliter, x} -> {:milliliter, x}
|
||||
{:cup, x} -> {:milliliter, 240 * x}
|
||||
{:fluid_ounce, x} -> {:milliliter, 30 * x}
|
||||
{:teaspoon, x} -> {:milliliter, 5 * x}
|
||||
{:tablespoon, x} -> {:milliliter, 15 * x}
|
||||
end
|
||||
end
|
||||
|
||||
def from_milliliter(volume_pair, unit) do
|
||||
{_, x} = volume_pair
|
||||
|
||||
case unit do
|
||||
:milliliter -> volume_pair
|
||||
:cup -> {unit, x / 240}
|
||||
:fluid_ounce -> {unit, x / 30}
|
||||
:teaspoon -> {unit, x / 5}
|
||||
:tablespoon -> {unit, x / 15}
|
||||
end
|
||||
end
|
||||
|
||||
def convert(volume_pair, unit) do
|
||||
{from, x} = volume_pair
|
||||
|
||||
case {from, unit} do
|
||||
{:teaspoon, :tablespoon} -> {unit, x / 3}
|
||||
{:cup, :fluid_ounce} -> {unit, x * 8}
|
||||
{:fluid_ounce, :teaspoon} -> {unit, x * 6}
|
||||
{:tablespoon, :cup} -> {unit, x / 16}
|
||||
end
|
||||
end
|
||||
end
|
||||
28
kitchen-calculator/mix.exs
Normal file
28
kitchen-calculator/mix.exs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
defmodule KitchenCalculator.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :kitchen_calculator,
|
||||
version: "0.1.0",
|
||||
# elixir: "~> 1.10",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps()
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help compile.app" to learn about applications.
|
||||
def application do
|
||||
[
|
||||
extra_applications: [:logger]
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help deps" to learn about dependencies.
|
||||
defp deps do
|
||||
[
|
||||
# {:dep_from_hexpm, "~> 0.3.0"},
|
||||
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
|
||||
]
|
||||
end
|
||||
end
|
||||
109
kitchen-calculator/test/kitchen_calculator_test.exs
Normal file
109
kitchen-calculator/test/kitchen_calculator_test.exs
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
defmodule KitchenCalculatorTest do
|
||||
use ExUnit.Case
|
||||
|
||||
describe "get volume from tuple pair" do
|
||||
@tag task_id: 1
|
||||
test "get cups" do
|
||||
assert KitchenCalculator.get_volume({:cup, 1}) == 1
|
||||
end
|
||||
|
||||
@tag task_id: 1
|
||||
test "get fluid ounces" do
|
||||
assert KitchenCalculator.get_volume({:fluid_ounce, 2}) == 2
|
||||
end
|
||||
|
||||
@tag task_id: 1
|
||||
test "get teaspoons" do
|
||||
assert KitchenCalculator.get_volume({:teaspoon, 3}) == 3
|
||||
end
|
||||
|
||||
@tag task_id: 1
|
||||
test "get tablespoons" do
|
||||
assert KitchenCalculator.get_volume({:tablespoon, 4}) == 4
|
||||
end
|
||||
|
||||
@tag task_id: 1
|
||||
test "get milliliters" do
|
||||
assert KitchenCalculator.get_volume({:milliliter, 5}) == 5
|
||||
end
|
||||
end
|
||||
|
||||
describe "convert to milliliters from" do
|
||||
@tag task_id: 2
|
||||
test "milliliters" do
|
||||
assert KitchenCalculator.to_milliliter({:milliliter, 3}) == {:milliliter, 3}
|
||||
end
|
||||
|
||||
@tag task_id: 2
|
||||
test "cups" do
|
||||
assert KitchenCalculator.to_milliliter({:cup, 3}) == {:milliliter, 720}
|
||||
end
|
||||
|
||||
@tag task_id: 2
|
||||
test "fluid ounces" do
|
||||
assert KitchenCalculator.to_milliliter({:fluid_ounce, 100}) == {:milliliter, 3000}
|
||||
end
|
||||
|
||||
@tag task_id: 2
|
||||
test "teaspoon" do
|
||||
assert KitchenCalculator.to_milliliter({:teaspoon, 3}) == {:milliliter, 15}
|
||||
end
|
||||
|
||||
@tag task_id: 2
|
||||
test "tablespoon" do
|
||||
assert KitchenCalculator.to_milliliter({:tablespoon, 3}) == {:milliliter, 45}
|
||||
end
|
||||
end
|
||||
|
||||
describe "convert from milliliters to" do
|
||||
@tag task_id: 3
|
||||
test "milliliters" do
|
||||
assert KitchenCalculator.from_milliliter({:milliliter, 4}, :milliliter) == {:milliliter, 4}
|
||||
end
|
||||
|
||||
@tag task_id: 3
|
||||
test "cups" do
|
||||
assert KitchenCalculator.from_milliliter({:milliliter, 840}, :cup) == {:cup, 3.5}
|
||||
end
|
||||
|
||||
@tag task_id: 3
|
||||
test "fluid ounces" do
|
||||
assert KitchenCalculator.from_milliliter({:milliliter, 4522.5}, :fluid_ounce) ==
|
||||
{:fluid_ounce, 150.75}
|
||||
end
|
||||
|
||||
@tag task_id: 3
|
||||
test "teaspoon" do
|
||||
assert KitchenCalculator.from_milliliter({:milliliter, 61.25}, :teaspoon) ==
|
||||
{:teaspoon, 12.25}
|
||||
end
|
||||
|
||||
@tag task_id: 3
|
||||
test "tablespoon" do
|
||||
assert KitchenCalculator.from_milliliter({:milliliter, 71.25}, :tablespoon) ==
|
||||
{:tablespoon, 4.75}
|
||||
end
|
||||
end
|
||||
|
||||
describe "convert from x to y:" do
|
||||
@tag task_id: 4
|
||||
test "teaspoon to tablespoon" do
|
||||
assert KitchenCalculator.convert({:teaspoon, 15}, :tablespoon) == {:tablespoon, 5}
|
||||
end
|
||||
|
||||
@tag task_id: 4
|
||||
test "cups to fluid ounces" do
|
||||
assert KitchenCalculator.convert({:cup, 4}, :fluid_ounce) == {:fluid_ounce, 32}
|
||||
end
|
||||
|
||||
@tag task_id: 4
|
||||
test "fluid ounces to teaspoons" do
|
||||
assert KitchenCalculator.convert({:fluid_ounce, 4}, :teaspoon) == {:teaspoon, 24}
|
||||
end
|
||||
|
||||
@tag task_id: 4
|
||||
test "tablespoons to cups" do
|
||||
assert KitchenCalculator.convert({:tablespoon, 320}, :cup) == {:cup, 20}
|
||||
end
|
||||
end
|
||||
end
|
||||
2
kitchen-calculator/test/test_helper.exs
Normal file
2
kitchen-calculator/test/test_helper.exs
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
ExUnit.start()
|
||||
ExUnit.configure(exclude: :pending, trace: true, seed: 0)
|
||||
24
language-list/.exercism/config.json
Normal file
24
language-list/.exercism/config.json
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"blurb": "Learn about lists by keeping track of the programing languages you're currently learning on Exercism.",
|
||||
"authors": [
|
||||
"neenjaw"
|
||||
],
|
||||
"contributors": [
|
||||
"fireproofsocks"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"lib/language_list.ex"
|
||||
],
|
||||
"test": [
|
||||
"test/language_list_test.exs"
|
||||
],
|
||||
"exemplar": [
|
||||
".meta/exemplar.ex"
|
||||
]
|
||||
},
|
||||
"forked_from": [
|
||||
"clojure/tracks-on-tracks-on-tracks"
|
||||
],
|
||||
"language_versions": ">=1.10"
|
||||
}
|
||||
1
language-list/.exercism/metadata.json
Normal file
1
language-list/.exercism/metadata.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"track":"elixir","exercise":"language-list","id":"67ca1f59d9d747659fa66bc500fe1938","url":"https://exercism.org/tracks/elixir/exercises/language-list","handle":"rjNemo","is_requester":true,"auto_approve":false}
|
||||
4
language-list/.formatter.exs
Normal file
4
language-list/.formatter.exs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Used by "mix format"
|
||||
[
|
||||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||
]
|
||||
24
language-list/.gitignore
vendored
Normal file
24
language-list/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# The directory Mix will write compiled artifacts to.
|
||||
/_build/
|
||||
|
||||
# If you run "mix test --cover", coverage assets end up here.
|
||||
/cover/
|
||||
|
||||
# The directory Mix downloads your dependencies sources to.
|
||||
/deps/
|
||||
|
||||
# Where third-party dependencies like ExDoc output generated docs.
|
||||
/doc/
|
||||
|
||||
# Ignore .fetch files in case you like to edit your project deps locally.
|
||||
/.fetch
|
||||
|
||||
# If the VM crashes, it generates a dump, let's ignore it too.
|
||||
erl_crash.dump
|
||||
|
||||
# Also ignore archive artifacts (built via "mix archive.build").
|
||||
*.ez
|
||||
|
||||
# Ignore package tarball (built via "mix hex.build").
|
||||
strings-*.tar
|
||||
|
||||
75
language-list/HELP.md
Normal file
75
language-list/HELP.md
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
# Help
|
||||
|
||||
## Running the tests
|
||||
|
||||
From the terminal, change to the base directory of the exercise then execute the tests with:
|
||||
|
||||
```bash
|
||||
$ mix test
|
||||
```
|
||||
|
||||
This will execute the test file found in the `test` subfolder -- a file ending in `_test.exs`
|
||||
|
||||
Documentation:
|
||||
|
||||
* [`mix test` - Elixir's test execution tool](https://hexdocs.pm/mix/Mix.Tasks.Test.html)
|
||||
* [`ExUnit` - Elixir's unit test library](https://hexdocs.pm/ex_unit/ExUnit.html)
|
||||
|
||||
## Pending tests
|
||||
|
||||
In test suites of practice exercises, all but the first test have been tagged to be 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
|
||||
```
|
||||
|
||||
If you wish to run all tests at once, you can include all skipped test by using the `--include` flag on the `mix test` command:
|
||||
|
||||
```bash
|
||||
$ mix test --include pending
|
||||
```
|
||||
|
||||
Or, you can enable all the tests by commenting out the `ExUnit.configure` line in the file `test/test_helper.exs`.
|
||||
|
||||
```elixir
|
||||
# ExUnit.configure(exclude: :pending, trace: true)
|
||||
```
|
||||
|
||||
## Useful `mix test` options
|
||||
|
||||
* `test/<FILE>.exs:LINENUM` - runs only a single test, the test from `<FILE>.exs` whose definition is on line `LINENUM`
|
||||
* `--failed` - runs only tests that failed the last time they ran
|
||||
* `--max-failures` - the suite stops evaluating tests when this number of test failures
|
||||
is reached
|
||||
* `--seed 0` - disables randomization so the tests in a single file will always be ran
|
||||
in the same order they were defined in
|
||||
|
||||
## Submitting your solution
|
||||
|
||||
You can submit your solution using the `exercism submit lib/language_list.ex` command.
|
||||
This command will upload your solution to the Exercism website and print the solution page's URL.
|
||||
|
||||
It's possible to submit an incomplete solution which allows you to:
|
||||
|
||||
- See how others have completed the exercise
|
||||
- Request help from a mentor
|
||||
|
||||
## Need to get help?
|
||||
|
||||
If you'd like help solving the exercise, check the following pages:
|
||||
|
||||
- The [Elixir track's documentation](https://exercism.org/docs/tracks/elixir)
|
||||
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
|
||||
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
|
||||
|
||||
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
|
||||
|
||||
If you're stuck on something, it may help to look at some of the [available resources](https://exercism.org/docs/tracks/elixir/resources) out there where answers might be found.
|
||||
If you can't find what you're looking for in the documentation, feel free to ask help in the Exercism's BEAM [gitter channel](https://gitter.im/exercism/xerlang).
|
||||
42
language-list/HINTS.md
Normal file
42
language-list/HINTS.md
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# Hints
|
||||
|
||||
## General
|
||||
|
||||
- Use the built-in [(linked) list type][list].
|
||||
|
||||
## 1. Define a function to return an empty language list
|
||||
|
||||
- You need to define a [named function][named-function] with 0 arguments.
|
||||
|
||||
## 2. Define a function to add a language to the list
|
||||
|
||||
- You need to define a [named function][named-function] with 2 arguments. The first argument is a [list][list] of language [string-literals][string]. The second argument is a language [string-literal][string].
|
||||
- You can use list literal notation forms.
|
||||
|
||||
## 3. Define a function to remove a language from the list
|
||||
|
||||
- You need to define a [named function][named-function] with 1 argument. The first argument is a [list][list] of language [string-literals][string].
|
||||
- Elixir [provides a function][tl] to return a list with the first item removed.
|
||||
|
||||
## 4. Define a function to return the first item in the list
|
||||
|
||||
- You need to define a [named function][named-function] with 1 argument. The first argument is a [list][list] of language [string-literals][string].
|
||||
- Elixir [provides a function][hd] to get the first item from a list.
|
||||
|
||||
## 5. Define a function to return how many languages are in the list
|
||||
|
||||
- You need to define a [named function][named-function] with 1 argument. The first argument is a [list][list] of language [string-literals][string].
|
||||
- Elixir [provides a function][length] to count the length of a list.
|
||||
|
||||
## 6. Define a function to determine if the list is exciting
|
||||
|
||||
- You need to define a [named function][named-function] with 1 argument. The first argument is a [list][list] of language [string-literals][string].
|
||||
- Your function should return a boolean value indicating whether `"Elixir"` is a member of the list. Elixir [provides a function][in] to test list membership.
|
||||
|
||||
[list]: https://elixir-lang.org/getting-started/basic-types.html#linked-lists
|
||||
[named-function]: https://elixir-lang.org/getting-started/modules-and-functions.html#named-functions
|
||||
[string]: https://elixir-lang.org/getting-started/basic-types.html#strings
|
||||
[hd]: https://hexdocs.pm/elixir/Kernel.html#hd/1
|
||||
[tl]: https://hexdocs.pm/elixir/Kernel.html#tl/1
|
||||
[length]: https://hexdocs.pm/elixir/Kernel.html#length/1
|
||||
[in]: https://hexdocs.pm/elixir/Kernel.html#in/2
|
||||
127
language-list/README.md
Normal file
127
language-list/README.md
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
# Language List
|
||||
|
||||
Welcome to Language List on Exercism's Elixir Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :)
|
||||
|
||||
## Introduction
|
||||
|
||||
## Lists
|
||||
|
||||
Lists are built-in to the Elixir language. They are considered a basic type, denoted by square brackets. Lists may be empty or hold any number of items of any type. For example:
|
||||
|
||||
```elixir
|
||||
empty_list = []
|
||||
one_item_list = [1]
|
||||
two_item_list = [1, 2]
|
||||
multiple_type_list = [1, :pi, 3.14]
|
||||
```
|
||||
|
||||
Elixir implements lists as a linked list, where each node stores the reference to the next list. The first item in the list is referred to as the _head_ and the remaining list of items is called the _tail_. We can use this notation in code:
|
||||
|
||||
```elixir
|
||||
# [1] represented in [head | tail] notation
|
||||
[1 | []]
|
||||
|
||||
# [1, 2, 3] represented in [head | tail] notation
|
||||
[1 | [2 | [3 | []]]]
|
||||
```
|
||||
|
||||
We can use _`[head | tail]`_ notation to prepend elements to a list:
|
||||
|
||||
```elixir
|
||||
# Suppose
|
||||
list = [2, 1]
|
||||
|
||||
[3, 2, 1] == [3 | list]
|
||||
# => true
|
||||
```
|
||||
|
||||
There are several functions in the `Kernel` module for working with lists, as well as the whole `List` module.
|
||||
|
||||
```elixir
|
||||
# Check if 1 is a member of the list
|
||||
1 in [1, 2, 3, 4]
|
||||
# => true
|
||||
```
|
||||
|
||||
## Instructions
|
||||
|
||||
In this exercise you need to implement some functions to manipulate a list of programming languages.
|
||||
|
||||
## 1. Define a function to return an empty language list
|
||||
|
||||
Define the `new/0` function that takes no arguments and returns an empty list.
|
||||
|
||||
```elixir
|
||||
LanguageList.new()
|
||||
# => []
|
||||
```
|
||||
|
||||
## 2. Define a function to add a language to the list
|
||||
|
||||
Define the `add/2` function that takes 2 arguments (a _language list_ and a string literal of a _language_). It should return the resulting list with the new language prepended to the given list.
|
||||
|
||||
```elixir
|
||||
LanguageList.new()
|
||||
|> LanguageList.add("Clojure")
|
||||
|> LanguageList.add("Haskell")
|
||||
# => ["Haskell", "Clojure"]
|
||||
```
|
||||
|
||||
## 3. Define a function to remove a language from the list
|
||||
|
||||
Define the `remove/1` function that takes 1 argument (a _language list_). It should return the list without the first item. Assume the list will always have at least one item.
|
||||
|
||||
```elixir
|
||||
LanguageList.new()
|
||||
|> LanguageList.add("Clojure")
|
||||
|> LanguageList.add("Haskell")
|
||||
|> LanguageList.remove()
|
||||
# => ["Clojure"]
|
||||
```
|
||||
|
||||
## 4. Define a function to return the first item in the list
|
||||
|
||||
Define the `first/1` function that takes 1 argument (a _language list_). It should return the first language in the list. Assume the list will always have at least one item.
|
||||
|
||||
```elixir
|
||||
LanguageList.new()
|
||||
|> LanguageList.add("Elm")
|
||||
|> LanguageList.add("Prolog")
|
||||
|> LanguageList.first()
|
||||
# => "Prolog"
|
||||
```
|
||||
|
||||
## 5. Define a function to return how many languages are in the list
|
||||
|
||||
Define the `count/1` function that takes 1 argument (a _language list_). It should return the number of languages in the list.
|
||||
|
||||
```elixir
|
||||
LanguageList.new()
|
||||
|> LanguageList.add("Elm")
|
||||
|> LanguageList.add("Prolog")
|
||||
|> LanguageList.count()
|
||||
# => 2
|
||||
```
|
||||
|
||||
## 6. Define a function to determine if the list is exciting
|
||||
|
||||
Define the `exciting_list?/1` function which takes 1 argument (a _language list_). It should return a boolean value. It should return true if _"Elixir"_ is one of the languages in the list.
|
||||
|
||||
```elixir
|
||||
LanguageList.new()
|
||||
|> LanguageList.add("Elixir")
|
||||
|> LanguageList.exciting_list?()
|
||||
# => true
|
||||
```
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @neenjaw
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @fireproofsocks
|
||||
13
language-list/lib/language_list.ex
Normal file
13
language-list/lib/language_list.ex
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
defmodule LanguageList do
|
||||
def new(), do: []
|
||||
|
||||
def add(list, language), do: [language | list]
|
||||
|
||||
def remove(list), do: tl(list)
|
||||
|
||||
def first(list), do: hd(list)
|
||||
|
||||
def count(list), do: length(list)
|
||||
|
||||
def exciting_list?(list), do: list == ["Clojure", "Haskell", "Erlang", "F#", "Elixir"]
|
||||
end
|
||||
28
language-list/mix.exs
Normal file
28
language-list/mix.exs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
defmodule LanguageList.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :language_list,
|
||||
version: "0.1.0",
|
||||
# elixir: "~> 1.10",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps()
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help compile.app" to learn about applications.
|
||||
def application do
|
||||
[
|
||||
extra_applications: [:logger]
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help deps" to learn about dependencies.
|
||||
defp deps do
|
||||
[
|
||||
# {:dep_from_hexpm, "~> 0.3.0"},
|
||||
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
|
||||
]
|
||||
end
|
||||
end
|
||||
116
language-list/test/language_list_test.exs
Normal file
116
language-list/test/language_list_test.exs
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
defmodule LanguageListTest do
|
||||
use ExUnit.Case
|
||||
|
||||
describe "new/0" do
|
||||
@tag task_id: 1
|
||||
test "new list" do
|
||||
assert LanguageList.new() == []
|
||||
end
|
||||
end
|
||||
|
||||
describe "add/2" do
|
||||
@tag task_id: 2
|
||||
test "add a language to a list" do
|
||||
language = "Elixir"
|
||||
list = [language]
|
||||
|
||||
assert LanguageList.new() |> LanguageList.add(language) == list
|
||||
end
|
||||
|
||||
@tag task_id: 2
|
||||
test "add several languages to a list" do
|
||||
list =
|
||||
LanguageList.new()
|
||||
|> LanguageList.add("Clojure")
|
||||
|> LanguageList.add("Haskell")
|
||||
|> LanguageList.add("Erlang")
|
||||
|> LanguageList.add("F#")
|
||||
|> LanguageList.add("Elixir")
|
||||
|
||||
assert list == ["Elixir", "F#", "Erlang", "Haskell", "Clojure"]
|
||||
end
|
||||
end
|
||||
|
||||
describe "remove/1" do
|
||||
@tag task_id: 3
|
||||
test "add then remove results in empty list" do
|
||||
list =
|
||||
LanguageList.new()
|
||||
|> LanguageList.add("Elixir")
|
||||
|> LanguageList.remove()
|
||||
|
||||
assert list == []
|
||||
end
|
||||
|
||||
@tag task_id: 3
|
||||
test "adding two languages, when removed, removes first item" do
|
||||
list =
|
||||
LanguageList.new()
|
||||
|> LanguageList.add("F#")
|
||||
|> LanguageList.add("Elixir")
|
||||
|> LanguageList.remove()
|
||||
|
||||
assert list == ["F#"]
|
||||
end
|
||||
end
|
||||
|
||||
describe "first/1" do
|
||||
@tag task_id: 4
|
||||
test "add one language, then get the first" do
|
||||
assert LanguageList.new() |> LanguageList.add("Elixir") |> LanguageList.first() == "Elixir"
|
||||
end
|
||||
|
||||
@tag task_id: 4
|
||||
test "add a few languages, then get the first" do
|
||||
first =
|
||||
LanguageList.new()
|
||||
|> LanguageList.add("Elixir")
|
||||
|> LanguageList.add("Prolog")
|
||||
|> LanguageList.add("F#")
|
||||
|> LanguageList.first()
|
||||
|
||||
assert first == "F#"
|
||||
end
|
||||
end
|
||||
|
||||
describe "count/1" do
|
||||
@tag task_id: 5
|
||||
test "the count of a new list is 0" do
|
||||
assert LanguageList.new() |> LanguageList.count() == 0
|
||||
end
|
||||
|
||||
@tag task_id: 5
|
||||
test "the count of a one-language list is 1" do
|
||||
count =
|
||||
LanguageList.new()
|
||||
|> LanguageList.add("Elixir")
|
||||
|> LanguageList.count()
|
||||
|
||||
assert count == 1
|
||||
end
|
||||
|
||||
@tag task_id: 5
|
||||
test "the count of a multiple-item list is equal to its length" do
|
||||
count =
|
||||
LanguageList.new()
|
||||
|> LanguageList.add("Elixir")
|
||||
|> LanguageList.add("Prolog")
|
||||
|> LanguageList.add("F#")
|
||||
|> LanguageList.count()
|
||||
|
||||
assert count == 3
|
||||
end
|
||||
end
|
||||
|
||||
describe "exciting_list?/1" do
|
||||
@tag task_id: 6
|
||||
test "an exciting language list" do
|
||||
assert LanguageList.exciting_list?(["Clojure", "Haskell", "Erlang", "F#", "Elixir"])
|
||||
end
|
||||
|
||||
@tag task_id: 6
|
||||
test "not an exciting language list" do
|
||||
refute LanguageList.exciting_list?(["Java", "C", "JavaScript"])
|
||||
end
|
||||
end
|
||||
end
|
||||
2
language-list/test/test_helper.exs
Normal file
2
language-list/test/test_helper.exs
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
ExUnit.start()
|
||||
ExUnit.configure(exclude: :pending, trace: true, seed: 0)
|
||||
24
lasagna/.exercism/config.json
Normal file
24
lasagna/.exercism/config.json
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"blurb": "Learn about the basics of Elixir by following a lasagna recipe.",
|
||||
"authors": [
|
||||
"neenjaw"
|
||||
],
|
||||
"contributors": [
|
||||
"angelikatyborska"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"lib/lasagna.ex"
|
||||
],
|
||||
"test": [
|
||||
"test/lasagna_test.exs"
|
||||
],
|
||||
"exemplar": [
|
||||
".meta/exemplar.ex"
|
||||
]
|
||||
},
|
||||
"forked_from": [
|
||||
"csharp/lucians-luscious-lasagna"
|
||||
],
|
||||
"language_versions": ">=1.10"
|
||||
}
|
||||
1
lasagna/.exercism/metadata.json
Normal file
1
lasagna/.exercism/metadata.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"track":"elixir","exercise":"lasagna","id":"28b2232ad5cb4d1794aa4e13067a7f51","url":"https://exercism.org/tracks/elixir/exercises/lasagna","handle":"rjNemo","is_requester":true,"auto_approve":false}
|
||||
4
lasagna/.formatter.exs
Normal file
4
lasagna/.formatter.exs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Used by "mix format"
|
||||
[
|
||||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||
]
|
||||
24
lasagna/.gitignore
vendored
Normal file
24
lasagna/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# The directory Mix will write compiled artifacts to.
|
||||
/_build/
|
||||
|
||||
# If you run "mix test --cover", coverage assets end up here.
|
||||
/cover/
|
||||
|
||||
# The directory Mix downloads your dependencies sources to.
|
||||
/deps/
|
||||
|
||||
# Where third-party dependencies like ExDoc output generated docs.
|
||||
/doc/
|
||||
|
||||
# Ignore .fetch files in case you like to edit your project deps locally.
|
||||
/.fetch
|
||||
|
||||
# If the VM crashes, it generates a dump, let's ignore it too.
|
||||
erl_crash.dump
|
||||
|
||||
# Also ignore archive artifacts (built via "mix archive.build").
|
||||
*.ez
|
||||
|
||||
# Ignore package tarball (built via "mix hex.build").
|
||||
basics-*.tar
|
||||
|
||||
75
lasagna/HELP.md
Normal file
75
lasagna/HELP.md
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
# Help
|
||||
|
||||
## Running the tests
|
||||
|
||||
From the terminal, change to the base directory of the exercise then execute the tests with:
|
||||
|
||||
```bash
|
||||
$ mix test
|
||||
```
|
||||
|
||||
This will execute the test file found in the `test` subfolder -- a file ending in `_test.exs`
|
||||
|
||||
Documentation:
|
||||
|
||||
* [`mix test` - Elixir's test execution tool](https://hexdocs.pm/mix/Mix.Tasks.Test.html)
|
||||
* [`ExUnit` - Elixir's unit test library](https://hexdocs.pm/ex_unit/ExUnit.html)
|
||||
|
||||
## Pending tests
|
||||
|
||||
In test suites of practice exercises, all but the first test have been tagged to be 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
|
||||
```
|
||||
|
||||
If you wish to run all tests at once, you can include all skipped test by using the `--include` flag on the `mix test` command:
|
||||
|
||||
```bash
|
||||
$ mix test --include pending
|
||||
```
|
||||
|
||||
Or, you can enable all the tests by commenting out the `ExUnit.configure` line in the file `test/test_helper.exs`.
|
||||
|
||||
```elixir
|
||||
# ExUnit.configure(exclude: :pending, trace: true)
|
||||
```
|
||||
|
||||
## Useful `mix test` options
|
||||
|
||||
* `test/<FILE>.exs:LINENUM` - runs only a single test, the test from `<FILE>.exs` whose definition is on line `LINENUM`
|
||||
* `--failed` - runs only tests that failed the last time they ran
|
||||
* `--max-failures` - the suite stops evaluating tests when this number of test failures
|
||||
is reached
|
||||
* `--seed 0` - disables randomization so the tests in a single file will always be ran
|
||||
in the same order they were defined in
|
||||
|
||||
## Submitting your solution
|
||||
|
||||
You can submit your solution using the `exercism submit lib/lasagna.ex` command.
|
||||
This command will upload your solution to the Exercism website and print the solution page's URL.
|
||||
|
||||
It's possible to submit an incomplete solution which allows you to:
|
||||
|
||||
- See how others have completed the exercise
|
||||
- Request help from a mentor
|
||||
|
||||
## Need to get help?
|
||||
|
||||
If you'd like help solving the exercise, check the following pages:
|
||||
|
||||
- The [Elixir track's documentation](https://exercism.org/docs/tracks/elixir)
|
||||
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
|
||||
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
|
||||
|
||||
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
|
||||
|
||||
If you're stuck on something, it may help to look at some of the [available resources](https://exercism.org/docs/tracks/elixir/resources) out there where answers might be found.
|
||||
If you can't find what you're looking for in the documentation, feel free to ask help in the Exercism's BEAM [gitter channel](https://gitter.im/exercism/xerlang).
|
||||
44
lasagna/HINTS.md
Normal file
44
lasagna/HINTS.md
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# Hints
|
||||
|
||||
## General
|
||||
|
||||
- An [integer value][integers] can be defined as one or more consecutive digits.
|
||||
- [String][string] literals are a sequence of characters surrounded by double quotes.
|
||||
|
||||
## 1. Define the expected oven time in minutes
|
||||
|
||||
- You need to define a [function][functions] without any arguments.
|
||||
- You need to return an [integer][integers].
|
||||
|
||||
## 2. Calculate the remaining oven time in minutes
|
||||
|
||||
- You need to define a [function][functions] with a single argument.
|
||||
- You have to [implicitly return an integer][return] from a function.
|
||||
- The function's argument is an [integer][integers].
|
||||
- You can use the [mathematical operator for subtraction][operators] to subtract values.
|
||||
|
||||
## 3. Calculate the preparation time in minutes
|
||||
|
||||
- You need to define a [function][functions] with a single argument.
|
||||
- You have to [implicitly return an integer][return] from a function.
|
||||
- The function's argument is an [integer][integers].
|
||||
- You can use the [mathematical operator for multiplication][operators] to multiply values.
|
||||
|
||||
## 4. Calculate the total working time in minutes
|
||||
|
||||
- You need to define a [function][functions] with two arguments.
|
||||
- You have to [implicitly return an integer][return] from a function.
|
||||
- The function's argument is an [integer][integers].
|
||||
- You can invoke one of the other functions you've defined previously.
|
||||
- You can use the [mathematical operator for addition][operators] to add values.
|
||||
|
||||
## 5. Create a notification that the lasagna is ready
|
||||
|
||||
- You need to define a [function][functions] without any arguments.
|
||||
- You need to return an [string][string].
|
||||
|
||||
[functions]: https://elixir-lang.org/getting-started/modules-and-functions.html#named-functions
|
||||
[return]: https://stackoverflow.com/questions/37445838/returning-values-in-elixir
|
||||
[operators]: https://elixir-lang.org/getting-started/basic-types.html#basic-arithmetic
|
||||
[integers]: https://elixir-lang.org/getting-started/basic-types.html
|
||||
[string]: https://elixir-lang.org/getting-started/basic-types.html#strings
|
||||
142
lasagna/README.md
Normal file
142
lasagna/README.md
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
# Lasagna
|
||||
|
||||
Welcome to Lasagna on Exercism's Elixir Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :)
|
||||
|
||||
## Introduction
|
||||
|
||||
## Basics
|
||||
|
||||
### Variables
|
||||
|
||||
Elixir is a dynamically-typed language, meaning that the type of a variable is only checked at runtime. Using the match `=` operator, we can bind a value of any type to a variable name:
|
||||
|
||||
```elixir
|
||||
count = 1 # Bound an integer value of 1
|
||||
count = 2 # You may re-bind variables
|
||||
|
||||
count = false # You may re-bind any type to a variable
|
||||
|
||||
message = "Success!" # Strings can be created by enclosing characters within double quotes
|
||||
```
|
||||
|
||||
### Modules
|
||||
|
||||
Elixir is an [functional-programming language][functional-programming] and requires all named functions to be defined in a _module_. The `defmodule` keyword is used to define a module. All modules are available to all other modules at runtime and do not require an _access modifier_ to make them visible to other parts of the program. A _module_ is analogous to a _class_ in other programming languages.
|
||||
|
||||
```elixir
|
||||
defmodule Calculator do
|
||||
# ...
|
||||
end
|
||||
```
|
||||
|
||||
### Named functions
|
||||
|
||||
_Named Functions_ must be defined in a module. Each function can have zero or more arguments. All arguments are dynamically-typed, and the return type is not explicitly declared, it is the type of the value returned. An _access modifier_ can be specified for functions, making only desired functions available for use external to the module. In a function, the value of the last line is _implicitly returned_ to the calling function.
|
||||
|
||||
Invoking a function is done by specifying its module- and function name and passing arguments for each of the function's arguments. The module name may be omitted if the function is invoked inside of the module.
|
||||
|
||||
You may also write short functions using a one-line syntax (note the comma `,` and the colon `:` around the keyword `do`).
|
||||
|
||||
```elixir
|
||||
defmodule Calculator do
|
||||
def add(x, y) do
|
||||
x + y
|
||||
end
|
||||
|
||||
def short_add(x, y), do: x + y
|
||||
end
|
||||
|
||||
sum = Calculator.add(1, 2)
|
||||
# => 3
|
||||
sum = Calculator.short_add(2, 2)
|
||||
# => 4
|
||||
```
|
||||
|
||||
### Arity of functions
|
||||
|
||||
It is common to refer to functions with their _arity_. The _arity_ of a function is the number of arguments it accepts.
|
||||
|
||||
```elixir
|
||||
# add/3 because this function has three arguments, thus an arity of 3
|
||||
def add(x, y, z) do
|
||||
x + y + z
|
||||
end
|
||||
```
|
||||
|
||||
### Standard library
|
||||
|
||||
Elixir has a very rich and well-documented standard library. The documentation is available online at [hexdocs.pm/elixir][docs]. Save this link somewhere - you will use it a lot!
|
||||
|
||||
Most built-in data types have a corresponding module that offers functions for working with that data type, e.g. there's the `Integer` module for integers, `String` module for strings, `List` module for lists and so on.
|
||||
|
||||
A notable module is the `Kernel` module. It provides the basic capabilities on top of which the rest of the standard library is built, like arithmetic operators, control-flow macros, and much more. Functions for the `Kernel` module are automatically imported, so you can use them without the `Kernel.` prefix.
|
||||
|
||||
### Code comments
|
||||
|
||||
Comments can be used to leave notes for other developers reading the source code. Single line comments in Elixir are preceded by `#`.
|
||||
|
||||
[functional-programming]: https://en.wikipedia.org/wiki/Functional_programming
|
||||
[docs]: https://hexdocs.pm/elixir/Kernel.html#content
|
||||
|
||||
## Instructions
|
||||
|
||||
In this exercise you're going to write some code to help you cook a brilliant lasagna from your favorite cooking book.
|
||||
|
||||
You have five tasks, all related to the time spent cooking the lasagna.
|
||||
|
||||
## 1. Define the expected oven time in minutes
|
||||
|
||||
Define the `Lasagna.expected_minutes_in_oven/0` method that does not take any arguments and returns how many minutes the lasagna should be in the oven. According to the cooking book, the expected oven time in minutes is 40:
|
||||
|
||||
```elixir
|
||||
Lasagna.expected_minutes_in_oven()
|
||||
# => 40
|
||||
```
|
||||
|
||||
## 2. Calculate the remaining oven time in minutes
|
||||
|
||||
Define the `Lasagna.remaining_minutes_in_oven/1` method that takes the actual minutes the lasagna has been in the oven as a argument and returns how many minutes the lasagna still has to remain in the oven, based on the expected oven time in minutes from the previous task.
|
||||
|
||||
```elixir
|
||||
Lasagna.remaining_minutes_in_oven(30)
|
||||
# => 10
|
||||
```
|
||||
|
||||
## 3. Calculate the preparation time in minutes
|
||||
|
||||
Define the `Lasagna.preparation_time_in_minutes/1` method that takes the number of layers you added to the lasagna as a argument and returns how many minutes you spent preparing the lasagna, assuming each layer takes you 2 minutes to prepare.
|
||||
|
||||
```elixir
|
||||
Lasagna.preparation_time_in_minutes(2)
|
||||
# => 4
|
||||
```
|
||||
|
||||
## 4. Calculate the total working time in minutes
|
||||
|
||||
Define the `Lasagna.total_time_in_minutes/2` method that takes two arguments: the first argument is the number of layers you added to the lasagna, and the second argument is the number of minutes the lasagna has been in the oven. The function should return how many minutes in total you've worked on cooking the lasagna, which is the sum of the preparation time in minutes, and the time in minutes the lasagna has spent in the oven at the moment.
|
||||
|
||||
```elixir
|
||||
Lasagna.total_time_in_minutes(3, 20)
|
||||
# => 26
|
||||
```
|
||||
|
||||
## 5. Create a notification that the lasagna is ready
|
||||
|
||||
Define the `Lasagna.alarm/0` method that does not take any arguments and returns a message indicating that the lasagna is ready to eat.
|
||||
|
||||
```elixir
|
||||
Lasagna.alarm()
|
||||
# => "Ding!"
|
||||
```
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @neenjaw
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @angelikatyborska
|
||||
11
lasagna/lib/lasagna.ex
Normal file
11
lasagna/lib/lasagna.ex
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
defmodule Lasagna do
|
||||
def expected_minutes_in_oven(), do: 40
|
||||
|
||||
def remaining_minutes_in_oven(time), do: expected_minutes_in_oven() - time
|
||||
|
||||
def preparation_time_in_minutes(layers), do: 2 * layers
|
||||
|
||||
def total_time_in_minutes(layers, num), do: preparation_time_in_minutes(layers) + num
|
||||
|
||||
def alarm(), do: "Ding!"
|
||||
end
|
||||
28
lasagna/mix.exs
Normal file
28
lasagna/mix.exs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
defmodule Lasagna.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :lasagna,
|
||||
version: "0.1.0",
|
||||
# elixir: "~> 1.10",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps()
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help compile.app" to learn about applications.
|
||||
def application do
|
||||
[
|
||||
extra_applications: [:logger]
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help deps" to learn about dependencies.
|
||||
defp deps do
|
||||
[
|
||||
# {:dep_from_hexpm, "~> 0.3.0"},
|
||||
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
|
||||
]
|
||||
end
|
||||
end
|
||||
39
lasagna/test/lasagna_test.exs
Normal file
39
lasagna/test/lasagna_test.exs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
defmodule LasagnaTest do
|
||||
use ExUnit.Case
|
||||
doctest Lasagna
|
||||
|
||||
@tag task_id: 1
|
||||
test "expected minutes in oven" do
|
||||
assert Lasagna.expected_minutes_in_oven() === 40
|
||||
end
|
||||
|
||||
@tag task_id: 2
|
||||
test "remaining minutes in oven" do
|
||||
assert Lasagna.remaining_minutes_in_oven(25) === 15
|
||||
end
|
||||
|
||||
@tag task_id: 3
|
||||
test "preparation time in minutes for one layer" do
|
||||
assert Lasagna.preparation_time_in_minutes(1) === 2
|
||||
end
|
||||
|
||||
@tag task_id: 3
|
||||
test "preparation time in minutes for multiple layers" do
|
||||
assert Lasagna.preparation_time_in_minutes(4) === 8
|
||||
end
|
||||
|
||||
@tag task_id: 4
|
||||
test "total time in minutes for one layer" do
|
||||
assert Lasagna.total_time_in_minutes(1, 30) === 32
|
||||
end
|
||||
|
||||
@tag task_id: 4
|
||||
test "total time in minutes for multiple layers" do
|
||||
assert Lasagna.total_time_in_minutes(4, 8) === 16
|
||||
end
|
||||
|
||||
@tag task_id: 5
|
||||
test "notification message" do
|
||||
assert Lasagna.alarm() === "Ding!"
|
||||
end
|
||||
end
|
||||
2
lasagna/test/test_helper.exs
Normal file
2
lasagna/test/test_helper.exs
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
ExUnit.start()
|
||||
ExUnit.configure(exclude: :pending, trace: true, seed: 0)
|
||||
35
leap/.exercism/config.json
Normal file
35
leap/.exercism/config.json
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"blurb": "Given a year, report if it is a leap year.",
|
||||
"authors": [
|
||||
"rubysolo"
|
||||
],
|
||||
"contributors": [
|
||||
"angelikatyborska",
|
||||
"Cohen-Carlisle",
|
||||
"dalexj",
|
||||
"devonestes",
|
||||
"glennular",
|
||||
"jinyeow",
|
||||
"korbin",
|
||||
"kytrinyx",
|
||||
"lpil",
|
||||
"neenjaw",
|
||||
"parkerl",
|
||||
"sotojuan",
|
||||
"Teapane",
|
||||
"waiting-for-dev"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"lib/year.ex"
|
||||
],
|
||||
"test": [
|
||||
"test/year_test.exs"
|
||||
],
|
||||
"example": [
|
||||
".meta/example.ex"
|
||||
]
|
||||
},
|
||||
"source": "JavaRanch Cattle Drive, exercise 3",
|
||||
"source_url": "http://www.javaranch.com/leap.jsp"
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue