├── code ├── stats │ ├── test │ │ ├── test_helper.exs │ │ ├── stats_test.exs │ │ └── central_tendency_test.exs │ ├── .formatter.exs │ ├── functional_elixir_octallium.png │ ├── lib │ │ ├── errors.ex │ │ ├── validators.ex │ │ ├── variability │ │ │ ├── standard_deviation.ex │ │ │ └── variance.ex │ │ ├── stats.ex │ │ └── central_tendency │ │ │ ├── mode.ex │ │ │ ├── mean.ex │ │ │ └── median.ex │ ├── README.md │ ├── mix.exs │ └── .gitignore └── tutorials │ ├── test │ ├── test_helper.exs │ └── tutorials_test.exs │ ├── lib │ ├── tutorials.ex │ └── tutorials │ │ ├── recursion │ │ ├── print_digits.ex │ │ ├── comp.ex │ │ ├── factorial.ex │ │ ├── reverse_num.ex │ │ └── sum_digits.ex │ │ ├── lists │ │ └── lists.ex │ │ └── structs │ │ ├── seven_wonders.ex │ │ └── expense.ex │ ├── .vscode │ └── settings.json │ ├── .formatter.exs │ ├── functional_elixir_octallium.png │ ├── README.md │ ├── mix.exs │ └── .gitignore ├── .gitignore ├── functional_elixir_octallium.png ├── scripts ├── hello.exs └── universe.exs ├── readme.md └── notebooks ├── 03_flow_control.livemd ├── 02_data_types.livemd └── 01_introduction.livemd /code/stats/test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | -------------------------------------------------------------------------------- /code/tutorials/test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | -------------------------------------------------------------------------------- /code/tutorials/lib/tutorials.ex: -------------------------------------------------------------------------------- 1 | defmodule Tutorials do 2 | end 3 | -------------------------------------------------------------------------------- /code/tutorials/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": ["upto"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.elixir_ls 2 | 3 | /.DS_store 4 | 5 | _build 6 | 7 | .elixir_ls 8 | 9 | .DS_store 10 | 11 | -------------------------------------------------------------------------------- /code/stats/.formatter.exs: -------------------------------------------------------------------------------- 1 | # Used by "mix format" 2 | [ 3 | inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] 4 | ] 5 | -------------------------------------------------------------------------------- /code/tutorials/.formatter.exs: -------------------------------------------------------------------------------- 1 | # Used by "mix format" 2 | [ 3 | inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] 4 | ] 5 | -------------------------------------------------------------------------------- /functional_elixir_octallium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octallium/functional-programming-with-elixir/HEAD/functional_elixir_octallium.png -------------------------------------------------------------------------------- /scripts/hello.exs: -------------------------------------------------------------------------------- 1 | defmodule Hello do 2 | @spec world() :: :ok 3 | def world do 4 | IO.puts("Hello Elixir") 5 | end 6 | end 7 | 8 | Hello.world() 9 | -------------------------------------------------------------------------------- /code/stats/functional_elixir_octallium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octallium/functional-programming-with-elixir/HEAD/code/stats/functional_elixir_octallium.png -------------------------------------------------------------------------------- /code/tutorials/functional_elixir_octallium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octallium/functional-programming-with-elixir/HEAD/code/tutorials/functional_elixir_octallium.png -------------------------------------------------------------------------------- /code/stats/lib/errors.ex: -------------------------------------------------------------------------------- 1 | defmodule Stats.Errors do 2 | @spec invalid_data_type :: {:error, String.t()} 3 | def invalid_data_type, do: {:error, "Invalid Data Type"} 4 | end 5 | -------------------------------------------------------------------------------- /code/stats/test/stats_test.exs: -------------------------------------------------------------------------------- 1 | defmodule StatsTest do 2 | use ExUnit.Case 3 | doctest Stats 4 | 5 | # test "greets the world" do 6 | # assert Stats.hello() == :world 7 | # end 8 | end 9 | -------------------------------------------------------------------------------- /code/tutorials/lib/tutorials/recursion/print_digits.ex: -------------------------------------------------------------------------------- 1 | defmodule Tutorials.Recursion.PrintDigits do 2 | def upto(0), do: 0 3 | 4 | def upto(num) do 5 | IO.puts(num) 6 | upto(num - 1) 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /code/tutorials/test/tutorials_test.exs: -------------------------------------------------------------------------------- 1 | defmodule TutorialsTest do 2 | use ExUnit.Case 3 | doctest Tutorials 4 | 5 | test "greets the world" do 6 | assert Tutorials.hello() == :world 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /code/tutorials/lib/tutorials/recursion/comp.ex: -------------------------------------------------------------------------------- 1 | defmodule Tutorials.Recursion.Comp do 2 | alias Tutorials.Structs.SevenWonders 3 | 4 | def all_names() do 5 | for %{name: name} <- SevenWonders.all() do 6 | name 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /code/tutorials/lib/tutorials/recursion/factorial.ex: -------------------------------------------------------------------------------- 1 | defmodule Tutorials.Recursion.Factorial do 2 | def of(1), do: 1 3 | def of(num), do: num * of(num - 1) 4 | 5 | def of_tail_rec(num, acc \\ 1) 6 | def of_tail_rec(1, acc), do: acc 7 | def of_tail_rec(num, acc), do: of_tail_rec(num - 1, acc * num) 8 | end 9 | -------------------------------------------------------------------------------- /code/stats/lib/validators.ex: -------------------------------------------------------------------------------- 1 | defmodule Stats.Validators do 2 | alias Stats.Errors 3 | 4 | @spec validate_num_list([number()]) :: {boolean(), [number()]} | {atom(), String.t()} 5 | def validate_num_list(nums) when is_list(nums) do 6 | {Enum.all?(nums, fn el -> is_number(el) end), nums} 7 | end 8 | 9 | def validate_num_list(_), do: Errors.invalid_data_type() 10 | end 11 | -------------------------------------------------------------------------------- /code/tutorials/lib/tutorials/recursion/reverse_num.ex: -------------------------------------------------------------------------------- 1 | defmodule Tutorials.Recursion.ReverseNum do 2 | def of(num, acc \\ 0) 3 | def of(0, acc), do: acc 4 | # def of(num, acc), do: of(div(num, 10), acc * 10 + rem(num, 10)) 5 | def of(num, acc) do 6 | new_num = div(num, 10) 7 | new_acc = acc * 10 + rem(num, 10) 8 | 9 | of(new_num, new_acc) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /scripts/universe.exs: -------------------------------------------------------------------------------- 1 | defmodule Universe do 2 | def big_bang do 3 | IO.puts("Big Bang 💣 ") 4 | Process.sleep(1000) 5 | expand() 6 | end 7 | 8 | def expand(state \\ 0) do 9 | IO.puts("Size of Universe is: #{state}") 10 | Process.sleep(1000) 11 | # Think of `state + 1` as mutation 12 | # and not just as addition. 13 | expand(state + 1) 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /code/tutorials/lib/tutorials/recursion/sum_digits.ex: -------------------------------------------------------------------------------- 1 | defmodule Tutorials.Recursion.SumDigits do 2 | def upto(0), do: 0 3 | 4 | def upto(num) do 5 | num + upto(num - 1) 6 | end 7 | 8 | def upto_tail_rec(num, acc \\ 0) 9 | def upto_tail_rec(0, acc), do: acc 10 | def upto_tail_rec(num, acc), do: upto_tail_rec(num - 1, acc + num) 11 | 12 | # Student Exercise - Can you write a case to handle negative numbers? 13 | # 123 14 | end 15 | -------------------------------------------------------------------------------- /code/stats/README.md: -------------------------------------------------------------------------------- 1 |  2 | 3 | # Project Statistics - Functional Programming With Elixir 4 | 5 | Help Louis take better business decisions by creating a Statistics library. 6 | 7 | ## Sequence 8 | 9 | 1. Central Tendency - 10 | 1. Mean 11 | 2. Median 12 | 3. Mode 13 | 2. Variability - 14 | 1. Variance 15 | 2. Standard Deviation 16 | 17 | ## Connect 18 | 19 | [www.octallium.com](https://www.octallium.com/) 20 | 21 | [@octallium](https://twitter.com/octallium) -------------------------------------------------------------------------------- /code/tutorials/README.md: -------------------------------------------------------------------------------- 1 |  2 | 3 | # Basic Tutorials - Functional Programming With Elixir 4 | 5 | Learn the basics of functional programming by creating a mix Tutorial project. 6 | 7 | ## Sequence - 8 | 9 | 1. Recursion - 10 | 1. Print Digits 11 | 2. Sum Digits 12 | 3. Factorial 13 | 4. Reverse Num 14 | 2. Lists 15 | 3. Structs - 16 | 1. Seven Wonders 17 | 2. Expense Manager 18 | 19 | ## Connect 20 | 21 | [www.octallium.com](https://www.octallium.com/) 22 | 23 | [@octallium](https://twitter.com/octallium) -------------------------------------------------------------------------------- /code/stats/lib/variability/standard_deviation.ex: -------------------------------------------------------------------------------- 1 | defmodule Stats.Variability.StandardDeviation do 2 | alias Stats.Variability.Variance 3 | 4 | @doc """ 5 | 6 | Steps: 7 | 1. Take square root of Variance 8 | 9 | """ 10 | @spec p_std_dev([number()]) :: float() 11 | def p_std_dev(nums) when is_list(nums) do 12 | nums 13 | |> Variance.p_variance() 14 | |> :math.sqrt() 15 | end 16 | 17 | @spec s_std_dev([number]) :: float 18 | def s_std_dev(nums) when is_list(nums) do 19 | nums 20 | |> Variance.s_variance() 21 | |> :math.sqrt() 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /code/stats/mix.exs: -------------------------------------------------------------------------------- 1 | defmodule Stats.MixProject do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :stats, 7 | version: "0.1.0", 8 | elixir: "~> 1.14", 9 | start_permanent: Mix.env() == :prod, 10 | deps: deps() 11 | ] 12 | end 13 | 14 | # Run "mix help compile.app" to learn about applications. 15 | def application do 16 | [ 17 | extra_applications: [:logger] 18 | ] 19 | end 20 | 21 | # Run "mix help deps" to learn about dependencies. 22 | defp deps do 23 | [ 24 | # {:dep_from_hexpm, "~> 0.3.0"}, 25 | # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"} 26 | ] 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /code/stats/test/central_tendency_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CentralTendencyTest do 2 | use ExUnit.Case, async: true 3 | alias Stats.CentralTendency.{Mean, Median, Mode} 4 | 5 | doctest Mode 6 | doctest Median 7 | doctest Mean 8 | 9 | test "mode returns multiple right values" do 10 | mode = Mode.mode([1, 2, 3, 4, 5, 4, 5, 2, 8, 9, 8, 6, 8, 4]) 11 | assert [4, 8] == mode 12 | end 13 | 14 | test "mode returns single right values" do 15 | mode = Mode.mode([1, 2, 3, 4, 5, 4, 5, 2, 8, 9, 8, 6, 8]) 16 | assert [8] == mode 17 | end 18 | 19 | test "mode returns nil for the right values" do 20 | mode = Mode.mode([1, 2, 3, 4, 5, 6, 7, 8, 9]) 21 | assert nil == mode 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /code/tutorials/mix.exs: -------------------------------------------------------------------------------- 1 | defmodule Tutorials.MixProject do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :tutorials, 7 | version: "0.1.0", 8 | elixir: "~> 1.14", 9 | start_permanent: Mix.env() == :prod, 10 | deps: deps() 11 | ] 12 | end 13 | 14 | # Run "mix help compile.app" to learn about applications. 15 | def application do 16 | [ 17 | extra_applications: [:logger] 18 | ] 19 | end 20 | 21 | # Run "mix help deps" to learn about dependencies. 22 | defp deps do 23 | [ 24 | # {:dep_from_hexpm, "~> 0.3.0"}, 25 | # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"} 26 | ] 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /code/stats/.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build/ 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover/ 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps/ 9 | 10 | # Where third-party dependencies like ExDoc output generated docs. 11 | /doc/ 12 | 13 | # Ignore .fetch files in case you like to edit your project deps locally. 14 | /.fetch 15 | 16 | # If the VM crashes, it generates a dump, let's ignore it too. 17 | erl_crash.dump 18 | 19 | # Also ignore archive artifacts (built via "mix archive.build"). 20 | *.ez 21 | 22 | # Ignore package tarball (built via "mix hex.build"). 23 | stats-*.tar 24 | 25 | # Temporary files, for example, from tests. 26 | /tmp/ 27 | 28 | /.elixir_ls -------------------------------------------------------------------------------- /code/tutorials/.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build/ 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover/ 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps/ 9 | 10 | # Where third-party dependencies like ExDoc output generated docs. 11 | /doc/ 12 | 13 | # Ignore .fetch files in case you like to edit your project deps locally. 14 | /.fetch 15 | 16 | # If the VM crashes, it generates a dump, let's ignore it too. 17 | erl_crash.dump 18 | 19 | # Also ignore archive artifacts (built via "mix archive.build"). 20 | *.ez 21 | 22 | # Ignore package tarball (built via "mix hex.build"). 23 | tutorials-*.tar 24 | 25 | # Temporary files, for example, from tests. 26 | /tmp/ 27 | 28 | /.elixir_ls 29 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 |  2 | 3 | # Functional Programming With Elixir 4 | 5 | A Introductory Course On Functional Programming Using The Elixir Programming Language 6 | 7 | ## Topics 8 | 9 | 1. Introduction to Functional Programming 10 | 2. Introduction to Elixir 11 | 3. Installation & Set Up 12 | 4. Introduction to Recursion 13 | 5. Notebooks - 14 | 1. Match Operator 15 | 2. Pattern Matching 16 | 3. Data Types - Tuple, Atom, List, Map, Struct 17 | 4. Flow Control - Recursion, for, cond, case, with 18 | 6. Introduction to Mix 19 | 7. Mix - Tutorials 20 | 8. Mix - Stats Project 21 | 9. Conclusion 22 | 10. Final Words 23 | 24 | ## Connect 25 | 26 | [www.octallium.com](https://www.octallium.com/) 27 | 28 | [@octallium](https://twitter.com/octallium) -------------------------------------------------------------------------------- /code/stats/lib/stats.ex: -------------------------------------------------------------------------------- 1 | defmodule Stats do 2 | alias Stats.CentralTendency.{Mean, Median, Mode} 3 | alias Stats.Variability.{StandardDeviation, Variance} 4 | 5 | @spec population_mean([number]) :: number | {:error, String.t()} 6 | defdelegate population_mean(num_list), to: Mean 7 | 8 | @spec sample_mean([number]) :: number | {:error, String.t()} 9 | defdelegate sample_mean(num_list), to: Mean 10 | 11 | @spec median([number]) :: number | {:error, String.t()} 12 | defdelegate median(num_list), to: Median 13 | 14 | @spec mode([number]) :: nil | number() 15 | defdelegate mode(num_list), to: Mode 16 | 17 | @spec p_variance([number]) :: float 18 | defdelegate p_variance(nums), to: Variance 19 | 20 | @spec s_variance([number]) :: float 21 | defdelegate s_variance(nums), to: Variance 22 | 23 | @spec p_std_dev([number]) :: float 24 | defdelegate p_std_dev(nums), to: StandardDeviation 25 | 26 | @spec s_std_dev([number]) :: float 27 | defdelegate s_std_dev(nums), to: StandardDeviation 28 | end 29 | -------------------------------------------------------------------------------- /code/stats/lib/variability/variance.ex: -------------------------------------------------------------------------------- 1 | defmodule Stats.Variability.Variance do 2 | alias Stats.CentralTendency.Mean 3 | 4 | @doc """ 5 | 6 | Steps: 7 | 1. Find mean 8 | 2. Find Sigma = sum of (mean - num)^2 9 | 3. Divide sigma by sum of number of elements 10 | 11 | """ 12 | @spec p_variance([number()]) :: number() 13 | def p_variance(nums) do 14 | s = nums |> Mean.population_mean() |> sigma(nums) 15 | s / Enum.count(nums) 16 | end 17 | 18 | # ==================================================================== 19 | 20 | @spec s_variance([number()]) :: number() 21 | def s_variance(nums) do 22 | s = nums |> Mean.sample_mean() |> sigma(nums) 23 | s / (Enum.count(nums) - 1) 24 | end 25 | 26 | # ==================================================================== 27 | 28 | @spec sigma(number(), [number], number()) :: number() 29 | defp sigma(mean, nums, acc \\ 0) 30 | defp sigma(_, [], acc), do: acc 31 | 32 | defp sigma(mean, [h | t], acc) do 33 | x = :math.pow(h - mean, 2) 34 | sigma(mean, t, acc + x) 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /code/stats/lib/central_tendency/mode.ex: -------------------------------------------------------------------------------- 1 | defmodule Stats.CentralTendency.Mode do 2 | @doc """ 3 | 4 | `Mode` finds out the elements which are repeating most often. 5 | 6 | Steps: 7 | 1. Group elements by the number of occurrences/frequencies. 8 | 2. Find the maximum number of occurrences. 9 | 3. Group the elements which has the max occurrences. 10 | 11 | Example: 12 | 13 | iex> values = [1, 2, 3, 4, 5, 4, 5, 2, 8, 9, 8, 6, 8, 4] 14 | iex> Stats.CentralTendency.Mode.mode(values) 15 | [4, 8] 16 | 17 | """ 18 | @spec mode([number()]) :: nil | [number()] 19 | def mode(nums) when is_list(nums) do 20 | ranked_map = nums |> Enum.frequencies() 21 | 22 | ranked_map 23 | |> Map.values() 24 | |> Enum.max() 25 | |> mode_func(ranked_map) 26 | end 27 | 28 | # ==================================================================== 29 | 30 | @spec mode_func(number(), map()) :: nil | [number()] 31 | defp mode_func(1, _), do: nil 32 | 33 | defp mode_func(max, ranked_map) do 34 | ranked_map 35 | |> Map.filter(fn {_key, value} -> value == max end) 36 | |> Map.keys() 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /code/stats/lib/central_tendency/mean.ex: -------------------------------------------------------------------------------- 1 | defmodule Stats.CentralTendency.Mean do 2 | alias Stats.Errors 3 | alias Stats.Validators 4 | 5 | # =============================x======================================= 6 | 7 | @spec population_mean([number()]) :: number() | {:error, String.t()} 8 | def population_mean([]), do: Errors.invalid_data_type() 9 | 10 | def population_mean(nums) when is_list(nums) do 11 | nums 12 | |> Validators.validate_num_list() 13 | |> calc_population_mean() 14 | end 15 | 16 | def population_mean(_), do: Errors.invalid_data_type() 17 | 18 | # ==================================================================== 19 | 20 | @spec sample_mean([number()]) :: number() | {:error, String.t()} 21 | def sample_mean(nums), do: population_mean(nums) 22 | 23 | # ==================================================================== 24 | 25 | defp calc_population_mean({false, _}), do: Errors.invalid_data_type() 26 | 27 | defp calc_population_mean({true, nums}) do 28 | nums 29 | |> Enum.sum() 30 | |> mean(Enum.count(nums)) 31 | end 32 | 33 | # ==================================================================== 34 | 35 | defp mean(sigma, count), do: sigma / count 36 | 37 | # ==================================================================== 38 | end 39 | -------------------------------------------------------------------------------- /notebooks/03_flow_control.livemd: -------------------------------------------------------------------------------- 1 | # Flow Control 2 | 3 | ## Case 4 | 5 | ```elixir 6 | list = [1, 2, 3] 7 | ``` 8 | 9 | ```elixir 10 | case Enum.at(list, 2) do 11 | 1 -> "This won't print" 12 | 3 -> "3 is a match!" 13 | _ -> "Catch all" 14 | end 15 | ``` 16 | 17 | ```elixir 18 | defmodule Post do 19 | defstruct( 20 | id: nil, 21 | title: "", 22 | description: "", 23 | author: "" 24 | ) 25 | end 26 | ``` 27 | 28 | ```elixir 29 | post1 = %Post{id: 1, title: "Title No 1", author: "Julius Caeser"} 30 | ``` 31 | 32 | ```elixir 33 | case post1 do 34 | %{author: "Octallium"} -> "Got a post from Octallium" 35 | %{author: "Anil Kulkarni"} -> "Got a post from Anil Kulkarni" 36 | %{author: author} -> "Got a post from #{author}" 37 | end 38 | ``` 39 | 40 | ```elixir 41 | post1 = %{post1 | author: "Anil Kulkarni"} 42 | ``` 43 | 44 | ## Cond 45 | 46 | ```elixir 47 | cond do 48 | post1.author == "Octallium" -> "Editing a post from Octallium" 49 | post1.author == "Anil Kulkarni" -> "Editing a post from Anil Kulkarni" 50 | true -> "This is a catch all" 51 | end 52 | ``` 53 | 54 | ```elixir 55 | cond do 56 | hd(list) == 1 -> "Got a 1" 57 | true -> "Head is #{hd(list)}" 58 | end 59 | ``` 60 | 61 | ## If/Else 62 | 63 | ```elixir 64 | if true do 65 | "This will work" 66 | else 67 | "Else this will work" 68 | end 69 | ``` 70 | -------------------------------------------------------------------------------- /code/tutorials/lib/tutorials/lists/lists.ex: -------------------------------------------------------------------------------- 1 | defmodule Tutorials.Lists do 2 | @moduledoc """ 3 | 4 | Function Summary: 5 | 6 | 1. sum 7 | 2. reverse 8 | 3. map 9 | 4. concat 10 | 5. flat_map 11 | """ 12 | 13 | # -------------------------- Sum ------------------------------ 14 | 15 | @doc """ 16 | Returns the sum of numbers in a list. 17 | """ 18 | @spec sum(list(number())) :: number() 19 | def sum(nums), do: sum_tail_rec(nums) 20 | 21 | @spec sum_simple(list(number())) :: number() 22 | def sum_simple([]), do: 0 23 | def sum_simple([h | t]), do: h + sum(t) 24 | 25 | @spec sum_tail_rec(list(number()), integer()) :: number() 26 | def sum_tail_rec(nums, acc \\ 0) 27 | def sum_tail_rec([], acc), do: acc 28 | def sum_tail_rec([h | t], acc), do: sum_tail_rec(t, acc + h) 29 | 30 | # -------------------------- Reverse --------------------------- 31 | 32 | @spec reverse([any()], [any()]) :: [any()] 33 | def reverse(elements, acc \\ []) 34 | def reverse([], acc), do: acc 35 | def reverse([h | t], acc), do: reverse(t, [h | acc]) 36 | 37 | # -------------------------- Map ------------------------------ 38 | 39 | @spec map([any()], (any -> any), [any()]) :: [any()] 40 | def map(elements, fun, acc \\ []) 41 | def map([], _, acc), do: reverse(acc) 42 | def map([h | t], func, acc), do: map(t, func, [func.(h) | acc]) 43 | 44 | # -------------------------- Concat ------------------------------ 45 | 46 | @spec concat([any], [any]) :: [any] 47 | def concat(src, dst), do: concat_func(src |> reverse(), dst) 48 | 49 | defp concat_func([], dst), do: dst 50 | defp concat_func([h | t], dst), do: concat_func(t, [h | dst]) 51 | 52 | # -------------------------- FlatMap ------------------------------ 53 | 54 | def flat_map(elements, func, acc \\ []) 55 | def flat_map([], _, acc), do: acc 56 | def flat_map([h | t], func, acc), do: flat_map(t, func, concat(acc, func.(h))) 57 | end 58 | -------------------------------------------------------------------------------- /code/stats/lib/central_tendency/median.ex: -------------------------------------------------------------------------------- 1 | defmodule Stats.CentralTendency.Median do 2 | require Integer 3 | 4 | alias Stats.Errors 5 | alias Stats.Validators 6 | 7 | # ==================================================================== 8 | 9 | # Scenario - Louis Sold The Following Candies in 9 hrs 10 | # 10, 40, 20, 50, 30, 90, 70, 60, 80 11 | 12 | # Median -> Centre Value or The Value Which Divides Measurements In Two Parts. 13 | # First 50% and the next 50% 14 | 15 | # Steps - 16 | # 1. Sort - [10, 20, 30, 40, 50, 60, 70, 80, 90] 17 | # 2. Count - 18 | # 2.1 - If Odd - Take the value at the middle = 50 (Median) 19 | # 2.2 - If Even - Take 2 value at the middle And Return Their Average = 50 (Median) 20 | # E.g [10, 20, 30, 40, 50, 60, 70, 80] = (40 + 50) / 2 = 45 (Median) 21 | 22 | # ==================================================================== 23 | 24 | @spec median([number()]) :: number() | {:error, String.t()} 25 | def median(num_list) when is_list(num_list) do 26 | num_list 27 | |> Validators.validate_num_list() 28 | |> calc_median() 29 | end 30 | 31 | def median(_), do: Errors.invalid_data_type() 32 | 33 | # ==================================================================== 34 | 35 | defp calc_median({:error, _msg}), do: Errors.invalid_data_type() 36 | defp calc_median({false, _}), do: Errors.invalid_data_type() 37 | 38 | defp calc_median({true, num_list}) do 39 | count = Enum.count(num_list) 40 | 41 | num_list 42 | |> Enum.sort() 43 | |> get_median(Integer.is_even(count), count) 44 | end 45 | 46 | # ==================================================================== 47 | 48 | defp get_median(num_list, false, count), do: Enum.at(num_list, div(count, 2)) 49 | 50 | defp get_median(num_list, true, count) do 51 | a = Enum.at(num_list, div(count - 1, 2)) 52 | b = Enum.at(num_list, div(count, 2)) 53 | 54 | (a + b) / 2 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /code/tutorials/lib/tutorials/structs/seven_wonders.ex: -------------------------------------------------------------------------------- 1 | defmodule Tutorials.Structs.SevenWonders do 2 | defstruct name: "", country: "" 3 | 4 | alias Tutorials.Structs.SevenWonders 5 | 6 | @type t :: %SevenWonders{ 7 | name: String.t(), 8 | country: String.t() 9 | } 10 | 11 | # -------------------------- All ------------------------------ 12 | 13 | @spec all :: [t()] 14 | def all() do 15 | [ 16 | %SevenWonders{name: "Taj Mahal", country: "India"}, 17 | %SevenWonders{name: "Chichén Itzá", country: "Mexico"}, 18 | %SevenWonders{name: "Petra", country: "Jordan"}, 19 | %SevenWonders{name: "Machu Picchu", country: "Peru"}, 20 | %SevenWonders{name: "Christ the Redeemer", country: "Brazil"}, 21 | %SevenWonders{name: "Colosseum", country: "Italy"}, 22 | %SevenWonders{name: "The Great Wall of China", country: "China"} 23 | ] 24 | end 25 | 26 | # --------------------------------------------------------------- 27 | 28 | @spec print_names([t()]) :: :ok 29 | def print_names(wonders) do 30 | Enum.each(wonders, fn %{name: name} -> IO.puts(name) end) 31 | end 32 | 33 | @spec filter_by_country([t()], String.t()) :: [t()] 34 | def filter_by_country(wonders, country) do 35 | wonders 36 | |> Enum.filter(fn %{country: country_name} -> country_name == country end) 37 | end 38 | 39 | @spec in_countries_starting_with_i([t]) :: [String.t()] 40 | def in_countries_starting_with_i(wonders) do 41 | wonders 42 | |> Enum.filter(fn %{country: country} -> String.starts_with?(country, "I") end) 43 | end 44 | 45 | @spec sort_by_country_length([t]) :: [t()] 46 | def sort_by_country_length(wonders) do 47 | wonders 48 | |> Enum.sort(fn x, y -> String.length(x.country) < String.length(y.country) end) 49 | end 50 | 51 | @spec name_country_list([t()]) :: [any()] 52 | def name_country_list(wonders) do 53 | wonders 54 | |> Enum.reduce([], fn wonder, acc -> [[wonder.name, wonder.country] | acc] end) 55 | end 56 | 57 | @spec country_name_keyword_list([t()]) :: Keyword.t() 58 | def country_name_keyword_list(wonders) do 59 | wonders 60 | |> Enum.reduce([], fn wonder, acc -> [{String.to_atom(wonder.country), wonder.name} | acc] end) 61 | end 62 | 63 | # -------------------------- For ------------------------------ 64 | 65 | def all_names() do 66 | for %{name: name} <- all(), do: name 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /code/tutorials/lib/tutorials/structs/expense.ex: -------------------------------------------------------------------------------- 1 | defmodule Tutorials.Structs.Expense do 2 | alias Tutorials.Structs.Expense 3 | 4 | defstruct( 5 | title: "", 6 | date: nil, 7 | amount: 0, 8 | store: "" 9 | ) 10 | 11 | @type t :: %Expense{ 12 | title: String.t(), 13 | date: Date.t() | nil, 14 | amount: number(), 15 | store: String.t() 16 | } 17 | 18 | # -------------------------- Sample ------------------------------ 19 | 20 | @spec sample :: [t()] 21 | def sample do 22 | [ 23 | %Expense{title: "Grocery", date: ~D[2022-09-12], amount: 18.99, store: "Metro"}, 24 | %Expense{title: "Mobile", date: ~D[2022-10-18], amount: 55.65, store: "Bell"}, 25 | %Expense{title: "Jacket", date: ~D[2022-07-06], amount: 150.99, store: "Marks"}, 26 | %Expense{title: "Air Jordans", date: ~D[2022-10-30], amount: 799.99, store: "Nike"} 27 | ] 28 | end 29 | 30 | # --------------------------------------------------------------- 31 | 32 | @spec total([t()]) :: number() 33 | def total(expenses) do 34 | expenses 35 | |> Enum.reduce(0, fn expense, acc -> expense.amount + acc end) 36 | end 37 | 38 | @spec sort_by_date([t]) :: [t()] 39 | def sort_by_date(expenses) do 40 | expenses 41 | |> Enum.sort_by(& &1.date) 42 | end 43 | 44 | @spec add_expense(t) :: [t()] 45 | def add_expense(%Expense{} = expense) do 46 | [expense | sample()] 47 | end 48 | 49 | @spec update_amount(String.t(), number()) :: [t()] 50 | def update_amount(title, amount) do 51 | [item] = Enum.filter(sample(), fn %{title: expense_title} -> expense_title == title end) 52 | new_item = %{item | amount: amount} 53 | 54 | [new_item | sample() |> List.delete(item)] 55 | end 56 | 57 | # -------------------------- With ------------------------------ 58 | 59 | @users ["Coco", "Cece", "Louis", "Chiko"] 60 | 61 | def authenticate(user) when user in @users, do: {:ok, "authorized"} 62 | def authenticate(_), do: {:error, "unauthorized"} 63 | 64 | def verify_password(user, _password) when user in @users, do: {:ok, "password verified"} 65 | def verify_password(_user, _password), do: {:error, "wrong password"} 66 | 67 | def login(user, password) do 68 | with {:ok, _auth_msg} <- authenticate(user), 69 | {:ok, _msg} <- verify_password(user, password) do 70 | {:ok, "#{user} logged in successfully"} 71 | else 72 | {:error, msg} -> {:error, msg} 73 | _ -> :unauthorized 74 | end 75 | end 76 | end 77 | -------------------------------------------------------------------------------- /notebooks/02_data_types.livemd: -------------------------------------------------------------------------------- 1 | # Data Types 2 | 3 | ## Atom 4 | 5 | ```elixir 6 | :error 7 | ``` 8 | 9 | ```elixir 10 | {:error, reason} = {:error, "file not found"} 11 | reason 12 | ``` 13 | 14 | ```elixir 15 | {:ok, msg} = {:ok, "status 200 ok"} 16 | msg 17 | ``` 18 | 19 | ## String & Code Points 20 | 21 | ```elixir 22 | # Strings are UTF-8 encoded 23 | name = "Octallium" 24 | name 25 | ``` 26 | 27 | ```elixir 28 | is_binary(name) 29 | ``` 30 | 31 | ```elixir 32 | msg = "Hello " <> name 33 | ``` 34 | 35 | ```elixir 36 | "Hello " <> name = msg 37 | name 38 | ``` 39 | 40 | ```elixir 41 | <
> = name 42 | head 43 | ``` 44 | 45 | ```elixir 46 | head == ?O 47 | ``` 48 | 49 | ```elixir 50 | # Pattern Matching 51 | <<"O", rest::binary>> = name 52 | rest 53 | ``` 54 | 55 | ```elixir 56 | <<"Oc", rest::binary>> = name 57 | rest 58 | ``` 59 | 60 | ```elixir 61 | # Specify Size 62 | <