Initial commit

This commit is contained in:
2023-12-17 00:26:14 -05:00
commit 59d97be20c
89 changed files with 3184 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
{
"authors": [
"neenjaw"
],
"files": {
"solution": [
"lib/secrets.ex"
],
"test": [
"test/secrets_test.exs"
],
"exemplar": [
".meta/exemplar.ex"
]
},
"language_versions": ">=1.10",
"blurb": "Learn about bit manipulation and anonymous functions by writing the software for an encryption device."
}

View File

@@ -0,0 +1 @@
{"track":"elixir","exercise":"secrets","id":"baa7ad210ba049e6be7ec6f9c548a06a","url":"https://exercism.org/tracks/elixir/exercises/secrets","handle":"negrienko","is_requester":true,"auto_approve":false}

View File

@@ -0,0 +1,4 @@
# Used by "mix format"
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]

24
elixir/secrets/.gitignore vendored Normal file
View 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").
anonymous_functions-*.tar

75
elixir/secrets/HELP.md Normal file
View 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/secrets.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)
- The [Elixir track's programming category on the forum](https://forum.exercism.org/c/programming/elixir)
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
- 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.

47
elixir/secrets/HINTS.md Normal file
View File

@@ -0,0 +1,47 @@
# Hints
## General
- Make use of [anonymous functions][anon-fns].
- Use a [closure][closure] to reference the variable from the outer scope.
## 1. Create an adder
- Return an anonymous function which adds the argument from the anonymous function to the argument passed in to `Secret.secret_add/1`.
## 2. Create a subtractor
- Return an anonymous function which subtracts the argument passed in to `Secret.secret_subtract/1` from the argument from the anonymous function.
## 3. Create a multiplier
- Return an anonymous function which multiplies the argument from the anonymous function to the argument passed in to `Secret.secret_multiply/1`.
## 4. Create a divider
- Return an anonymous function which divides the argument from the anonymous function by the argument passed in to `Secret.secret_divide/1`.
- Make use of [integer division][div].
## 5. Create an "and"-er
- Return an anonymous function which performs a [bitwise _and_][bitwise-wiki] operation using the argument passed in to the anonymous function and the argument passed in to `Secret.secret_and/1`
- Functions in the [Bitwise module][bitwise-hexdocs] may be of use.
- If you are running Elixir version 1.9 or lower, you will need to call `require Bitwise` at the beginning of the module definition to be able to use the _Bitwise_ module.
## 6. Create an "xor"-er
- Return an anonymous function which performs a [bitwise _xor_][bitwise-wiki] operation using the argument passed in to the anonymous function and the argument passed in to `Secret.secret_xor/1`
- Functions in the [Bitwise module][bitwise-hexdocs] may be of use.
- If you are running Elixir version 1.9 or lower, you will need to call `require Bitwise` at the beginning of the module definition to be able to use the _Bitwise_ module.
## 7. Create a function combiner
- Return an anonymous function which [composes the functions][fn-composition] passed in to `Secret.secret_combine/2`.
- Use a `.` before `()` when calling an anonymous function.
[anon-fns]: https://elixir-lang.org/getting-started/basic-types.html#anonymous-functions
[bitwise-hexdocs]: https://hexdocs.pm/elixir/Bitwise.html
[bitwise-wiki]: https://en.wikipedia.org/wiki/Bitwise_operation
[closure]: https://en.wikipedia.org/wiki/Closure_(computer_programming)
[div]: https://hexdocs.pm/elixir/Kernel.html#div/2
[fn-composition]: https://en.wikipedia.org/wiki/Function_composition_(computer_science)

155
elixir/secrets/README.md Normal file
View File

@@ -0,0 +1,155 @@
# Secrets
Welcome to Secrets 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
## Anonymous Functions
Functions are treated as first class citizens in Elixir. This means that:
- Named and anonymous functions can be assigned to variables.
- Named and anonymous functions can be passed around like data as arguments and return values.
- Anonymous functions can be created dynamically.
Anonymous functions, in contrast to named functions, don't have a static reference available to them, they are only available if they are assigned to a variable or immediately invoked.
We might use anonymous functions to:
- Hide data using lexical scope (also known as a closure).
- Dynamically create functions at run-time.
Anonymous functions start with the reserved word `fn`, the arguments are separated from the body of the function with the `->` token, and they are finished with an `end`. As with named functions, the last expression in the function is _implicitly returned_ to the calling function.
To invoke a function reference, you must use a `.` between the reference variable and the list of arguments:
```elixir
function_variable = fn param ->
param + 1
end
function_variable.(1)
# => 2
```
You can even use short hand capture notation to make this more concise:
```elixir
variable = &(&1 + 1)
variable.(1)
# => 2
```
## Bit Manipulation
Elixir supports many functions for working with bits found in the _Bitwise module_.
- `band/2`: bitwise AND
- `bsl/2`: bitwise SHIFT LEFT
- `bsr/2`: bitwise SHIFT RIGHT
- `bxor/2`: bitwise XOR
- `bor/2`: bitwise OR
- `bnot/1`: bitwise NOT
Here is an example how to use a bitwise function:
```elixir
Bitwise.bsl(1, 3)
# => 8
```
All bitwise functions only work on integers.
If you are running Elixir version 1.9 or lower, you will need to call `require Bitwise` at the beginning of the module definition to be able to use the _Bitwise_ module.
## Instructions
In this exercise, you've been tasked with writing the software for an encryption device that works by performing transformations on data. You need a way to flexibly create complicated functions by combining simpler functions together.
For each task, return an anonymous function that can be invoked from the calling scope.
All functions should expect integer arguments. Integers are also suitable for performing bitwise operations in Elixir.
## 1. Create an adder
Implement `Secrets.secret_add/1`. It should return a function which takes one argument and adds to it the argument passed in to `secret_add`.
```elixir
adder = Secrets.secret_add(2)
adder.(2)
# => 4
```
## 2. Create a subtractor
Implement `Secrets.secret_subtract/1`. It should return a function which takes one argument and subtracts the secret passed in to `secret_subtract` from that argument.
```elixir
subtractor = Secrets.secret_subtract(2)
subtractor.(3)
# => 1
```
## 3. Create a multiplier
Implement `Secrets.secret_multiply/1`. It should return a function which takes one argument and multiplies it by the secret passed in to `secret_multiply`.
```elixir
multiplier = Secrets.secret_multiply(7)
multiplier.(3)
# => 21
```
## 4. Create a divider
Implement `Secrets.secret_divide/1`. It should return a function which takes one argument and divides it by the secret passed in to `secret_divide`.
```elixir
divider = Secrets.secret_divide(3)
divider.(32)
# => 10
```
Make use of integer division so the output is compatible with the other functions' expected input.
## 5. Create an "and"-er
Implement `Secrets.secret_and/1`. It should return a function which takes one argument and performs a bitwise _and_ operation on it and the secret passed in to `secret_and`.
```elixir
ander = Secrets.secret_and(1)
ander.(2)
# => 0
```
## 6. Create an "xor"-er
Implement `Secrets.secret_xor/1`. It should return a function which takes one argument and performs a bitwise _xor_ operation on it and the secret passed in to `secret_xor`.
```elixir
xorer = Secrets.secret_xor(1)
xorer.(3)
# => 2
```
## 7. Create a function combiner
Implement `Secrets.secret_combine/2`. It should return a function which takes one argument and applies to it the two functions passed in to `secret_combine` in order.
```elixir
multiply = Secrets.secret_multiply(7)
divide = Secrets.secret_divide(3)
combined = Secrets.secret_combine(multiply, divide)
combined.(6)
# => 14
```
## Source
### Created by
- @neenjaw

View File

@@ -0,0 +1,29 @@
defmodule Secrets do
def secret_add(secret) do
&(&1 + secret)
end
def secret_subtract(secret) do
&(&1 - secret)
end
def secret_multiply(secret) do
&(&1 * secret)
end
def secret_divide(secret) do
&(div(&1, secret))
end
def secret_and(secret) do
&(Bitwise.band(secret, &1))
end
def secret_xor(secret) do
&(Bitwise.bxor(secret, &1))
end
def secret_combine(secret_function1, secret_function2) do
&(secret_function2.(secret_function1.(&1)))
end
end

28
elixir/secrets/mix.exs Normal file
View File

@@ -0,0 +1,28 @@
defmodule Secrets.MixProject do
use Mix.Project
def project do
[
app: :secrets,
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

View File

@@ -0,0 +1,179 @@
defmodule SecretsTest do
use ExUnit.Case
describe "secret_add" do
@tag task_id: 1
test "add 3" do
add = Secrets.secret_add(3)
assert add.(3) === 6
end
@tag task_id: 1
test "add 6" do
add = Secrets.secret_add(6)
assert add.(9) === 15
end
end
describe "secret_subtract" do
@tag task_id: 2
test "subtract 3" do
subtract = Secrets.secret_subtract(3)
assert subtract.(6) === 3
end
@tag task_id: 2
test "subtract 6" do
subtract = Secrets.secret_subtract(6)
assert subtract.(3) === -3
end
end
describe "secret_multiply" do
@tag task_id: 3
test "multiply by 3" do
multiply = Secrets.secret_multiply(3)
assert multiply.(6) === 18
end
@tag task_id: 3
test "multiply by 6" do
multiply = Secrets.secret_multiply(6)
assert multiply.(7) === 42
end
end
describe "secret_divide" do
@tag task_id: 4
test "divide by 3" do
divide = Secrets.secret_divide(3)
assert divide.(6) === 2
end
@tag task_id: 4
test "divide by 6" do
divide = Secrets.secret_divide(6)
assert divide.(7) === 1
end
end
describe "secret_and" do
@tag task_id: 5
test "2 and 1" do
ander = Secrets.secret_and(1)
assert ander.(2) === 0
end
@tag task_id: 5
test "7 and 7" do
ander = Secrets.secret_and(7)
assert ander.(7) === 7
end
end
describe "secret_xor" do
@tag task_id: 6
test "2 xor 1" do
xorer = Secrets.secret_xor(1)
assert xorer.(2) === 3
end
@tag task_id: 6
test "7 xor 7" do
xorer = Secrets.secret_xor(7)
assert xorer.(7) === 0
end
end
describe "secret_combine" do
@tag task_id: 7
test "5 add 10 then subtract 5" do
f = Secrets.secret_add(10)
g = Secrets.secret_subtract(5)
h = Secrets.secret_combine(f, g)
assert h.(5) === 10
end
@tag task_id: 7
test "100 multiply by 2 then subtract 20" do
f = Secrets.secret_multiply(2)
g = Secrets.secret_subtract(20)
h = Secrets.secret_combine(f, g)
assert h.(100) === 180
end
@tag task_id: 7
test "100 divide by 10 then add 10" do
f = Secrets.secret_divide(10)
g = Secrets.secret_add(10)
h = Secrets.secret_combine(f, g)
assert h.(100) === 20
end
@tag task_id: 7
test "32 divide by 3 then add 5" do
f = Secrets.secret_divide(3)
g = Secrets.secret_add(5)
h = Secrets.secret_combine(f, g)
assert h.(32) === 15
end
@tag task_id: 7
test "7 and 3 then and 5" do
f = Secrets.secret_and(3)
g = Secrets.secret_and(5)
h = Secrets.secret_combine(f, g)
assert h.(7) === 1
end
@tag task_id: 7
test "7 and 7 then and 7" do
f = Secrets.secret_and(7)
g = Secrets.secret_and(7)
h = Secrets.secret_combine(f, g)
assert h.(7) === 7
end
@tag task_id: 7
test "4 xor 1 then xor 2" do
f = Secrets.secret_xor(1)
g = Secrets.secret_xor(2)
h = Secrets.secret_combine(f, g)
assert h.(4) === 7
end
@tag task_id: 7
test "7 xor 7 then xor 7" do
f = Secrets.secret_xor(7)
g = Secrets.secret_xor(7)
h = Secrets.secret_combine(f, g)
assert h.(7) === 7
end
@tag task_id: 7
test "4 add 3 then xor 7" do
f = Secrets.secret_add(3)
g = Secrets.secret_xor(7)
h = Secrets.secret_combine(f, g)
assert h.(4) === 0
end
@tag task_id: 7
test "81 divide by 9 then and 7" do
f = Secrets.secret_divide(9)
g = Secrets.secret_and(7)
h = Secrets.secret_combine(f, g)
assert h.(81) === 1
end
end
end

View File

@@ -0,0 +1,2 @@
ExUnit.start()
ExUnit.configure(exclude: :pending, trace: true, seed: 0)