Closed ErikSchierboom closed 12 months ago
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
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?
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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!
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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.
This is an automated comment
Hello :wave: 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!
Hello lovely maintainers :wave:
We've recently added "tags" to student's solutions. These express the constructs, paradigms and techniques that a solution uses. We are going to be using these tags for lots of things including filtering, pointing a student to alternative approaches, and much more.
In order to do this, we've built out a full AST-based tagger in C#, which has allowed us to do things like detect recursion or bit shifting. We've set things up so other tracks can do the same for their languages, but its a lot of work, and we've determined that actually it may be unnecessary. Instead we think that we can use machine learning to achieve tagging with good enough results. We've fine-tuned a model that can determine the correct tags for C# from the examples with a high success rate. It's also doing reasonably well in an untrained state for other languages. We think that with only a few examples per language, we can potentially get some quite good results, and that we can then refine things further as we go.
I released a new video on the Insiders page that talks through this in more detail.
We're going to be adding a fully-fledged UI in the coming weeks that allow maintainers and mentors to tag solutions and create training sets for the neural networks, but to start with, we're hoping you would be willing to manually tag 20 solutions for this track. In this post we'll add 20 comments, each with a student's solution, and the tags our model has generated. Your mission (should you choose to accept it) is to edit the tags on each issue, removing any incorrect ones, and add any that are missing. In order to build one model that performs well across languages, it's best if you stick as closely as possible to the C# tags as you can. Those are listed here. If you want to add extra tags, that's totally fine, but please don't arbitrarily reword existing tags, even if you don't like what Erik's chosen, as it'll just make it less likely that your language gets the correct tags assigned by the neural network.
To summarise - there are two paths forward for this issue:
If you tell us you're not able/wanting to help or there's no comment added, we'll automatically crowd-source this in a week or so.
Finally, if you have questions or want to discuss things, it would be best done on the forum, so the knowledge can be shared across all maintainers in all tracks.
Thanks for your help! :blue_heart:
Note: Meta discussion on the forum