On Github jfeaver / talk-try-elixir
defmodule FizzBuzz do
def fizz_word(0, 0, _), do: "FizzBuzz"
def fizz_word(0, _, _), do: "Fizz"
def fizz_word(_, 0, _), do: "Buzz"
def fizz_word(_, _, n), do: n
def fizzbuzz(n) do
fizz_word( rem(n,3), rem(n,5), n )
end
def result(n) do
Enum.map(1..n, fn(n) ->
fizzbuzz(n)
end)
end
def print(n) do
result(n)
|> Enum.join(", ")
|> IO.puts
end
end
FizzBuzz.print(100)
Pattern Matching | Pipe Operator
Functional Bits: pattern matching, pipe operator, recursion features
Erlang was originally developed in 1986 by Ericsson for telephony applications to support highly concurrent systems
Elixir doesn't have built-in random or time libraries (although there are multiple packages). Let's use Erlang's libraries:
defmodule Random do
def uniform do
:random.uniform
end
def seed do
:random.seed(:erlang.now)
end
end
defmacro match?(left, right) do
quote do
case unquote(right) do
unquote(left) ->
true
_ ->
false
end
end
end
iex> list = [{:a,1},{:b,2},{:a,3}]
[a: 1, b: 2, a: 3]
iex> Enum.filter list, fn (thing) -> match?({:a, _}, thing) end
[a: 1, a: 3]
Taken from: Elixir: It's Not About Syntax
Built from the beginning with a focus on metaprogramming using macrosdefmodule Math do
def sum_list([head|tail], accumulator) do
sum_list(tail, head + accumulator)
end
def sum_list([], accumulator) do
accumulator
end
end
Math.sum_list([1, 2, 3], 0) #=> 6
Taken from: Elixir Getting Started: Recursion
# Compile the module and create a process that evaluates `area_loop` in the shell
pid = spawn(fn -> Geometry.area_loop() end) #=> #PID<0.40.0>
# Send a message to `pid` that will match a pattern in the receive statement
send pid, {:rectangle, 2, 3}
#=> Area = 6
# {:rectangle,2,3}
send pid, {:circle, 2}
#=> Area = 12.56000000000000049738
# {:circle,2}
# The shell is also a process, you can use `self` to get the current pid
self() #=> #PID<0.27.0>
Taken from: Learn X in Y Minutes - Elixir
# Elixir relies on the actor model for concurrency. All we need to write
# concurrent programs in elixir are three primitives: spawning processes,
# sending messages and receiving messages.
iex> a = [ 1,2,3 ] iex> b = [[ 1,2,3 ]] iex> [c] = [[ 1,2,3 ]] iex> a == c true iex> [ x,y,z ] = [ 1,2,3 ] iex> z 3 iex> "Today is " <> date = "Today is December 16th" iex> date "December 16th"
defmodule MyApp do
def callback(:ok, data) do
# success
end
def callback(:error, data) do
# handle failure
end
end
Back To FizzBuzz
pervasive
You're not assigning 1 to a, you're challenging elixir to find a way to make that true
The pipe operator(|>) is most helpful when transforming data through a sequence. The operator pipes the result of an expression into the first argument of the next function. Confusing, nested function calls no longer need temporary variables to make them readable:
# Confusing, nested function calls
def print(n) do
IO.puts( Enum.join( result(n), ", " ) )
end
# versus using the pipe operator
def print(n) do
result(n)
|> Enum.join(", ")
|> IO.puts
end
Back To FizzBuzz
it 'releases one invite credit when it is withdrawn' do
sender = @invite.sender
expect{ @invite.withdraw; sender.reload }.
to change{ sender.available_invite_credits.length }.
by(1)
end
it 'releases one invite credit when it is withdrawn' do
sender = @invite.sender
expect{ @invite.withdraw }.
to change{ User.available_invite_credits(sender.id).length }.
by(1)
end
# find the author counts
author_counts = []
author_counts_index = -1
current_author = ''
for i in 0...commits.length
commit = commits[i]
if commit.author == current_author
author_counts[author_counts_index][1] += 1
else
current_author = commit.author
author_counts << [commit.author, 1]
author_counts_index += 1
end
end
commits.sort_by do |commit|
commit.author
end.
chunk do |commit|
commit.author
end.
each do |author, author_commits|
puts "#{author} has #{author_commits.count} commits"
end
My approaches to some Ruby problems have probably changed in subtle ways. Learning Elixir has been fun and will help me learn other languages (and their strengths) faster. I'm probably also better prepared to use Proc objects, the Proc#curry method, and Enumerator::Lazy objects but directly practicing them would be even better (perhaps with katas).