nucleotide count + robot simulator solutions elixir

This commit is contained in:
Xevion
2021-01-18 22:45:34 -06:00
parent 2e71b743ac
commit e469b45491
16 changed files with 479 additions and 0 deletions

View File

@@ -0,0 +1 @@
{"track":"elixir","exercise":"nucleotide-count","id":"651779260530498397cb25e2a1209466","url":"https://exercism.io/my/solutions/651779260530498397cb25e2a1209466","handle":"Xevion","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/nucleotide-count/.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").
nucleotide_count-*.tar

View File

@@ -0,0 +1,55 @@
# Nucleotide Count
Given a single stranded DNA string, compute how many times each nucleotide occurs in the string.
The genetic language of every living thing on the planet is DNA.
DNA is a large molecule that is built from an extremely long sequence of individual elements called nucleotides.
4 types exist in DNA and these differ only slightly and can be represented as the following symbols: 'A' for adenine, 'C' for cytosine, 'G' for guanine, and 'T' thymine.
Here is an analogy:
- twigs are to birds nests as
- nucleotides are to DNA as
- legos are to lego houses as
- words are to sentences as...
## Running tests
Execute the tests with:
```bash
$ mix test
```
### Pending tests
In the test suites, all but the first test have been skipped.
Once you get a test passing, you can unskip the next one by
commenting out the relevant `@tag :pending` with a `#` symbol.
For example:
```elixir
# @tag :pending
test "shouting" do
assert Bob.hey("WATCH OUT!") == "Whoa, chill out!"
end
```
Or, you can enable all the tests by commenting out the
`ExUnit.configure` line in the test suite.
```elixir
# ExUnit.configure exclude: :pending, trace: true
```
If you're stuck on something, it may help to look at some of
the [available resources](https://exercism.io/tracks/elixir/resources)
out there where answers might be found.
## Source
The Calculating DNA Nucleotides_problem at Rosalind [http://rosalind.info/problems/dna/](http://rosalind.info/problems/dna/)
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -0,0 +1,32 @@
defmodule NucleotideCount do
@doc """
Counts individual nucleotides in a DNA strand.
## Examples
iex> NucleotideCount.count('AATAA', ?A)
4
iex> NucleotideCount.count('AATAA', ?T)
1
"""
@spec count(charlist(), char()) :: non_neg_integer()
def count(strand, nucleotide) do
Enum.count(strand, &(&1 == nucleotide))
end
@doc """
Returns a summary of counts by nucleotide.
## Examples
iex> NucleotideCount.histogram('AATAA')
%{?A => 4, ?T => 1, ?C => 0, ?G => 0}
"""
@spec histogram(charlist()) :: map()
def histogram(strand) do
Enum.reduce(strand, %{?A => 0, ?T => 0, ?C => 0, ?G => 0}, fn nucleotide, counts ->
Map.update(counts, nucleotide, 1, &(&1 + 1))
end)
end
end

View File

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

View File

@@ -0,0 +1,37 @@
defmodule NucleotideCountTest do
use ExUnit.Case
# @tag :pending
test "empty dna string has no adenine" do
assert NucleotideCount.count('', ?A) == 0
end
@tag :pending
test "repetitive cytosine gets counted" do
assert NucleotideCount.count('CCCCC', ?C) == 5
end
@tag :pending
test "counts only thymine" do
assert NucleotideCount.count('GGGGGTAACCCGG', ?T) == 1
end
@tag :pending
test "empty dna string has no nucleotides" do
expected = %{?A => 0, ?T => 0, ?C => 0, ?G => 0}
assert NucleotideCount.histogram('') == expected
end
@tag :pending
test "repetitive sequence has only guanine" do
expected = %{?A => 0, ?T => 0, ?C => 0, ?G => 8}
assert NucleotideCount.histogram('GGGGGGGG') == expected
end
@tag :pending
test "counts all nucleotides" do
s = 'AGCTTTTCATTCTGACTGCAACGGGCAATATGTCTCTGTGTGGATTAAAAAAAGAGTGTCTGATAGCAGC'
expected = %{?A => 20, ?T => 21, ?C => 12, ?G => 17}
assert NucleotideCount.histogram(s) == expected
end
end

View File

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

View File

@@ -0,0 +1 @@
{"track":"elixir","exercise":"robot-simulator","id":"a8c52ea3878944528a085201a24226e4","url":"https://exercism.io/my/solutions/a8c52ea3878944528a085201a24226e4","handle":"Xevion","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/robot-simulator/.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").
robot_simulator-*.tar

View File

@@ -0,0 +1,70 @@
# Robot Simulator
Write a robot simulator.
A robot factory's test facility needs a program to verify robot movements.
The robots have three possible movements:
- turn right
- turn left
- advance
Robots are placed on a hypothetical infinite grid, facing a particular
direction (north, east, south, or west) at a set of {x,y} coordinates,
e.g., {3,8}, with coordinates increasing to the north and east.
The robot then receives a number of instructions, at which point the
testing facility verifies the robot's new position, and in which
direction it is pointing.
- The letter-string "RAALAL" means:
- Turn right
- Advance twice
- Turn left
- Advance once
- Turn left yet again
- Say a robot starts at {7, 3} facing north. Then running this stream
of instructions should leave it at {9, 4} facing west.
## Running tests
Execute the tests with:
```bash
$ mix test
```
### Pending tests
In the test suites, all but the first test have been skipped.
Once you get a test passing, you can unskip the next one by
commenting out the relevant `@tag :pending` with a `#` symbol.
For example:
```elixir
# @tag :pending
test "shouting" do
assert Bob.hey("WATCH OUT!") == "Whoa, chill out!"
end
```
Or, you can enable all the tests by commenting out the
`ExUnit.configure` line in the test suite.
```elixir
# ExUnit.configure exclude: :pending, trace: true
```
If you're stuck on something, it may help to look at some of
the [available resources](https://exercism.io/tracks/elixir/resources)
out there where answers might be found.
## Source
Inspired by an interview question at a famous company.
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -0,0 +1,93 @@
defmodule RobotSimulator do
defmodule Robot, do: defstruct([:position, :direction])
@directions [:north, :east, :south, :west]
@turns %{"R" => 1, "L" => -1}
defguardp is_direction(direction) when direction in [:north, :east, :south, :west]
defguardp is_position(position)
when is_tuple(position) and tuple_size(position) == 2 and
is_integer(elem(position, 0)) and is_integer(elem(position, 1))
@doc """
Create a Robot Simulator given an initial direction and position.
Valid directions are: `:north`, `:east`, `:south`, `:west`
"""
@spec create(direction :: atom, position :: {integer, integer}) :: any
def create(direction \\ :north, position \\ {0, 0})
def create(direction, _position) when not is_direction(direction),
do: {:error, "invalid direction"}
def create(_direction, position) when not is_position(position),
do: {:error, "invalid position"}
def create(direction, position) do
%Robot{position: position, direction: direction}
end
@doc """
Simulate the robot's movement given a string of instructions.
Valid instructions are: "R" (turn right), "L", (turn left), and "A" (advance)
"""
@spec simulate(robot :: any, instructions :: String.t()) :: any
def simulate(robot, ""), do: robot
def simulate(
%Robot{position: position, direction: direction} = robot,
<<head::bytes-size(1)>> <> tail
) do
# {head, tail} = String.split_at(instructions, 1)
case head do
"A" ->
# Map.get_and_update!(robot, :position, &{&1, get_change(&1, robot.direction)})
simulate(%Robot{robot | position: get_change(position, direction)}, tail)
"L" ->
simulate(%Robot{robot | direction: get_turn(head, direction)}, tail)
"R" ->
simulate(%Robot{robot | direction: get_turn(head, direction)}, tail)
_ ->
{:error, "invalid instruction"}
end
end
@doc """
Return the robot's direction.
Valid directions are: `:north`, `:east`, `:south`, `:west`
"""
@spec direction(robot :: any) :: atom
def direction(robot) do
robot.direction
end
@doc """
Return the robot's position.
"""
@spec position(robot :: any) :: {integer, integer}
def position(robot) do
robot.position
end
def get_turn(turn, direction) do
@directions
|> Enum.fetch(rem(Enum.find_index(@directions, &(&1 == direction)) + @turns[turn], 4))
|> elem(1)
end
def get_change({x, y}, direction) do
case direction do
:north -> {x, y + 1}
:east -> {x + 1, y}
:south -> {x, y - 1}
:west -> {x - 1, y}
end
end
end

View File

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

View File

@@ -0,0 +1,74 @@
defmodule RobotSimulatorTest do
use ExUnit.Case
test "create has sensible defaults" do
robot = RobotSimulator.create()
assert RobotSimulator.position(robot) == {0, 0}
assert RobotSimulator.direction(robot) == :north
end
@tag :pending
test "create works with valid arguments" do
robot = RobotSimulator.create(:north, {0, 0})
assert RobotSimulator.position(robot) == {0, 0}
assert RobotSimulator.direction(robot) == :north
robot = RobotSimulator.create(:south, {-10, 0})
assert RobotSimulator.position(robot) == {-10, 0}
assert RobotSimulator.direction(robot) == :south
robot = RobotSimulator.create(:east, {0, 10})
assert RobotSimulator.position(robot) == {0, 10}
assert RobotSimulator.direction(robot) == :east
robot = RobotSimulator.create(:west, {100, -100})
assert RobotSimulator.position(robot) == {100, -100}
assert RobotSimulator.direction(robot) == :west
end
@tag :pending
test "create errors if invalid direction given" do
position = {0, 0}
invalid_direction = {:error, "invalid direction"}
assert RobotSimulator.create(:invalid, position) == invalid_direction
assert RobotSimulator.create(0, position) == invalid_direction
assert RobotSimulator.create("east", position) == invalid_direction
end
@tag :pending
test "create errors if invalid position given" do
direction = :north
invalid_position = {:error, "invalid position"}
assert RobotSimulator.create(direction, {0, 0, 0}) == invalid_position
assert RobotSimulator.create(direction, {0, :invalid}) == invalid_position
assert RobotSimulator.create(direction, {"0", 0}) == invalid_position
assert RobotSimulator.create(direction, "invalid") == invalid_position
assert RobotSimulator.create(direction, 0) == invalid_position
assert RobotSimulator.create(direction, [0, 0]) == invalid_position
assert RobotSimulator.create(direction, nil) == invalid_position
end
@tag :pending
test "simulate robots" do
robot1 = RobotSimulator.create(:north, {0, 0}) |> RobotSimulator.simulate("LAAARALA")
assert RobotSimulator.direction(robot1) == :west
assert RobotSimulator.position(robot1) == {-4, 1}
robot2 = RobotSimulator.create(:east, {2, -7}) |> RobotSimulator.simulate("RRAAAAALA")
assert RobotSimulator.direction(robot2) == :south
assert RobotSimulator.position(robot2) == {-3, -8}
robot3 = RobotSimulator.create(:south, {8, 4}) |> RobotSimulator.simulate("LAAARRRALLLL")
assert RobotSimulator.direction(robot3) == :north
assert RobotSimulator.position(robot3) == {11, 5}
end
@tag :pending
test "simulate errors on invalid instructions" do
assert RobotSimulator.create() |> RobotSimulator.simulate("UUDDLRLRBASTART") ==
{:error, "invalid instruction"}
end
end

View File

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