Comments (22)
Exercise: acronym
Code
defmodule Acronym do
@doc """
Generate an acronym from a string.
"This is a string" => "TIAS"
"""
@spec abbreviate(string) :: String.t()
def abbreviate(string), do: abbreviate(String.codepoints(string), "", [])
defp abbreviate([], _prev, result), do: Enum.join(result) |> String.upcase
defp abbreviate([head|tail], prev, result) do
cond do
String.match?(head, ~r{^[A-Z]}) ->
abbreviate(tail, head, result ++ [head])
String.match?(prev, ~r{\W}) && String.match?(head, ~r{^[a-z]}) ->
abbreviate(tail, head, result ++ [head])
true ->
abbreviate(tail, head, result)
end
end
end
Tags:
construct:bitstring
construct:charlist
construct:cond
construct:do-end
construct:doc-string
construct:function
construct:function-overloading
construct:invocation
construct:list
construct:module
construct:parameter
construct:pattern-matching
construct:pipe-operator
construct:string
construct:underscore-variables
construct:visibility-modifiers
paradigm:functional
paradigm:metaprogramming
paradigm:pattern-matching
technique:recursion
uses:String.codepoints
uses:String.match?
from elixir.
Exercise: simple-linked-list
Code
defmodule LinkedList do
defmodule Node do
defstruct [ data: "", next: nil ]
end
@opaque t :: tuple()
@doc """
Construct a new LinkedList
"""
@spec new() :: t
def new() do
%{head: self(), size: 0}
end
@doc """
Push an item onto a LinkedList
"""
@spec push(t, any()) :: t
def push(list, elem) do
node = %Node{data: elem, next: list[:head]}
%{head: node, size: list[:size] +1}
end
@doc """
Calculate the length of a LinkedList
"""
@spec length(t) :: non_neg_integer()
def length(list) do
list[:size]
end
@doc """
Determine if a LinkedList is empty
"""
@spec empty?(t) :: boolean()
def empty?(list) do
list[:size] == 0
end
@doc """
Get the value of a head of the LinkedList
"""
@spec peek(t) :: {:ok, any()} | {:error, :empty_list}
def peek(list) do
node = list.head
cond do
is_pid(node) ->
{:error, :empty_list}
true ->
{:ok, node.data}
end
end
@doc """
Get tail of a LinkedList
"""
@spec tail(t) :: {:ok, t} | {:error, :empty_list}
def tail(list) do
node = list.head
cond do
is_pid(node) ->
{:error, :empty_list}
true ->
next_node = node.next
{:ok, %{head: next_node}}
end
end
@doc """
Remove the head from a LinkedList
"""
@spec pop(t) :: {:ok, any(), t} | {:error, :empty_list}
def pop(list) do
case tail(list) do
{:ok, new_tail} ->
{:ok, item} = peek(list)
{:ok, item, Map.put(new_tail, :size, list[:size] -1)}
value ->
value # return {:error, :empty_list}
end
end
@doc """
Construct a LinkedList from a stdlib List
"""
@spec from_list(list()) :: t
def from_list(list) do
Enum.reverse(list)
|> Enum.reduce(LinkedList.new(), &LinkedList.push(&2,&1))
end
@doc """
Construct a stdlib List LinkedList from a LinkedList
"""
@spec to_list(t) :: list()
def to_list(list) do
go_deep(list.head, [])
end
defp go_deep(node, curr) when is_pid(node) do
Enum.reverse curr
end
defp go_deep(node, curr) do
new_curr = [node.data | curr]
go_deep(node.next, new_curr)
end
@doc """
Reverse a LinkedList
"""
@spec reverse(t) :: t
def reverse(list) do
to_list(list)
|> Enum.reverse
|> from_list
end
end
Tags:
construct:add
construct:annotation
construct:assignment
construct:boolean
construct:case
construct:constructor
construct:def
construct:defmodule
construct:doc-string
construct:field
construct:function
construct:header
construct:invocation
construct:keyword-list
construct:list
construct:map
construct:named-argument
construct:number
construct:parameter
construct:pattern-matching
construct:pipe
construct:struct
construct:subtract
construct:tuple
construct:variable
construct:visibility-modifiers
paradigm:functional
paradigm:imperative
paradigm:object-oriented
technique:higher-order-functions
technique:recursion
from elixir.
Exercise: anagram
Code
defmodule Anagram do
@doc """
Returns all candidates that are anagrams of, but not equal to, 'base'.
"""
@spec match(String.t, [String.t]) :: [String.t]
def match(base, candidates) do
Enum.filter(candidates, &(are_anagrams?(base, &1) && !are_similar?(base, &1)))
end
defp are_anagrams?(a, b) do
fingerprint(a) == fingerprint(b)
end
defp fingerprint(str) do
str |> String.downcase |> String.to_char_list |> fingerprint(%{})
end
defp fingerprint(list, acc) do
case list do
[] -> acc
[h|t] -> fingerprint(t, Map.put(acc, h, Map.get(acc, h, 0) + 1))
end
end
defp are_similar?(a, b) do
String.downcase(a) == String.downcase(b)
end
end
Tags:
construct:add
construct:ampersand
construct:annotation
construct:case
construct:char-list
construct:constructor
construct:doc-string
construct:invocation
construct:lambda
construct:list
construct:map
construct:module
construct:number
construct:parameter
construct:pattern-matching
construct:recursion
construct:string
construct:underscore
construct:visibility-modifiers
paradigm:functional
paradigm:object-oriented
technique:higher-order-functions
technique:recursion
from elixir.
Exercise: triangle
Code
defmodule Triangle do
@type kind :: :equilateral | :isosceles | :scalene
@side_count_type_mapping %{ 1 => :equilateral, 2 => :isosceles, 3 => :scalene }
@doc """
Return the kind of triangle of a triangle with 'a', 'b' and 'c' as lengths.
"""
@spec kind(number, number, number) :: { :ok, kind } | { :error, String.t }
def kind(a, b, c) do
list = [head|tail] = Enum.reverse Enum.sort [a,b,c]
cond do
Enum.any?(list, &(&1 <= 0)) -> { :error, "all side lengths must be positive" }
Enum.sum(tail) <= head -> { :error, "side lengths violate triangle inequality" }
true -> { :ok, @side_count_type_mapping[length(Enum.uniq(list))] }
end
end
end
Tags:
construct:assignment
construct:atom
construct:boolean
construct:cond
construct:do-end
construct:documentation
construct:expression
construct:field
construct:guard
construct:invocation
construct:list
construct:map
construct:module
construct:number
construct:pattern-matching
construct:sort
construct:string
construct:struct
construct:term-ordering
construct:tuple
construct:type
construct:type-conversion
construct:variable
construct:visibility-modifiers
paradigm:functional
paradigm:imperative
paradigm:metaprogramming
paradigm:pattern-matching
technique:sorting
from elixir.
Exercise: beer-song
Code
defmodule BeerSong do
@doc """
Get a single verse of the beer song
"""
@spec verse(integer) :: String.t
def verse(number) do
"#{first_line number-1}\n#{second_line number-2}\n"
end
def lyrics, do: lyrics(100..1)
@doc """
Get the entire beer song for a given range of numbers of bottles.
"""
@spec lyrics(Range.t) :: String.t
def lyrics(range) do
range
|> Enum.map(fn(i) -> verse(i) end)
|> Enum.join("\n")
end
defp first_line(0), do: "No more bottles of beer on the wall, no more bottles of beer."
defp first_line(1), do: "1 bottle of beer on the wall, 1 bottle of beer."
defp first_line(number), do: "#{number} bottles of beer on the wall, #{number} bottles of beer."
defp second_line(-1), do: "Go to the store and buy some more, 99 bottles of beer on the wall."
defp second_line(0), do: "Take it down and pass it around, no more bottles of beer on the wall."
defp second_line(1), do: "Take one down and pass it around, 1 bottle of beer on the wall."
defp second_line(number), do: "Take one down and pass it around, #{number} bottles of beer on the wall."
end
Tags:
construct:string-interpolation
construct:annotation
construct:atom
construct:bitstring
construct:charlist
construct:doc-string
construct:function
construct:function-overloading
construct:integer
construct:invocation
construct:method
construct:module
construct:number
construct:parameter
construct:pattern-matching
construct:pipe
construct:private-function
construct:range
construct:string
construct:subtract
construct:tag
construct:visibility-modifiers
paradigm:functional
paradigm:object-oriented
technique:functions
technique:higher-order-functions
from elixir.
Exercise: isogram
Code
defmodule Isogram do
@doc """
Determines if a word or sentence is an isogram
"""
@spec isogram?(String.t()) :: boolean
def isogram?(sentence) do
letters =
sentence
|> String.replace(~r/\s|-|_/, "")
|> String.codepoints()
original = letters |> Enum.count()
modified = letters |> Enum.uniq() |> Enum.count()
# IO.inspect([letters, original, modified])
original == modified
end
end
Tags:
construct:comment
construct:defmodule
construct:do
construct:module-attribute
construct:parameter
construct:pipeline
construct:regex
construct:string
construct:underscore
construct:verbatim-string
construct:visibility-modifiers
paradigm:functional
paradigm:regular-expressions
technique:higher-order-functions
uses:Regex
from elixir.
Exercise: etl
Code
defmodule ETL do
@doc """
Transform an index into an inverted index.
## Examples
iex> ETL.transform(%{"a" => ["ABILITY", "AARDVARK"]}, "b" => ["BALLAST", "BEAUTY"]})
%{"ability" => "a", "aardvark" => "a", "ballast" => "b", "beauty" =>"b"}
"""
@spec transform(Dict.t) :: map()
def transform(input) do
Enum.reduce input, %{}, fn({old_key, old_values}, map) ->
Enum.reduce old_values, map, fn(old_value, map2) ->
Dict.put map2, String.downcase(old_value), old_key
end
end
end
end
Tags:
construct:charlist
construct:definition
construct:doc-string
construct:fn
construct:function
construct:invocation
construct:map
construct:module
construct:parameter
construct:pattern-matching
construct:reduce
construct:string
construct:tuple
construct:visibility-modifiers
paradigm:functional
paradigm:object-oriented
technique:higher-order-functions
from elixir.
Exercise: grains
Code
defmodule Grains do
@doc """
Calculate two to the power of the input minus one.
"""
@spec square(pos_integer) :: pos_integer
def square(1), do: 1
def square(2), do: 2
def square(n), do: 2 * square(n - 1)
@doc """
Adds square of each number from 1 to 64.
"""
@spec total :: pos_integer
def total, do: total(64)
def total(1), do: 1
def total(n), do: square(n) + total(n - 1)
end
Tags:
construct:add
construct:annotation
construct:attribute
construct:doc-string
construct:invocation
construct:method
construct:multiply
construct:parameter
construct:recursion
construct:specification
construct:subtract
paradigm:functional
technique:higher-order-functions
technique:math
technique:recursion
from elixir.
Exercise: change
Code
defmodule Change do
@doc """
Determine the least number of coins to be given to the user such
that the sum of the coins' value would equal the correct amount of change.
It returns :error if it is not possible to compute the right amount of coins.
Otherwise returns the tuple {:ok, map_of_coins}
## Examples
iex> Change.generate(3, [5, 10, 15])
:error
iex> Change.generate(18, [1, 5, 10])
{:ok, %{1 => 3, 5 => 1, 10 => 1}}
"""
@spec generate(integer, list) :: {:ok, map} | :error
def generate(amount, values) do
do_generate(
amount,
Enum.sort(values, &(&2 < &1)),
Enum.into(values, %{ }, &{&1, 0})
)
end
defp do_generate(0, _values, change), do: {:ok, change}
defp do_generate(amount, values, change) do
case Enum.find(values, fn value -> value <= amount end) do
nil ->
:error
coin ->
do_generate(
amount - coin,
values,
Map.update!(change, coin, &(&1 + 1))
)
end
end
end
Tags:
construct:add
construct:assignment
construct:atom
construct:case
construct:docstring
construct:function
construct:invocation
construct:lambda
construct:list
construct:map
construct:module
construct:parameter
construct:pattern-matching
construct:recursion
construct:sort
construct:specification
construct:subtract
construct:tuple
construct:underscore
construct:visibility-modifiers
paradigm:functional
paradigm:imperative
paradigm:metaprogramming
paradigm:recursive
technique:higher-order-functions
uses:Enum.sort
uses:Map.update!
from elixir.
Exercise: scale-generator
Code
defmodule ScaleGenerator do
@doc """
Find the note for a given interval (`step`) in a `scale` after the `tonic`.
"m": one semitone
"M": two semitones (full tone)
"A": augmented second (three semitones)
Given the `tonic` "D" in the `scale` (C C# D D# E F F# G G# A A# B C), you
should return the following notes for the given `step`:
"m": D#
"M": E
"A": F
"""
@steps %{ "m" => 1, "M" => 2, "A" => 3 }
@spec step(scale :: list(String.t()), tonic :: String.t(), step :: String.t()) :: list(String.t())
def step scale, tonic, step do
offset = Enum.find_index(scale, &(&1 == tonic)) + @steps[step]
Enum.at scale, offset
end
@doc """
The chromatic scale is a musical scale with thirteen pitches, each a semitone
(half-tone) above or below another.
Notes with a sharp (#) are a semitone higher than the note below them, where
the next letter note is a full tone except in the case of B and E, which have
no sharps.
Generate these notes, starting with the given `tonic` and wrapping back
around to the note before it, ending with the tonic an octave higher than the
original. If the `tonic` is lowercase, capitalize it.
"C" should generate: ~w(C C# D D# E F F# G G# A A# B C)
"""
@chromatic_scale ~w[ C C# D D# E F F# G G# A A# B ]
@spec chromatic_scale(tonic :: String.t()) :: list(String.t())
def chromatic_scale tonic \\ "C" do
_chromatic_scale @chromatic_scale, tonic
end
@doc """
Sharp notes can also be considered the flat (b) note of the tone above them,
so the notes can also be represented as:
A Bb B C Db D Eb E F Gb G Ab
Generate these notes, starting with the given `tonic` and wrapping back
around to the note before it, ending with the tonic an octave higher than the
original. If the `tonic` is lowercase, capitalize it.
"C" should generate: ~w(C Db D Eb E F Gb G Ab A Bb B C)
"""
@flat_chromatic_scale ~w[ C Db D Eb E F Gb G Ab A Bb B ]
@spec flat_chromatic_scale(tonic :: String.t()) :: list(String.t())
def flat_chromatic_scale tonic \\ "C" do
_chromatic_scale @flat_chromatic_scale, tonic
end
defp _chromatic_scale scale, tonic do
offset = Enum.find_index scale, &(&1 == String.capitalize(tonic))
Enum.slice(scale, offset..-1) ++ Enum.take(scale, offset+1)
end
@doc """
Certain scales will require the use of the flat version, depending on the
`tonic` (key) that begins them, which is C in the above examples.
For any of the following tonics, use the flat chromatic scale:
F Bb Eb Ab Db Gb d g c f bb eb
For all others, use the regular chromatic scale.
"""
@spec find_chromatic_scale(tonic :: String.t()) :: list(String.t())
def find_chromatic_scale(tonic) when tonic in ~w[ F Bb Eb Ab Db Gb d g c f bb eb ] do
flat_chromatic_scale tonic
end
def find_chromatic_scale tonic do
chromatic_scale tonic
end
@doc """
The `pattern` string will let you know how many steps to make for the next
note in the scale.
For example, a C Major scale will receive the pattern "MMmMMMm", which
indicates you will start with C, make a full step over C# to D, another over
D# to E, then a semitone, stepping from E to F (again, E has no sharp). You
can follow the rest of the pattern to get:
C D E F G A B C
"""
@spec scale(tonic :: String.t(), pattern :: String.t()) :: list(String.t())
def scale(tonic, pattern) do
next_step find_chromatic_scale(tonic), pattern
end
defp next_step(tonic, ""), do: tonic
defp next_step(scale, pattern) do
{ step, next_pattern } = String.split_at pattern, 1
{ _, next_scale } = Enum.split scale, @steps[step]
[ List.first(scale) | next_step(next_scale, next_pattern) ]
end
end
Tags:
construct:add
construct:assignment
construct:atom
construct:bitstring
construct:charlist
construct:doc-comment
construct:enum
construct:field
construct:hexadecimal-integer
construct:indexing
construct:invocation
construct:keyword-argument
construct:list
construct:map
construct:module
construct:number
construct:optional-parameter
construct:pattern-matching
construct:recursion
construct:string
construct:struct
construct:tagged-atom
construct:tuple
construct:underscore
construct:variable
construct:when-clause
paradigm:functional
paradigm:imperative
paradigm:metaprogramming
paradigm:recursive
technique:bit-manipulation
technique:bit-shifting
from elixir.
Exercise: gigasecond
Code
defmodule Gigasecond do
@doc """
Calculate a date one billion seconds after an input date.
"""
@spec from({pos_integer, pos_integer, pos_integer}) :: :calendar.date
def from({year, month, day}) do
date_to_seconds({year,month,day}) + 1_000_000_000
|> seconds_to_date
end
@spec date_to_seconds({pos_integer, pos_integer, pos_integer}) :: pos_integer
defp date_to_seconds({year, month, day}) do
{{year,month,day}, {0,0,0}}
|> :calendar.datetime_to_gregorian_seconds
end
@spec seconds_to_date(number) :: :calendar.date
defp seconds_to_date(seconds) do
seconds
|> :calendar.gregorian_seconds_to_datetime
|> Tuple.to_list
|> hd
end
end
Tags:
construct:add
construct:attribute
construct:date
construct:date_to_seconds
construct:def
construct:defp
construct:doc
construct:hexadecimal
construct:integer
construct:invocation
construct:method
construct:module
construct:number
construct:parameter
construct:pipeline
construct:specification
construct:tuple
construct:underscored_number
construct:variable
construct:visibility-modifiers
paradigm:functional
technique:functional-composition
uses:Tuple
from elixir.
Exercise: binary-search-tree
Code
defmodule BinarySearchTree do
@type bst_node :: %{data: any, left: bst_node | nil, right: bst_node | nil}
@doc """
Create a new Binary Search Tree with root's value as the given 'data'
"""
@spec new(any) :: bst_node
def new(data), do: %{ data: data, left: nil, right: nil }
@doc """
Creates and inserts a node with its value as 'data' into the tree.
"""
@spec insert(bst_node, any) :: bst_node
def insert(nil, new_data), do: new(new_data)
def insert(tree = %{ data: data, left: left, right: _}, new_data) when data >= new_data do
%{ tree | left: insert(left, new_data) }
end
def insert(tree, new_data), do: %{ tree | right: insert(tree[:right], new_data) }
@doc """
Traverses the Binary Search Tree in order and returns a list of each node's data.
"""
@spec in_order(bst_node) :: [any]
def in_order(nil), do: []
def in_order(tree), do: in_order(tree[:left]) ++ [ tree[:data] ] ++ in_order(tree[:right])
end
Tags:
construct:assignment
construct:atom
construct:binary
construct:bitstring
construct:boolean
construct:charlist
construct:doc-comment
construct:field-label
construct:head
construct:invocation
construct:keyword-arguments
construct:list
construct:map
construct:module
construct:parameter
construct:pattern-matching
construct:record
construct:string
construct:tail
construct:type
construct:type-alias
construct:underscore
construct:variable
construct:visibility-modifiers
paradigm:functional
paradigm:imperative
paradigm:object-oriented
technique:bit-manipulation
technique:bit-shifting
technique:bitwise-operations
technique:higher-order-functions
technique:recursion
uses:BinarySearchTree
from elixir.
Exercise: prime-factors
Code
defmodule PrimeFactors do
@spec factors_for(pos_integer) :: [pos_integer]
def factors_for(number) do
factors_for(number, 2, [])
end
defp factors_for(1, _factor, factors), do: Enum.reverse(factors)
defp factors_for(number, factor, factors) when number < factor * factor,
do: Enum.reverse(factors, [number])
defp factors_for(number, factor, factors) when rem(number, factor) == 0,
do: factors_for(div(number, factor), factor, [ factor | factors ])
defp factors_for(number, factor, factors),
do: factors_for(number, factor + 1, factors)
end
Tags:
construct:add
construct:bitwise-xor
construct:div
construct:head
construct:invocation
construct:lambda
construct:list
construct:parameter
construct:pattern-matching
construct:private-function
construct:recursion
construct:when-clause
paradigm:functional
paradigm:declarative
technique:bit-manipulation
technique:bit-shifting
from elixir.
Exercise: robot-simulator
Code
defmodule RobotSimulator do
@directions [:north, :east, :south, :west]
@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 direction in @directions do
{ :error, "invalid direction" }
end
def create(direction, {x,y}) when is_number(x) and is_number(y) do
%{position: {x,y}, direction: direction}
end
def create(_direction, _position), do: { :error, "invalid position" }
@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, instructions) do
{next, rest} = String.next_grapheme(instructions)
dir = RobotSimulator.direction(robot)
{x,y} = RobotSimulator.position(robot)
cond do
rest == "" -> move(dir, {x,y}, next)
Regex.match?(~r/[RLA]/, next) -> move(dir, {x,y}, next) |> simulate(rest)
true -> { :error, "invalid instruction" }
end
end
defp move(:north, {x,y}, "A"), do: RobotSimulator.create(:north, {x, y + 1})
defp move(:east, {x,y}, "A"), do: RobotSimulator.create(:east, {x + 1, y})
defp move(:south, {x,y}, "A"), do: RobotSimulator.create(:south, {x, y - 1})
defp move(:west, {x,y}, "A"), do: RobotSimulator.create(:west, {x - 1, y})
defp move(dir, {x,y}, "R") do
new_index = Enum.find_index(@directions, &(&1 == dir)) + 1
RobotSimulator.create(Enum.at(@directions, rem(new_index, 4)), {x,y})
end
defp move(dir, {x,y}, "L") do
new_index = Enum.find_index(@directions, &(&1 == dir)) - 1
RobotSimulator.create(Enum.at(@directions, rem(new_index, 4)), {x,y})
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
@doc """
Return the robot's position.
"""
@spec position(robot :: any) :: { integer, integer }
def position(robot), do: robot.position
end
Tags:
construct:add
construct:and
construct:atom
construct:assignment
construct:bitstring
construct:boolean
construct:charlist
construct:cond
construct:constructor
construct:doc-string
construct:enum
construct:field
construct:header
construct:hexadecimal-integer
construct:implicit-conversion
construct:index
construct:invocation
construct:keyword-argument
construct:list
construct:map
construct:module
construct:number
construct:optional-parameter
construct:parameter
construct:pattern-matching
construct:pipe
construct:string
construct:struct
construct:subtract
construct:tag
construct:tuples
construct:underscore
construct:variable
construct:visibility-modifiers
paradigm:functional
paradigm:imperative
paradigm:metaprogramming
paradigm:object-oriented
technique:bit-manipulation
technique:boolean-logic
technique:higher-order-functions
technique:recursion
uses:Regex
from elixir.
Exercise: bowling
Code
defmodule Bowling do
@doc """
Creates a new game of bowling that can be used to store the results of
the game
"""
@spec start() :: any
def start do
[]
end
@doc """
Records the number of pins knocked down on a single roll. Returns `:ok`
unless there is something wrong with the given number of pins, in which
case it returns a helpful message.
"""
def roll(_, roll) when not (roll in 0..10) do
{ :error, "Pins must have a value from 0 to 10" }
end
def roll([{10} | rest], roll) do
[ {roll}, {10} | rest ]
end
def roll([{a} | _], roll) when a + roll > 10 do
{:error, "Pin count exceeds pins on the lane"}
end
def roll([{a} | rest], roll) do
[ {a,roll} | rest ]
end
def roll(game, roll) do
[ {roll} | game ]
end
@doc """
Returns the score of a given game of bowling if the game is complete.
If the game isn't complete, it returns a helpful message.
"""
@game_not_finished { :error, "Score cannot be taken until the end of the game" }
@too_many_frames { :error, "Invalid game: too many frames" }
@spec score(any) :: integer | String.t
def score(game) do
cond do
length(game) == 10 -> score(Enum.reverse(game), 0)
length(game) == 11 ->
case game do
[ {u}, {a,b} | _ ] when a + b == 10 -> score(Enum.reverse([{:extra, {u}} | tl(game)]), 0)
[ {x,y}, {10} | _ ] -> score(Enum.reverse([{:extra, {x,y}} | tl(game)]), 0)
[ {_}, {10} | _ ] -> @game_not_finished
_ -> @too_many_frames
end
length(game) == 12 ->
case game do
[ {10}, {10}, {10} | rest ] -> score( Enum.reverse( [{:extra, {10,10}}, {10} | rest ] ), 0 )
true -> @too_many_frames
end
length(game) < 10 -> @game_not_finished
true -> @too_many_frames
end
end
def nextroll( [] ) do
@game_not_finished
end
def nextroll( [ {:extra, n} | rest ] ) do
nextroll( [n | rest] )
end
def nextroll( [ {n} | rest ] ) do
{n, rest}
end
def nextroll( [ {n,x} | rest ] ) do
{n, [{x} | rest] }
end
def score( [ {:extra, _} | _ ], acc) do
acc
end
def score( [ {a,b} | rest ], acc ) when a + b == 10 do
case nextroll rest do
{:error, str} -> {:error, str}
{x, _} -> score(rest, acc + 10 + x)
end
end
def score( [ {a,b} | rest ], acc ) do
score(rest, acc + a + b)
end
def score( [ {10} | rest ], acc ) do
case nextroll rest do
{:error, str} -> {:error, str}
{x, rem1} ->
case nextroll rem1 do
{:error, str} -> {:error, str}
{y, _} -> score(rest, acc + 10 + x + y)
end
end
end
def score( [], acc ) do
acc
end
end
Tags:
construct:add
construct:assignment
construct:atom
construct:case
construct:cond
construct:constructor
construct:doc-comment
construct:head
construct:integer
construct:invocation
construct:length
construct:list
construct:module
construct:parameter
construct:pattern-matching
construct:recursion
construct:specification
construct:string
construct:underscore
construct:variable
construct:visibility-modifiers
paradigm:functional
paradigm:imperative
paradigm:object-oriented
technique:recursion
from elixir.
Exercise: clock
Code
defmodule Clock do
defstruct hour: 0, minute: 0
@doc """
Returns a string representation of a clock:
iex> Clock.new(8, 9) |> to_string
"08:09"
"""
@spec new(integer, integer) :: Clock
def new(hour, minute) do
%Clock{} |> add(hour * 60) |> add(minute)
end
@doc """
Adds two clock times:
iex> Clock.add(10, 0) |> Clock.add(3) |> to_string
"10:03"
"""
@spec add(Clock, integer) :: Clock
def add(%Clock{hour: hour, minute: minute}, add_minute) do
hours_from_minutes = div(add_minute, 60)
remaining_minutes = rem(add_minute, 60)
hours = cond do
minute + remaining_minutes < 0 -> hours_from_minutes - 1
minute + remaining_minutes >= 60 -> hours_from_minutes + 1
true -> hours_from_minutes
end
%Clock{
hour: (hour + hours) |> scale_to(24),
minute: (minute + remaining_minutes) |> scale_to(60)
}
end
defp scale_to(number, max) when is_integer(number) and is_integer(max) and max > 0 do
number
|> rem(max)
|> Kernel.+(max)
|> rem(max)
end
defimpl String.Chars, for: Clock do
def to_string(%Clock{hour: hour, minute: minute}) do
[hour, minute]
|> Enum.map(&(&1 |> Integer.to_string |> String.rjust(2, ?0)))
|> Enum.join(":")
end
end
end
Tags:
construct:add
construct:and
construct:attribute
construct:boolean
construct:charlist
construct:cond
construct:constructor
construct:defimpl
construct:doc-string
construct:divide
construct:field
construct:function
construct:integer
construct:integral-number
construct:invocation
construct:lambda
construct:list
construct:map
construct:module
construct:multiply
construct:number
construct:parameter
construct:pattern-matching
construct:pipe
construct:struct
construct:subtract
construct:tag
construct:variable
construct:visibility-modifiers
paradigm:functional
paradigm:object-oriented
technique:higher-order-functions
from elixir.
Exercise: say
Code
defmodule Say do
def in_english(0) do
{:ok, "zero"}
end
def in_english(number) when 0 < number and number < 1_000_000_000_000 do
english = number |> groups_of_3d() |> groups_eng()
{:ok, english}
end
def in_english(_) do
{:error, "number is out of range"}
end
defp groups_of_3d(number) do
_groups_of_3d(number, [])
end
defp _groups_of_3d(0, acc) do
Enum.reverse(acc)
end
defp _groups_of_3d(number, acc) do
_groups_of_3d(div(number, 1000), [rem(number, 1000) | acc])
end
defp groups_eng(groups) do
groups
|> Enum.with_index()
|> Enum.map(&group_eng/1)
|> Enum.reject(&is_nil/1)
|> Enum.reverse()
|> Enum.join(" ")
end
defp group_eng({0, _group_index}) do
nil
end
defp group_eng({num_3d, 0}) do
"#{num_3d_eng(num_3d)}"
end
defp group_eng({num_3d, group_index}) do
"#{num_3d_eng(num_3d)} #{group_name(group_index)}"
end
defp group_name(1), do: "thousand"
defp group_name(2), do: "million"
defp group_name(3), do: "billion"
defp num_3d_eng(num_3d) do
hundreds = div(num_3d, 100)
num_2d = rem(num_3d, 100)
case {hundreds, num_2d} do
{0, _} -> "#{num_2d_eng(num_2d)}"
{_, 0} -> "#{num_2d_eng(hundreds)} hundred"
_ -> "#{num_2d_eng(hundreds)} hundred #{num_2d_eng(num_2d)}"
end
end
defp num_2d_eng(0), do: nil
defp num_2d_eng(1), do: "one"
defp num_2d_eng(2), do: "two"
defp num_2d_eng(3), do: "three"
defp num_2d_eng(4), do: "four"
defp num_2d_eng(5), do: "five"
defp num_2d_eng(6), do: "six"
defp num_2d_eng(7), do: "seven"
defp num_2d_eng(8), do: "eight"
defp num_2d_eng(9), do: "nine"
defp num_2d_eng(10), do: "ten"
defp num_2d_eng(11), do: "eleven"
defp num_2d_eng(12), do: "twelve"
defp num_2d_eng(13), do: "thirteen"
defp num_2d_eng(14), do: "fourteen"
defp num_2d_eng(15), do: "fifteen"
defp num_2d_eng(16), do: "sixteen"
defp num_2d_eng(17), do: "seventeen"
defp num_2d_eng(18), do: "eighteen"
defp num_2d_eng(19), do: "nineteen"
defp num_2d_eng(20), do: "twenty"
defp num_2d_eng(30), do: "thirty"
defp num_2d_eng(40), do: "forty"
defp num_2d_eng(50), do: "fifty"
defp num_2d_eng(60), do: "sixty"
defp num_2d_eng(70), do: "seventy"
defp num_2d_eng(80), do: "eighty"
defp num_2d_eng(90), do: "ninty"
defp num_2d_eng(num_2d) do
tens = num_2d |> div(10) |> Kernel.*(10) |> num_2d_eng()
ones = num_2d |> rem(10) |> num_2d_eng()
if ones, do: "#{tens}-#{ones}", else: tens
end
end
Tags:
construct:and
construct:assignment
construct:atom
construct:binary
construct:bitstring
construct:case
construct:charlist
construct:divide
construct:do
construct:enum
construct:expression
construct:guard
construct:hexadecimal
construct:if
construct:if
construct:if_unless
construct:implicit_capture
construct:indexing
construct:invocation
construct:lambda
construct:list
construct:map
construct:module
construct:number
construct:parameter
construct:pattern_matching
construct:pipeline
construct:private_function
construct:string
construct:underscore
construct:variable
construct:when_guard
paradigm:functional
paradigm:imperative
paradigm:object-oriented
technique:higher-order-functions
from elixir.
Exercise: say
Code
defmodule Say do
@doc """
Translate a positive integer into English.
"""
@basics %{
1 => "one",
2 => "two",
3 => "three",
4 => "four",
5 => "five",
6 => "six",
7 => "seven",
8 => "eight",
9 => "nine",
10 => "ten",
11 => "eleven",
12 => "twelve",
13 => "thirteen",
14 => "fourteen",
15 => "fifteen",
16 => "sixteen",
17 => "seventeen",
18 => "eighteen",
19 => "nineteen",
20 => "twenty",
30 => "thirty",
40 => "forty",
50 => "fifty",
60 => "sixty",
70 => "seventy",
80 => "eighty",
90 => "ninety",
}
@spec in_english(integer) :: {atom, String.t}
def in_english(number) when number < 0 or number > 999_999_999_999, do: {:error, "number is out of range"}
def in_english(0), do: {:ok, "zero"}
def in_english(number) do
{:ok, do_in_english(number, 1_000_000_000, [])}
end
def do_in_english(number, 10, acc) do
acc ++ [two_digits(number)]
|> Enum.join(" ")
|> String.trim
end
def do_in_english(number, 100, acc) do
do_in_english(rem(number, 100), 10, acc ++ [hundreds(div(number, 100))])
end
def do_in_english(number, 1_000, acc) do
do_in_english(rem(number, 1_000), 100, acc ++ [thousands(div(number, 1_000))])
end
def do_in_english(number, 1_000_000, acc) do
do_in_english(rem(number, 1_000_000), 1_000, acc ++ [millions(div(number, 1_000_000))])
end
def do_in_english(number, 1_000_000_000, acc) do
do_in_english(rem(number, 1_000_000_000), 1_000_000, acc ++ [billions(div(number, 1_000_000_000))])
end
defp two_digits(number) do
case div(number, 10) do
n when n in [0, 1] -> Map.get(@basics, number, "")
n -> Map.get(@basics, n * 10) <> "-" <> Map.get(@basics, rem(number, 10), "") |> String.trim_trailing("-")
end
end
defp hundreds(0), do: ""
defp hundreds(number) do
do_in_english(number, 10, []) <> " hundred"
end
defp thousands(0), do: ""
defp thousands(number) do
do_in_english(number, 100, []) <> " thousand"
end
defp millions(0), do: ""
defp millions(number) do
do_in_english(number, 100, []) <> " million"
end
defp billions(0), do: ""
defp billions(number) do
do_in_english(number, 100, []) <> " billion"
end
end
Tags:
construct:atom
construct:binary
construct:bitstring
construct:boolean
construct:case
construct:charlist
construct:div
construct:do-end
construct:doc-string
construct:double
construct:exponentiation
construct:expression
construct:floating-point-number
construct:function
construct:function-overloading
construct:guard
construct:hexadecimal-integer
construct:implicit-conversion
construct:integer
construct:invocation
construct:list
construct:map
construct:module
construct:multiply
construct:number
construct:parameter
construct:pattern-matching
construct:record
construct:string
construct:underscored-number
construct:variable
construct:visibility-modifiers
paradigm:functional
paradigm:imperative
paradigm:object-oriented
technique:bit-manipulation
technique:bit-shifting
technique:bitwise-operations
technique:higher-order-functions
technique:looping
uses:Map
uses:charlist
uses:recursion
from elixir.
Exercise: say
Code
defmodule Say do
@doc """
Translate a positive integer into English.
"""
@spec in_english(integer) :: {atom, String.t}
def in_english(number) when (number < 0) or (number > 999_999_999_999) do
{:error, "number is out of range"}
end
def in_english(number) do
translated =
number
|> chunk
|> Enum.map(&to_english(&1))
|> Enum.join("\s")
{:ok, translated}
end
@spec chunk(integer) :: [integer | {integer, integer}]
def chunk(number) do
[]
|> chunk(number)
|> Enum.reverse
end
@spec chunk(list, integer) :: [integer | {integer, integer}]
def chunk(chunks, number) do
cond do
number <= 20 ->
{number, 0}
number < 100 ->
{div(number, 10) * 10, rem(number, 10)}
number < 1000 ->
{div(number, 100), 100, rem(number, 100)}
number < 1_000_000 ->
{div(number, 1000), 1000, rem(number, 1000)}
number < 1_000_000_000 ->
{div(number, 1_000_000), 1_000_000, rem(number, 1_000_000)}
number < 1_000_000_000_000 ->
{div(number, 1_000_000_000), 1_000_000_000, rem(number, 1_000_000_000)}
end
|> case do
{n, 0} ->
[n | chunks]
{n, remainder} when n < 100 and remainder < 10 ->
[{n, remainder}| chunks]
{n, remainder} ->
[n | chunks]
|> chunk(remainder)
{n, base, 0} ->
chunks
|>chunk(n)
|> List.insert_at(0, base)
{n, base, remainder} ->
chunks
|> chunk(n)
|> List.insert_at(0, base)
|> chunk(remainder)
end
end
@spec to_english(integer) :: String.t
def to_english(number) do
case number do
0 -> "zero"
1 -> "one"
2 -> "two"
3 -> "three"
4 -> "four"
5 -> "five"
6 -> "six"
7 -> "seven"
8 -> "eight"
9 -> "nine"
10 -> "ten"
11 -> "eleven"
12 -> "twelve"
13 -> "thirteen"
14 -> "fourteen"
15 -> "fifhteen"
16 -> "sixteen"
17 -> "seventeen"
18 -> "eighteen"
19 -> "nineteen"
20 -> "twenty"
30 -> "thirty"
40 -> "forty"
50 -> "fifty"
60 -> "sixty"
70 -> "seventy"
80 -> "eighty"
90 -> "ninety"
100 -> "hundred"
1_000 -> "thousand"
1_000_000 -> "million"
1_000_000_000 -> "billion"
1_000_000_000_000 -> "trillion"
{n, n2} -> to_english(n) <> "-" <> to_english(n2)
end
end
end
Tags:
construct:and
construct:atom
construct:binary
construct:bitstring
construct:case
construct:cond
construct:decimal
construct:defmodule
construct:doc
construct:double
construct:end
construct:float
construct:floating_point_number
construct:function
construct:function_clause
construct:guard
construct:integer
construct:integral_number
construct:invocation
construct:list
construct:map
construct:multiply
construct:number
construct:or
construct:parameter
construct:pattern_matching
construct:pipe
construct:spec
construct:string
construct:underscore
construct:variable
construct:visibility
paradigm:functional
paradigm:metaprogramming
technique:higher_order_recursion
technique:recursion
from elixir.
Exercise: collatz-conjecture
Code
defmodule CollatzConjecture do
@doc """
calc/1 takes an integer and returns the number of steps required to get the
number to 1 when following the rules:
- if number is odd, multiply with 3 and add 1
- if number is even, divide by 2
"""
@spec calc(number :: pos_integer) :: pos_integer
def calc(input) when is_integer(input) and input > 0 do
do_calc(input, 0)
end
def calc(_), do: raise FunctionClauseError
defp do_calc(1, acc), do: acc
defp do_calc(n, acc) when rem(n, 2) == 0, do: do_calc(div(n, 2), acc + 1)
defp do_calc(n, acc) when rem(n, 2) == 1, do: do_calc(n * 3 + 1, acc + 1)
end
Tags:
construct:add
construct:and
construct:attribute
construct:doc-string
construct:divide
construct:double
construct:elixir-module
construct:floating-point-number
construct:function
construct:function-overloading
construct:invocation
construct:method-overloading
construct:multiply
construct:number
construct:parameter
construct:pattern-matching
construct:raise
construct:spec
construct:underscore
construct:visibility-modifiers
paradigm:functional
paradigm:object-oriented
technique:exceptions
from elixir.
Sorry for the late reply, but I was considering and considering, but in the end I do not have the time to do this task.
from elixir.
This is an automated comment
Hello 👋 Next week we're going to start using the tagging work people are doing on these. If you've already completed the work, thank you! If you've not, but intend to this week, that's great! If you're not going to get round to doing it, and you've not yet posted a comment letting us know, could you please do so, so that we can find other people to do it. Thanks!
from elixir.
Related Issues (20)
- Top Secret could use another test HOT 2
- [Dancing Dots] "defines a __using__ macro" test is flakey in certain scenarios HOT 2
- [log-level] Misused trailing question marks HOT 1
- Set up a dead link checker
- Update all Elixir Exercism repos to use Elixir 1.15 and OTP 26 HOT 8
- Adjust all concepts to use charlist sigils
- test_exercises.sh print fails when test output includes `%`
- Lasagna exercise HOT 2
- Why does this repository have two identical github workflows? HOT 6
- Phone Number exercise: difference between tests in exercise folder vs online Test runner HOT 2
- Implement new practice exercise: bottle-song
- Too forgiving test in exercises/concept/german-sysadmin HOT 1
- Mistakes in `bottle-song` example solution typespecs HOT 3
- [RPG Character Sheet] Misleading the 5th question HOT 4
- Run on Elixir 1.16
- Leap approaches for 48in24
- 48in24 Approaches for Raindrops HOT 2
- Darts exercise hint for sqrt workaround doesn't seem to work HOT 7
- Typo In Take-A-Number Deluxe Step 6 HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from elixir.