diff --git a/elixir/lasagna/.exercism/config.json b/elixir/lasagna/.exercism/config.json new file mode 100644 index 0000000..0f09981 --- /dev/null +++ b/elixir/lasagna/.exercism/config.json @@ -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" +} diff --git a/elixir/lasagna/.exercism/metadata.json b/elixir/lasagna/.exercism/metadata.json new file mode 100644 index 0000000..2e8b071 --- /dev/null +++ b/elixir/lasagna/.exercism/metadata.json @@ -0,0 +1 @@ +{"track":"elixir","exercise":"lasagna","id":"4eed18b0783f416fa3f709661467ede7","url":"https://exercism.org/tracks/elixir/exercises/lasagna","handle":"JBiason","is_requester":true,"auto_approve":false} \ No newline at end of file diff --git a/elixir/lasagna/.gitignore b/elixir/lasagna/.gitignore new file mode 100644 index 0000000..682b719 --- /dev/null +++ b/elixir/lasagna/.gitignore @@ -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 + diff --git a/elixir/lasagna/HELP.md b/elixir/lasagna/HELP.md new file mode 100644 index 0000000..77d4cc3 --- /dev/null +++ b/elixir/lasagna/HELP.md @@ -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/.exs:LINENUM` - runs only a single test, the test from `.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). \ No newline at end of file diff --git a/elixir/lasagna/HINTS.md b/elixir/lasagna/HINTS.md new file mode 100644 index 0000000..64dd9f9 --- /dev/null +++ b/elixir/lasagna/HINTS.md @@ -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 \ No newline at end of file diff --git a/elixir/lasagna/README.md b/elixir/lasagna/README.md new file mode 100644 index 0000000..ed9b357 --- /dev/null +++ b/elixir/lasagna/README.md @@ -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 \ No newline at end of file diff --git a/elixir/lasagna/lib/lasagna.ex b/elixir/lasagna/lib/lasagna.ex new file mode 100644 index 0000000..9812f1a --- /dev/null +++ b/elixir/lasagna/lib/lasagna.ex @@ -0,0 +1,26 @@ +defmodule Lasagna do + @spec expected_minutes_in_oven() :: integer() + def expected_minutes_in_oven do + 40 + end + + @spec remaining_minutes_in_oven(integer()) :: integer() + def remaining_minutes_in_oven(elapsed) do + expected_minutes_in_oven() - elapsed + end + + @spec preparation_time_in_minutes(integer()) :: integer() + def preparation_time_in_minutes(layers) do + layers * 2 + end + + @spec total_time_in_minutes(integer(), integer()) :: integer() + def total_time_in_minutes(layers, minutes) do + preparation_time_in_minutes(layers) + minutes + end + + @spec alarm() :: String.t() + def alarm do + "Ding!" + end +end diff --git a/elixir/lasagna/mix.exs b/elixir/lasagna/mix.exs new file mode 100644 index 0000000..0970583 --- /dev/null +++ b/elixir/lasagna/mix.exs @@ -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 diff --git a/elixir/lasagna/test/lasagna_test.exs b/elixir/lasagna/test/lasagna_test.exs new file mode 100644 index 0000000..f562bcf --- /dev/null +++ b/elixir/lasagna/test/lasagna_test.exs @@ -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 diff --git a/elixir/lasagna/test/test_helper.exs b/elixir/lasagna/test/test_helper.exs new file mode 100644 index 0000000..e8677a3 --- /dev/null +++ b/elixir/lasagna/test/test_helper.exs @@ -0,0 +1,2 @@ +ExUnit.start() +ExUnit.configure(exclude: :pending, trace: true, seed: 0)