|
|
defmodule Fibonacci do
def of(0), do: 0
def of(1), do: 1
def of(n), do: of(n-2) + of(n-1)
end
defmodule Fibonacci do
def of(n) when n < 2, do: n
def of(n), do: of(n-2) + of(n-1)
end
defmodule Fibonacci do
def of(n), do: fib_of(n, 0, 1)
defp fib_of(0, curr_fib, _), do: curr_fib
defp fib_of(limit, curr_fib, next_fib) do
fib_of(limit-1, next_fib, curr_fib + next_fib)
end
end
defmodule Factorial do
def of(n), do: facto_of(n, 1)
defp facto_of(0, acc), do: acc
defp facto_of(1, acc), do: acc
defp facto_of(n, acc), do: facto_of(n-1, acc*n)
end
defmodule Sum do
def of(list), do: sum_of(list, 0)
defp sum_of([], acc), do: acc
defp sum_of([x|xs], acc), do: sum_of(xs, x+acc)
end
const squared = (x) => Math.pow(x, 2);
squared(4); //=> 16
squared = fn x -> :math.pow(x, 2) end
squared.(4) #=> 16.0
const squared = _.partial(Math.pow, _, 2);
squared(4); //=> 16
squared = &:math.pow(&1, 2)
squared.(4) #=> 16.0
const pow = Math.pow;
pow(4, 2); //=> 16
pow = &:math.pow/2
pow.(4, 2) #=> 16.0
$$ 19 \rightarrow 1^2 + 9^2 = 82 \\ 82 \rightarrow 8^2 + 2^2 = 68 \\ 68 \rightarrow 6^2 + 8^2 = 100 \\ 100 \rightarrow 1^2 + 0^2 + 0^2 = 1 \\ $$
defmodule Happy do
def is_happy(n) do
do_is_happy(n, [])
end
defp do_is_happy(1, _), do: true
defp do_is_happy(n, past), do: ???
defp do_is_happy(n, past) do
if n in past do
false
else
n
|> digits
|> Enum.reduce(0, &sum_of_squares/2)
|> do_is_happy([n|past])
end
end
defp digits(num) do
num
|> to_string
|> String.split("", trim: true)
|> Enum.map(&Integer.parse/1)
end
defp sum_of_squares({digit, _}, acc) do
(:math.pow(digit, 2) |> trunc) + acc
end
iex> for x <- 1..5, y <- 1..5, x < y, do: {x,y}
[{1, 2}, {1, 3}, {1, 4}, {1, 5}, {2, 3},
{2, 4}, {2, 5}, {3, 4}, {3, 5}, {4, 5}]
defmodule PrimeNumbers do
def upto(n) do
sieve = :math.sqrt(n) |> trunc
non_primes = (for i <- 2..sieve do
multiples_of(i, i*i, n, [])
end) |> List.flatten
for i <- 1..n, !(i in non_primes), do: i
end
defp multiples_of(_, curr, limit, acc)
when curr > limit, do: acc
defp multiples_of(multiple, curr, limit, acc) do
multiples_of(multiple, curr+multiple, limit, [curr|acc])
end
“An actor is an independent process that shares nothing with any other process.
You can spawn new processes, send them messages, and receive messages back.”
Programming Elixir
defmodule Greeter do
def start do
receive do
{sender, {:greet, name}} ->
send sender, {:ok, "Hello, #{name}!"}
start
end
end
end
pid = spawn(Greeter, :start, [])
send pid, {self, {:greet, "World"}}
receive do
{:ok, greetings} ->
IO.puts greetings #=> "Hello, World!"
after 5000 ->
IO.puts "timed out"
end
“A GenServer is a process that can be used to keep state, execute code asynchronously and so on.”
Elixir documentation
“The advantage of using a generic server process is that it will include functionality for tracing and error reporting and will also fit into a supervision tree.”
Elixir documentation
defmodule Greeter do
use GenServer
def init(greeting) do
{:ok, %{greeting: greeting}}
end
def handle_call({:greet, name}, _sender, state) do
{:reply, "#{state.greeting}, #{name}!", state}
end
end
{:ok, pid} = GenServer.start_link(Greeter, "Hello")
GenServer.call(pid, {:greet, "World"}) #=> "Hello, World!"
defmodule Greeter do
use GenServer
# client api...
def start_link(greeting) do
GenServer.start_link(__MODULE__, greeting)
end
def greet(pid, name) do
GenServer.call(pid, {:greet, name})
end
# server implementation...
“A GenEvent handler is a process that subscribes to events emitted by a GenEvent manager.”
defmodule SearchIndexer do
use GenEvent
def handle_event({:new_entry, entry}, state) do
# actually do indexing...
{:ok, state}
end
end
{:ok, pid} = GenEvent.start_link
GenEvent.add_handler(pid, SearchIndexer, [])
GenEvent.notify(pid, {:new_entry, %{id: 1}})
# GenEvent.ack_notify...
# GenEvent.sync_notify...
“An elixir task is a function that runs in the background.”
Programming Elixir
worker = Task.async(Mod, :expensive_call, [])
# do other things...
result = Task.await(worker)
(for i <- 1..10_000, into: [], do: i)
|> Enum.map(&Task.async(Mod, :expensive_call, [&1]))
|> Enum.map(&Task.await/1)
“An agent is a background process that maintains state.”
Programming Elixir
Say.it("Bonjour")
|> Say.it("le")
|> Say.it("monde!")
|> Say.it()
"Bonjour le monde!"
def it(term) when is_binary(term) do
start_link |> it(term)
end
defp start_link do
{:ok, agent_pid} = Agent.start_link fn -> [] end
agent_pid
end
def it(agent_pid, word) do
Agent.update(agent_pid, &([word|&1]))
agent_pid
end
def it(term) when is_pid(term) do
sentence = Agent.get(term, &(&1))
|> Enum.reverse
|> Enum.join(" ")
Agent.stop(term)
sentence
end
“A supervisor is a process that is given a list of processes to monitor and is told what to do if a process dies, and how to prevent restart loops.”
Programming Elixir
“Supervisors are the heart of reliability”
Programming Elixir
defmodule GreeterSupervisor do
use Supervisor
def start_link do
Supervisor.start_link(
__MODULE__, [], name: __MODULE__)
end
defmodule GreeterSupervisor do
use Supervisor
# def start_link...
def init(_args) do
children = [ worker(Greeter, ["Hello"]) ]
supervise(children, strategy: :one_for_one)
end
end
Strategy | Description |
---|---|
:one_for_one |
When a process crashes, restart it. |
:one_for_all |
When one process crashes, restart all of them. |
:rest_for_all |
When one crashes, restart it and all others that depend on it. |
:simple_one_for_one |
Like :one_for_one but only when dynamically adding children. |
“Nodes are the basis of distribution”
Programming Elixir
$ iex --sname one greeter.exs
iex(one@myhost)1> GenServer.start_link( ...(one@myhost)1> Greeter, ...(one@myhost)1> "Hello", ...(one@myhost)1> name: {:global, :greeter})
$ iex --sname two
iex(two@myhost)1> Node.connect :"one@myhost" true iex(two@myhost)2> Node.list [:one@myhost] iex(two@myhost)3> GenServer.call( ...(two@myhost)3> {:global, :greeter}, ...(two@myhost)3> {:greet, "World"}) "Hello, World!"
$ iex --sname one
iex(one@myhost)1> Node.connect(:"two@myhost") iex(one@myhost)2> Node.spawn(:"two@myhost", ...(one@myhost)2> GenServer, :start_link, ...(one@myhost)2> [Greeter, "Hello", ...(one@myhost)2> [name: :hello]])
$ iex --sname two greeter.exs
iex(two@myhost)1> GenServer.call(:hello, ...(two@myhost)1> {:greet, "World"}) "Hello, World!"
“The first law of metaprogramming is: you do not use metaprogramming when plainprogramming will suffice.”
<html>
<head>
<script>
var re = /^<%= someExpression() %>\$/;
</script>
</head>
</html>
param = "world"
"Hello, #{param[0].toUpperCase() + param[1..-1]}!"
#=> "Hello, World!"
(+ 1 2) ;=> 3
'(+ 1 2) ;=> (+ 1 2)
`(+ 1 ,(+ 2 2)) ;=> (+ 1 4)
(let ((param 2))
`(+ 1 ,(expt param 3))) ;=> (+ 1 8)
iex(1)> 1 + 2
3
iex(2)> quote do 1 + 2 end
{:+, [...], [1, 2]}
param = 2
a_quoted_expr = quote do
1 + unquote(:math.pow(param, 3))
end
#=> {:+, [...], [1, 8.0]}
Macro.to_string(a_quoted_expr)
#=> "1 + 8.0"
{ id: 'name', type: 'string' }
{ id: 'age', type: 'integer', minimum: '18' }
{ id: 'contact', type: 'object',
properties: {
name: { '$ref': 'name' },
age: { '$ref': 'age' }
}
}
def valid?(:age, candidate)
when is_integer(candidate)
and candidate >= 18, do: true
def valid?(:contact, %{"name"=>n, "age"=>a}) do
valid?(:name, n) and valid?(:age, a)
end
defmodule ContactSchema do
use JSONSchema
schema id: :name, type: :string
schema id: :age, type: :integer, minimum: 18
schema id: :contact, type: :object, properties:
%{"age" => :age, "name" => :name}
end
defmodule JSONSchema do
defmacro schema(opts) do
do_schema(opts[:type], opts)
end
def do_schema(:string, opts) do ... end
def do_schema(:integer, opts) do ... end
def do_schema(:object, opts) do ... end
end
def do_schema(:string, opts) do
quote do
def valid?(unquote(opts[:id]), candidate)
when is_binary(candidate), do: true
end
end
def do_schema(:object, opts) do
quote do
def valid?(unquote(opts[:id]), candidate) do
props = unquote(opts[:properties])
Enum.all? props, fn {prop_name, sch_id} ->
valid?(sch_id, candidate[prop_name])
end
end
end
end
defmodule ContactSchema use JSONSchema end
defmodule JSONSchema do defmacro __using__(opts) do quote do import unquote(__MODULE__) end end end