First commit
This commit is contained in:
commit
1dba0af064
36 changed files with 1065 additions and 0 deletions
BIN
Elixir.Circle.beam
Normal file
BIN
Elixir.Circle.beam
Normal file
Binary file not shown.
1
README.md
Normal file
1
README.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
# elixir-in-action
|
||||
21
aliases.ex
Normal file
21
aliases.ex
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
defmodule Some.Nested do
|
||||
def print_from_nested do
|
||||
IO.puts("Somehting from nested")
|
||||
end
|
||||
end
|
||||
|
||||
defmodule MyModule do
|
||||
import IO
|
||||
alias IO, as: MyIO
|
||||
alias Some.Nested
|
||||
|
||||
def print do
|
||||
puts("Something")
|
||||
end
|
||||
|
||||
def print_from_alias do
|
||||
MyIO.puts("Something from alias")
|
||||
end
|
||||
|
||||
def print_from_nested, do: Nested.print_from_nested()
|
||||
end
|
||||
21
arity_calc.ex
Normal file
21
arity_calc.ex
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
defmodule CalcOne do
|
||||
def sum(a) do
|
||||
sum(a, 0)
|
||||
end
|
||||
|
||||
def sum(a, b) do
|
||||
a + b
|
||||
end
|
||||
end
|
||||
|
||||
defmodule CalcTwo do
|
||||
def sum(a, b \\ 0) do
|
||||
a + b
|
||||
end
|
||||
end
|
||||
|
||||
defmodule CalcThree do
|
||||
def fun(a, b, c \\ 0, d, e \\ 0) do
|
||||
a + b + c + d + e
|
||||
end
|
||||
end
|
||||
37
chapter3/enum_streams_practice.ex
Normal file
37
chapter3/enum_streams_practice.ex
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
defmodule FileHelper do
|
||||
defp filtered_lines!(path) do
|
||||
File.stream!(path)
|
||||
|> Stream.map(&String.replace(&1, "\n", ""))
|
||||
end
|
||||
|
||||
def line_lengths!(path) do
|
||||
path
|
||||
|> filtered_lines!()
|
||||
|> Enum.map(&String.length/1)
|
||||
end
|
||||
|
||||
def longest_line_length(path) do
|
||||
path
|
||||
|> filtered_lines!()
|
||||
|> Stream.map(&String.length/1)
|
||||
|> Enum.max()
|
||||
end
|
||||
|
||||
def longest_line(path) do
|
||||
path
|
||||
|> filtered_lines!()
|
||||
|> Enum.max_by(&String.length/1)
|
||||
end
|
||||
|
||||
def words_per_line(path) do
|
||||
path
|
||||
|> filtered_lines!()
|
||||
|> Enum.map(&word_count/1)
|
||||
end
|
||||
|
||||
def word_count(string) do
|
||||
string
|
||||
|> String.split()
|
||||
|> length()
|
||||
end
|
||||
end
|
||||
6
chapter3/enum_streams_practice.txt
Normal file
6
chapter3/enum_streams_practice.txt
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
This is a file
|
||||
that contains some words
|
||||
and is mean to be just an example
|
||||
to be used to practice
|
||||
with elixir streams
|
||||
and enums
|
||||
8
chapter3/natural_nums.ex
Normal file
8
chapter3/natural_nums.ex
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
defmodule NaturalNums do
|
||||
def print(1), do: IO.puts(1)
|
||||
|
||||
def print(n) when is_integer(n) and n > 1 do
|
||||
print(n - 1)
|
||||
IO.puts(n)
|
||||
end
|
||||
end
|
||||
21
chapter3/recursion_practice.ex
Normal file
21
chapter3/recursion_practice.ex
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
defmodule ListHelper do
|
||||
def list_len([]), do: 0
|
||||
|
||||
def list_len([_ | tail]) do
|
||||
1 + list_len(tail)
|
||||
end
|
||||
|
||||
def range(num, num), do: [num]
|
||||
|
||||
def range(num1, num2) do
|
||||
[num1 | range(num1 + 1, num2)]
|
||||
end
|
||||
|
||||
def positive([]), do: []
|
||||
|
||||
def positive([head | tail]) when head > 0 do
|
||||
[head | positive(tail)]
|
||||
end
|
||||
|
||||
def positive([_ | tail]), do: positive(tail)
|
||||
end
|
||||
33
chapter3/recursion_practice_tc.ex
Normal file
33
chapter3/recursion_practice_tc.ex
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
defmodule ListHelper do
|
||||
def list_len(list) do
|
||||
list_len_helper(0, list)
|
||||
end
|
||||
|
||||
defp list_len_helper(current_len, []), do: current_len
|
||||
|
||||
defp list_len_helper(current_len, [_ | tail]) do
|
||||
list_len_helper(current_len + 1, tail)
|
||||
end
|
||||
|
||||
def range(num1, num2), do: range_helper([], num1, num2)
|
||||
|
||||
defp range_helper(current_list, num, num), do: [num | current_list]
|
||||
|
||||
defp range_helper(current_list, num1, num2) do
|
||||
range_helper([num2 | current_list], num1, num2 - 1)
|
||||
end
|
||||
|
||||
def positive(list) do
|
||||
Enum.reverse(positive_helper([], list))
|
||||
end
|
||||
|
||||
defp positive_helper(current_list, []), do: current_list
|
||||
|
||||
defp positive_helper(current_list, [head | tail]) when head > 0 do
|
||||
positive_helper([head | current_list], tail)
|
||||
end
|
||||
|
||||
defp positive_helper(current_list, [_ | tail]) do
|
||||
positive_helper(current_list, tail)
|
||||
end
|
||||
end
|
||||
7
chapter3/sum_list.ex
Normal file
7
chapter3/sum_list.ex
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
defmodule ListHelper do
|
||||
def sum([]), do: 0
|
||||
|
||||
def sum([head | tail]) do
|
||||
head + sum(tail)
|
||||
end
|
||||
end
|
||||
16
chapter3/sum_list_tc.ex
Normal file
16
chapter3/sum_list_tc.ex
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
defmodule ListHelper do
|
||||
def sum(list) do
|
||||
do_sum(0, list)
|
||||
end
|
||||
|
||||
defp do_sum(current_sum, []) do
|
||||
current_sum
|
||||
end
|
||||
|
||||
defp do_sum(current_sum, [head | tail]) do
|
||||
# More concise implementation
|
||||
# do_sum(current_sum + head, tail)
|
||||
new_sum = head + current_sum
|
||||
do_sum(new_sum, tail)
|
||||
end
|
||||
end
|
||||
40
chapter3/user_extraction.ex
Normal file
40
chapter3/user_extraction.ex
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
defmodule UserData do
|
||||
defp extract_login(%{"login" => login}), do: {:ok, login}
|
||||
defp extract_login(_), do: {:error, "login missing"}
|
||||
|
||||
defp extract_password(%{"password" => password}), do: {:ok, password}
|
||||
defp extract_password(_), do: {:error, "password mising"}
|
||||
|
||||
defp extract_email(%{"email" => email}), do: {:ok, email}
|
||||
defp extract_email(_), do: {:error, "email missing"}
|
||||
|
||||
def extract_user_case(user) do
|
||||
case extract_login(user) do
|
||||
{:error, reason} ->
|
||||
{:error, reason}
|
||||
|
||||
{:ok, login} ->
|
||||
case extract_email(user) do
|
||||
{:error, reason} ->
|
||||
{:error, reason}
|
||||
|
||||
{:ok, email} ->
|
||||
case extract_password(user) do
|
||||
{:error, reason} ->
|
||||
{:error, reason}
|
||||
|
||||
{:ok, password} ->
|
||||
%{login: login, email: email, password: password}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def extract_user_with(user) do
|
||||
with {:ok, login} <- extract_login(user),
|
||||
{:ok, email} <- extract_email(user),
|
||||
{:ok, password} <- extract_password(user) do
|
||||
{:ok, %{login: login, email: email, password: password}}
|
||||
end
|
||||
end
|
||||
end
|
||||
11
chapter3/user_extraction_2.ex
Normal file
11
chapter3/user_extraction_2.ex
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
defmodule UserExtraction do
|
||||
def extract(user) do
|
||||
case Enum.filter(["login", "email", "password"], &(not Map.has_key?(user, &1))) do
|
||||
[] ->
|
||||
{:ok, %{login: user["login"], email: user["email"], password: user["password"]}}
|
||||
|
||||
missing_fields ->
|
||||
{:error, "missing fields: #{Enum.join(missing_fields, ", ")}"}
|
||||
end
|
||||
end
|
||||
end
|
||||
3
chapter4/files/todos.csv
Normal file
3
chapter4/files/todos.csv
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
2018/12/19,Dentist
|
||||
2018/12/20,Shopping
|
||||
2018/12/19,Movies
|
||||
|
16
chapter4/fraction.ex
Normal file
16
chapter4/fraction.ex
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
defmodule Fraction do
|
||||
defstruct a: nil, b: nil
|
||||
|
||||
def new(a, b), do: %Fraction{a: a, b: b}
|
||||
|
||||
def value(%Fraction{a: a, b: b}) do
|
||||
a / b
|
||||
end
|
||||
|
||||
def add(%Fraction{a: a1, b: b1}, %Fraction{a: a2, b: b2}) do
|
||||
new(
|
||||
a1 * b2 + a2 * b1,
|
||||
b1 * b2
|
||||
)
|
||||
end
|
||||
end
|
||||
16
chapter4/simple_todo.ex
Normal file
16
chapter4/simple_todo.ex
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
defmodule TodoList do
|
||||
def new(), do: %{}
|
||||
|
||||
def add_entry(todo_list, date, title) do
|
||||
Map.update(
|
||||
todo_list,
|
||||
date,
|
||||
[title],
|
||||
&[title | &1]
|
||||
)
|
||||
end
|
||||
|
||||
def entries(todo_list, date) do
|
||||
Map.get(todo_list, date, [])
|
||||
end
|
||||
end
|
||||
59
chapter4/todo_builder.ex
Normal file
59
chapter4/todo_builder.ex
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
defmodule TodoList do
|
||||
defstruct auto_id: 1, entries: %{}
|
||||
|
||||
def new(), do: %TodoList{}
|
||||
|
||||
def new(entries \\ []) do
|
||||
Enum.reduce(
|
||||
entries,
|
||||
%TodoList{},
|
||||
fn entry, todo_list_acc ->
|
||||
add_entry(todo_list_acc, entry)
|
||||
end
|
||||
# Alternative definition
|
||||
# &add_entry(&2, &1)
|
||||
)
|
||||
end
|
||||
|
||||
def add_entry(todo_list, entry) do
|
||||
entry = Map.put(entry, :id, todo_list.auto_id)
|
||||
|
||||
new_entries =
|
||||
Map.put(
|
||||
todo_list.entries,
|
||||
todo_list.auto_id,
|
||||
entry
|
||||
)
|
||||
|
||||
%TodoList{todo_list | entries: new_entries, auto_id: todo_list.auto_id + 1}
|
||||
end
|
||||
|
||||
def entries(todo_list, date) do
|
||||
todo_list.entries
|
||||
|> Stream.filter(fn {_, entry} -> entry.date == date end)
|
||||
|> Enum.map(fn {_, entry} -> entry end)
|
||||
end
|
||||
|
||||
def update_entry(todo_list, %{} = new_entry) do
|
||||
update_entry(todo_list, new_entry.id, fn _ -> new_entry end)
|
||||
end
|
||||
|
||||
def update_entry(todo_list, entry_id, updater_fun) do
|
||||
case Map.fetch(todo_list.entries, entry_id) do
|
||||
:error ->
|
||||
todo_list
|
||||
|
||||
{:ok, old_entry} ->
|
||||
old_entry_id = old_entry.id
|
||||
# Make sure that the result of the updater is a map and the
|
||||
# id remains unchanged.
|
||||
new_entry = %{id: ^old_entry_id} = updater_fun.(old_entry)
|
||||
new_entries = Map.put(todo_list.entries, new_entry.id, new_entry)
|
||||
%TodoList{todo_list | entries: new_entries}
|
||||
end
|
||||
end
|
||||
|
||||
def delete_entry(todo_list, entry_id) do
|
||||
%TodoList{todo_list | entries: Map.delete(todo_list.entries, entry_id)}
|
||||
end
|
||||
end
|
||||
47
chapter4/todo_crud.ex
Normal file
47
chapter4/todo_crud.ex
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
defmodule TodoList do
|
||||
defstruct auto_id: 1, entries: %{}
|
||||
|
||||
def new(), do: %TodoList{}
|
||||
|
||||
def add_entry(todo_list, entry) do
|
||||
entry = Map.put(entry, :id, todo_list.auto_id)
|
||||
|
||||
new_entries =
|
||||
Map.put(
|
||||
todo_list.entries,
|
||||
todo_list.auto_id,
|
||||
entry
|
||||
)
|
||||
|
||||
%TodoList{todo_list | entries: new_entries, auto_id: todo_list.auto_id + 1}
|
||||
end
|
||||
|
||||
def entries(todo_list, date) do
|
||||
todo_list.entries
|
||||
|> Stream.filter(fn {_, entry} -> entry.date == date end)
|
||||
|> Enum.map(fn {_, entry} -> entry end)
|
||||
end
|
||||
|
||||
def update_entry(todo_list, %{} = new_entry) do
|
||||
update_entry(todo_list, new_entry.id, fn _ -> new_entry end)
|
||||
end
|
||||
|
||||
def update_entry(todo_list, entry_id, updater_fun) do
|
||||
case Map.fetch(todo_list.entries, entry_id) do
|
||||
:error ->
|
||||
todo_list
|
||||
|
||||
{:ok, old_entry} ->
|
||||
old_entry_id = old_entry.id
|
||||
# Make sure that the result of the updater is a map and the
|
||||
# id remains unchanged.
|
||||
new_entry = %{id: ^old_entry_id} = updater_fun.(old_entry)
|
||||
new_entries = Map.put(todo_list.entries, new_entry.id, new_entry)
|
||||
%TodoList{todo_list | entries: new_entries}
|
||||
end
|
||||
end
|
||||
|
||||
def delete_entry(todo_list, entry_id) do
|
||||
%TodoList{todo_list | entries: Map.delete(todo_list.entries, entry_id)}
|
||||
end
|
||||
end
|
||||
126
chapter4/todo_import.ex
Normal file
126
chapter4/todo_import.ex
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
defmodule TodoList do
|
||||
defstruct auto_id: 1, entries: %{}
|
||||
|
||||
def new(entries \\ []) do
|
||||
Enum.reduce(
|
||||
entries,
|
||||
%TodoList{},
|
||||
fn entry, todo_list_acc ->
|
||||
add_entry(todo_list_acc, entry)
|
||||
end
|
||||
# Alternative definition
|
||||
# &add_entry(&2, &1)
|
||||
)
|
||||
end
|
||||
|
||||
def add_entry(todo_list, entry) do
|
||||
entry = Map.put(entry, :id, todo_list.auto_id)
|
||||
|
||||
new_entries =
|
||||
Map.put(
|
||||
todo_list.entries,
|
||||
todo_list.auto_id,
|
||||
entry
|
||||
)
|
||||
|
||||
%TodoList{todo_list | entries: new_entries, auto_id: todo_list.auto_id + 1}
|
||||
end
|
||||
|
||||
def entries(todo_list, date) do
|
||||
todo_list.entries
|
||||
|> Stream.filter(fn {_, entry} -> entry.date == date end)
|
||||
|> Enum.map(fn {_, entry} -> entry end)
|
||||
end
|
||||
|
||||
def update_entry(todo_list, %{} = new_entry) do
|
||||
update_entry(todo_list, new_entry.id, fn _ -> new_entry end)
|
||||
end
|
||||
|
||||
def update_entry(todo_list, entry_id, updater_fun) do
|
||||
case Map.fetch(todo_list.entries, entry_id) do
|
||||
:error ->
|
||||
todo_list
|
||||
|
||||
{:ok, old_entry} ->
|
||||
old_entry_id = old_entry.id
|
||||
# Make sure that the result of the updater is a map and the
|
||||
# id remains unchanged.
|
||||
new_entry = %{id: ^old_entry_id} = updater_fun.(old_entry)
|
||||
new_entries = Map.put(todo_list.entries, new_entry.id, new_entry)
|
||||
%TodoList{todo_list | entries: new_entries}
|
||||
end
|
||||
end
|
||||
|
||||
def delete_entry(todo_list, entry_id) do
|
||||
%TodoList{todo_list | entries: Map.delete(todo_list.entries, entry_id)}
|
||||
end
|
||||
end
|
||||
|
||||
defmodule TodoList.CsvImporter do
|
||||
def import(path) do
|
||||
File.stream!(path)
|
||||
|> Stream.map(&String.replace(&1, "\n", ""))
|
||||
|> Stream.map(fn string ->
|
||||
[date, title] = String.split(string, ",")
|
||||
{date, title}
|
||||
end)
|
||||
|> Stream.map(fn {date, title} ->
|
||||
[year, month, day] =
|
||||
date
|
||||
|> String.split("/")
|
||||
|> Enum.map(&String.to_integer(&1))
|
||||
|
||||
{{year, month, day}, title}
|
||||
end)
|
||||
|> Stream.map(fn {{year, month, day}, title} ->
|
||||
{:ok, date} = Date.new(year, month, day)
|
||||
%{date: date, title: title}
|
||||
end)
|
||||
|> TodoList.new()
|
||||
end
|
||||
end
|
||||
|
||||
defmodule TodoList.CsvImporterAlternative do
|
||||
def import(path) do
|
||||
path
|
||||
|> read_lines
|
||||
|> create_entires
|
||||
|> TodoList.new()
|
||||
end
|
||||
|
||||
defp read_lines(path) do
|
||||
path
|
||||
|> File.stream!()
|
||||
|> Stream.map(&String.replace(&1, "\n", ""))
|
||||
end
|
||||
|
||||
defp create_entires(lines) do
|
||||
lines
|
||||
|> Stream.map(&extract_fileds/1)
|
||||
|> Stream.map(&create_entry/1)
|
||||
end
|
||||
|
||||
defp extract_fileds(line) do
|
||||
line
|
||||
|> String.split(",")
|
||||
|> convert_date
|
||||
end
|
||||
|
||||
defp convert_date([date_string, title]) do
|
||||
{parse_date(date_string), title}
|
||||
end
|
||||
|
||||
defp parse_date(date_string) do
|
||||
[year, month, day] =
|
||||
date_string
|
||||
|> String.split("/")
|
||||
|> Enum.map(&String.to_integer/1)
|
||||
|
||||
{:ok, date = Date.new(year, month, day)}
|
||||
date
|
||||
end
|
||||
|
||||
defp create_entry({date, title}) do
|
||||
%{date: date, title: title}
|
||||
end
|
||||
end
|
||||
28
chapter4/todo_map_entry.ex
Normal file
28
chapter4/todo_map_entry.ex
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
defmodule MultiDict do
|
||||
def new(), do: %{}
|
||||
|
||||
def add(dict, key, value) do
|
||||
Map.update(
|
||||
dict,
|
||||
key,
|
||||
[value],
|
||||
&[value | &1]
|
||||
)
|
||||
end
|
||||
|
||||
def get(dict, key) do
|
||||
Map.get(dict, key, [])
|
||||
end
|
||||
end
|
||||
|
||||
defmodule TodoList do
|
||||
def new(), do: MultiDict.new()
|
||||
|
||||
def add_entry(todo_list, entry) do
|
||||
MultiDict.add(todo_list, entry.date, entry)
|
||||
end
|
||||
|
||||
def entries(todo_list, date) do
|
||||
MultiDict.get(todo_list, date)
|
||||
end
|
||||
end
|
||||
28
chapter4/todo_multi_dict.ex
Normal file
28
chapter4/todo_multi_dict.ex
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
defmodule MultiDict do
|
||||
def new(), do: %{}
|
||||
|
||||
def add(dict, key, value) do
|
||||
Map.update(
|
||||
dict,
|
||||
key,
|
||||
[value],
|
||||
&[value | &1]
|
||||
)
|
||||
end
|
||||
|
||||
def get(dict, key) do
|
||||
Map.get(dict, key, [])
|
||||
end
|
||||
end
|
||||
|
||||
defmodule TodoList do
|
||||
def new(), do: MultiDict.new()
|
||||
|
||||
def add_entry(todo_list, date, title) do
|
||||
MultiDict.add(todo_list, date, title)
|
||||
end
|
||||
|
||||
def entries(todo_list, date) do
|
||||
MultiDict.get(todo_list, date)
|
||||
end
|
||||
end
|
||||
46
chapter5/calculator.ex
Normal file
46
chapter5/calculator.ex
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
defmodule Calculator do
|
||||
def start do
|
||||
spawn(fn -> loop(0) end)
|
||||
end
|
||||
|
||||
defp loop(current_value) do
|
||||
new_value =
|
||||
receive do
|
||||
{:value, caller} ->
|
||||
send(caller, {:response, current_value})
|
||||
current_value
|
||||
|
||||
{:add, value} ->
|
||||
current_value + value
|
||||
|
||||
{:sub, value} ->
|
||||
current_value - value
|
||||
|
||||
{:mul, value} ->
|
||||
current_value * mul
|
||||
|
||||
{:div, value} ->
|
||||
current_value / value
|
||||
|
||||
invalid_request ->
|
||||
IO.puts("invalid request #{inspect(invalid_request)}")
|
||||
current_value
|
||||
end
|
||||
|
||||
loop(new_value)
|
||||
end
|
||||
|
||||
def value(server_pid) do
|
||||
send(server_pid, {:value, self()})
|
||||
|
||||
receive do
|
||||
{:response, value} ->
|
||||
value
|
||||
end
|
||||
end
|
||||
|
||||
def add(server_pid, value), do: send(server_pid, {:add, value})
|
||||
def sub(server_pid, value), do: send(server_pid, {:sub, value})
|
||||
def mul(server_pid, value), do: send(server_pid, {:mul, value})
|
||||
def div(server_pid, value), do: send(server_pid, {:div, value})
|
||||
end
|
||||
35
chapter5/calculator_refactor.ex
Normal file
35
chapter5/calculator_refactor.ex
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
defmodule Calculator do
|
||||
def start do
|
||||
spawn(fn -> loop(0) end)
|
||||
end
|
||||
|
||||
defp loop(current_value) do
|
||||
new_value =
|
||||
receive do
|
||||
message -> process_message(current_value, message)
|
||||
end
|
||||
|
||||
loop(new_value)
|
||||
end
|
||||
|
||||
defp process_message(current_value, {:value, caller}) do
|
||||
send(caller, {:response, current_value})
|
||||
current_value
|
||||
end
|
||||
|
||||
defp process_message(current_value, {:add, value}) do
|
||||
current_value + value
|
||||
end
|
||||
|
||||
defp process_message(current_value, {:sub, value}) do
|
||||
current_value - value
|
||||
end
|
||||
|
||||
defp process_message(current_value, {:mul, value}) do
|
||||
current_value * value
|
||||
end
|
||||
|
||||
defp process_message(current_value, {:div, value}) do
|
||||
current_value / value
|
||||
end
|
||||
end
|
||||
31
chapter5/database_server.ex
Normal file
31
chapter5/database_server.ex
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
defmodule DatabaseServer do
|
||||
def start do
|
||||
spawn(&loop/0)
|
||||
end
|
||||
|
||||
defp loop do
|
||||
receive do
|
||||
{:run_query, caller, query_def} ->
|
||||
send(caller, {:query_result, run_query(query_def)})
|
||||
end
|
||||
|
||||
loop()
|
||||
end
|
||||
|
||||
defp run_query(query_def) do
|
||||
Process.sleep(2000)
|
||||
"#{query_def} result"
|
||||
end
|
||||
|
||||
def run_async(server_pid, query_def) do
|
||||
send(server_pid, {:run_query, self(), query_def})
|
||||
end
|
||||
|
||||
def get_result do
|
||||
receive do
|
||||
{:query_result, result} -> result
|
||||
after
|
||||
5000 -> {:error, :timeout}
|
||||
end
|
||||
end
|
||||
end
|
||||
23
chapter5/process_bottleneck.ex
Normal file
23
chapter5/process_bottleneck.ex
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
defmodule Server do
|
||||
def start do
|
||||
spawn(fn -> loop() end)
|
||||
end
|
||||
|
||||
def send_msg(server, message) do
|
||||
send(server, {self(), message})
|
||||
|
||||
receive do
|
||||
{:response, response} -> response
|
||||
end
|
||||
end
|
||||
|
||||
def loop do
|
||||
receive do
|
||||
{caller, message} ->
|
||||
Process.sleep(1000)
|
||||
send(caller, {:response, message})
|
||||
end
|
||||
|
||||
loop()
|
||||
end
|
||||
end
|
||||
110
chapter5/registered_todo_server.ex
Normal file
110
chapter5/registered_todo_server.ex
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
defmodule TodoServer do
|
||||
def start do
|
||||
process_pid = spawn(fn -> loop(TodoList.new()) end)
|
||||
Process.register(process_pid, :todo_server)
|
||||
end
|
||||
|
||||
# Alternative for the start function
|
||||
# def start do
|
||||
# spawn(fn ->
|
||||
# Process.register(self(), :todo_server)
|
||||
# loop(TodoList.new())
|
||||
# end)
|
||||
# end
|
||||
|
||||
def add_entry(new_entry) do
|
||||
send(:todo_server, {:add_entry, new_entry})
|
||||
end
|
||||
|
||||
def entries(date) do
|
||||
send(:todo_server, {:entries, self(), date})
|
||||
|
||||
receive do
|
||||
{:todo_etries, entries} -> entries
|
||||
after
|
||||
5000 -> {:error, :timeout}
|
||||
end
|
||||
end
|
||||
|
||||
def update_entry(entry_id, updater_fun) do
|
||||
send(:todo_server, {:update_entry, entry_id, updater_fun})
|
||||
end
|
||||
|
||||
def delete_entry(entry_id) do
|
||||
send(:todo_server, {:delete_entry, entry_id})
|
||||
end
|
||||
|
||||
defp loop(todo_list) do
|
||||
new_todo_list =
|
||||
receive do
|
||||
message -> process_message(todo_list, message)
|
||||
end
|
||||
|
||||
loop(new_todo_list)
|
||||
end
|
||||
|
||||
defp process_message(todo_list, {:add_entry, new_entry}) do
|
||||
TodoList.add_entry(todo_list, new_entry)
|
||||
end
|
||||
|
||||
defp process_message(todo_list, {:entries, caller, date}) do
|
||||
send(caller, {:todo_etries, TodoList.entries(todo_list, date)})
|
||||
todo_list
|
||||
end
|
||||
|
||||
defp process_message(todo_list, {:update_entry, entry_id, updater_fun}) do
|
||||
TodoList.update_entry(todo_list, entry_id, updater_fun)
|
||||
end
|
||||
|
||||
defp process_message(todo_list, {:delete_entry, entry_id}) do
|
||||
TodoList.delete_entry(todo_list, entry_id)
|
||||
end
|
||||
end
|
||||
|
||||
defmodule TodoList do
|
||||
defstruct auto_id: 1, entries: %{}
|
||||
|
||||
def new(), do: %TodoList{}
|
||||
|
||||
def add_entry(todo_list, entry) do
|
||||
entry = Map.put(entry, :id, todo_list.auto_id)
|
||||
|
||||
new_entries =
|
||||
Map.put(
|
||||
todo_list.entries,
|
||||
todo_list.auto_id,
|
||||
entry
|
||||
)
|
||||
|
||||
%TodoList{todo_list | entries: new_entries, auto_id: todo_list.auto_id + 1}
|
||||
end
|
||||
|
||||
def entries(todo_list, date) do
|
||||
todo_list.entries
|
||||
|> Stream.filter(fn {_, entry} -> entry.date == date end)
|
||||
|> Enum.map(fn {_, entry} -> entry end)
|
||||
end
|
||||
|
||||
def update_entry(todo_list, %{} = new_entry) do
|
||||
update_entry(todo_list, new_entry.id, fn _ -> new_entry end)
|
||||
end
|
||||
|
||||
def update_entry(todo_list, entry_id, updater_fun) do
|
||||
case Map.fetch(todo_list.entries, entry_id) do
|
||||
:error ->
|
||||
todo_list
|
||||
|
||||
{:ok, old_entry} ->
|
||||
old_entry_id = old_entry.id
|
||||
# Make sure that the result of the updater is a map and the
|
||||
# id remains unchanged.
|
||||
new_entry = %{id: ^old_entry_id} = updater_fun.(old_entry)
|
||||
new_entries = Map.put(todo_list.entries, new_entry.id, new_entry)
|
||||
%TodoList{todo_list | entries: new_entries}
|
||||
end
|
||||
end
|
||||
|
||||
def delete_entry(todo_list, entry_id) do
|
||||
%TodoList{todo_list | entries: Map.delete(todo_list.entries, entry_id)}
|
||||
end
|
||||
end
|
||||
34
chapter5/stateful_database_server.ex
Normal file
34
chapter5/stateful_database_server.ex
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
defmodule DatabaseServer do
|
||||
def start do
|
||||
spawn(fn ->
|
||||
connection = :rand.uniform(1000)
|
||||
loop(connection)
|
||||
end)
|
||||
end
|
||||
|
||||
defp loop(connection) do
|
||||
receive do
|
||||
{:run_query, caller, query_def} ->
|
||||
send(caller, {:query_result, run_query(connection, query_def)})
|
||||
end
|
||||
|
||||
loop(connection)
|
||||
end
|
||||
|
||||
defp run_query(connection, query_def) do
|
||||
Process.sleep(2000)
|
||||
"Connection #{connection}: #{query_def} result"
|
||||
end
|
||||
|
||||
def run_async(server_pid, query_def) do
|
||||
send(server_pid, {:run_query, self(), query_def})
|
||||
end
|
||||
|
||||
def get_result do
|
||||
receive do
|
||||
{:query_result, result} -> result
|
||||
after
|
||||
5000 -> {:error, :timeout}
|
||||
end
|
||||
end
|
||||
end
|
||||
101
chapter5/todo_server.ex
Normal file
101
chapter5/todo_server.ex
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
defmodule TodoServer do
|
||||
def start do
|
||||
spawn(fn -> loop(TodoList.new()) end)
|
||||
end
|
||||
|
||||
def add_entry(todo_server, new_entry) do
|
||||
send(todo_server, {:add_entry, new_entry})
|
||||
end
|
||||
|
||||
def entries(todo_server, date) do
|
||||
send(todo_server, {:entries, self(), date})
|
||||
|
||||
receive do
|
||||
{:todo_etries, entries} -> entries
|
||||
after
|
||||
5000 -> {:error, :timeout}
|
||||
end
|
||||
end
|
||||
|
||||
def update_entry(todo_server, entry_id, updater_fun) do
|
||||
send(todo_server, {:update_entry, entry_id, updater_fun})
|
||||
end
|
||||
|
||||
def delete_entry(todo_server, entry_id) do
|
||||
send(todo_server, {:delete_entry, entry_id})
|
||||
end
|
||||
|
||||
defp loop(todo_list) do
|
||||
new_todo_list =
|
||||
receive do
|
||||
message -> process_message(todo_list, message)
|
||||
end
|
||||
|
||||
loop(new_todo_list)
|
||||
end
|
||||
|
||||
defp process_message(todo_list, {:add_entry, new_entry}) do
|
||||
TodoList.add_entry(todo_list, new_entry)
|
||||
end
|
||||
|
||||
defp process_message(todo_list, {:entries, caller, date}) do
|
||||
send(caller, {:todo_etries, TodoList.entries(todo_list, date)})
|
||||
todo_list
|
||||
end
|
||||
|
||||
defp process_message(todo_list, {:update_entry, entry_id, updater_fun}) do
|
||||
TodoList.update_entry(todo_list, entry_id, updater_fun)
|
||||
end
|
||||
|
||||
defp process_message(todo_list, {:delete_entry, entry_id}) do
|
||||
TodoList.delete_entry(todo_list, entry_id)
|
||||
end
|
||||
end
|
||||
|
||||
defmodule TodoList do
|
||||
defstruct auto_id: 1, entries: %{}
|
||||
|
||||
def new(), do: %TodoList{}
|
||||
|
||||
def add_entry(todo_list, entry) do
|
||||
entry = Map.put(entry, :id, todo_list.auto_id)
|
||||
|
||||
new_entries =
|
||||
Map.put(
|
||||
todo_list.entries,
|
||||
todo_list.auto_id,
|
||||
entry
|
||||
)
|
||||
|
||||
%TodoList{todo_list | entries: new_entries, auto_id: todo_list.auto_id + 1}
|
||||
end
|
||||
|
||||
def entries(todo_list, date) do
|
||||
todo_list.entries
|
||||
|> Stream.filter(fn {_, entry} -> entry.date == date end)
|
||||
|> Enum.map(fn {_, entry} -> entry end)
|
||||
end
|
||||
|
||||
def update_entry(todo_list, %{} = new_entry) do
|
||||
update_entry(todo_list, new_entry.id, fn _ -> new_entry end)
|
||||
end
|
||||
|
||||
def update_entry(todo_list, entry_id, updater_fun) do
|
||||
case Map.fetch(todo_list.entries, entry_id) do
|
||||
:error ->
|
||||
todo_list
|
||||
|
||||
{:ok, old_entry} ->
|
||||
old_entry_id = old_entry.id
|
||||
# Make sure that the result of the updater is a map and the
|
||||
# id remains unchanged.
|
||||
new_entry = %{id: ^old_entry_id} = updater_fun.(old_entry)
|
||||
new_entries = Map.put(todo_list.entries, new_entry.id, new_entry)
|
||||
%TodoList{todo_list | entries: new_entries}
|
||||
end
|
||||
end
|
||||
|
||||
def delete_entry(todo_list, entry_id) do
|
||||
%TodoList{todo_list | entries: Map.delete(todo_list.entries, entry_id)}
|
||||
end
|
||||
end
|
||||
55
chapter6/server_process.ex
Normal file
55
chapter6/server_process.ex
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
defmodule ServerProcess do
|
||||
def start(callback_module) do
|
||||
spawn(fn ->
|
||||
initial_state = callback_module.init()
|
||||
loop(callback_module, initial_state)
|
||||
end)
|
||||
end
|
||||
|
||||
def loop(callback_module, current_state) do
|
||||
receive do
|
||||
{request, caller} ->
|
||||
{response, new_state} =
|
||||
callback_module.handle_call(
|
||||
request,
|
||||
current_state
|
||||
)
|
||||
|
||||
send(caller, {:response, response})
|
||||
loop(callback_module, new_state)
|
||||
end
|
||||
end
|
||||
|
||||
def call(server_pid, request) do
|
||||
send(server_pid, {request, self()})
|
||||
|
||||
receive do
|
||||
{:response, response} ->
|
||||
response
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defmodule KeyValueStore do
|
||||
def init, do: %{}
|
||||
|
||||
def start do
|
||||
ServerProcess.start(KeyValueStore)
|
||||
end
|
||||
|
||||
def put(pid, key, value) do
|
||||
ServerProcess.call(pid, {:put, key, value})
|
||||
end
|
||||
|
||||
def get(pid, key) do
|
||||
ServerProcess.call(pid, {:get, key})
|
||||
end
|
||||
|
||||
def handle_call({:put, key, value}, state) do
|
||||
{:ok, Map.put(state, key, value)}
|
||||
end
|
||||
|
||||
def handle_call({:get, key}, state) do
|
||||
{Map.get(state, key), state}
|
||||
end
|
||||
end
|
||||
12
circle.ex
Normal file
12
circle.ex
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
defmodule Circle do
|
||||
@moduledoc "Implements basic circle functions"
|
||||
@pi 3.14159
|
||||
|
||||
@doc "Computes the area of a circle"
|
||||
@spec area(number) :: number
|
||||
def area(r), do: r * r * @pi
|
||||
|
||||
@doc "Computes the circumferenc of a circle"
|
||||
@spec circumference(number) :: number
|
||||
def circumference(r), do: 2 * r * @pi
|
||||
end
|
||||
30
geometry.ex
Normal file
30
geometry.ex
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
defmodule Geometry do
|
||||
def rectangle_area(a, b) do
|
||||
a * b
|
||||
end
|
||||
|
||||
def rectangle_area_condensed(a, b), do: a * b
|
||||
end
|
||||
|
||||
defmodule Rectangle do
|
||||
def area(a, b), do: a * b
|
||||
def area(a), do: area(a, a)
|
||||
end
|
||||
|
||||
defmodule GeometryMultiClause do
|
||||
def area({:rectangle, a, b}) do
|
||||
a * b
|
||||
end
|
||||
|
||||
def area({:square, a}) do
|
||||
a * a
|
||||
end
|
||||
|
||||
def area({:circle, r}) do
|
||||
r * r * 3.14159
|
||||
end
|
||||
|
||||
def area(unknown) do
|
||||
{:error, {:unkown_shape, unknown}}
|
||||
end
|
||||
end
|
||||
9
private_fun.ex
Normal file
9
private_fun.ex
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
defmodule TestPrivate do
|
||||
def double(a) do
|
||||
sum(a, a)
|
||||
end
|
||||
|
||||
defp sum(a, b) do
|
||||
a + b
|
||||
end
|
||||
end
|
||||
5
rect.ex
Normal file
5
rect.ex
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
defmodule Rectangle do
|
||||
def area({a, b}) do
|
||||
a * b
|
||||
end
|
||||
end
|
||||
7
script.exs
Normal file
7
script.exs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
defmodule MyModule do
|
||||
def run do
|
||||
IO.puts("Called MyModule run!")
|
||||
end
|
||||
end
|
||||
|
||||
MyModule.run()
|
||||
11
test_num.ex
Normal file
11
test_num.ex
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
defmodule TestNum do
|
||||
def test(x) when x < 0 do
|
||||
:negative
|
||||
end
|
||||
|
||||
def test(0), do: :zero
|
||||
|
||||
def test(x) when x > 0 do
|
||||
:positive
|
||||
end
|
||||
end
|
||||
11
test_num2.ex
Normal file
11
test_num2.ex
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
defmodule TestNum do
|
||||
def test(x) when is_number(x) and x < 0 do
|
||||
:negative
|
||||
end
|
||||
|
||||
def test(0), do: :zero
|
||||
|
||||
def test(x) when is_number(x) and x > 0 do
|
||||
:positive
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue