Powered by AppSignal & Oban Pro

5. Read 4: Lists and Algorithms

5_read_4_lists_and_algorithms.livemd

5. Read 4: Lists and Algorithms

Lists

[1, 2, 3]
[1, 2, 3]

List construction

list1 = []
[]
list2 = [3 | list1]
[3]
list3 = [2 | list2]
[2, 3]
list4 = [1 | list3]
[1, 2, 3]
append = fn lst, item -> [item | lst] end
#Function<41.105768164/2 in :erl_eval.expr/6>
[] |> append.(3) |> append.(2) |> append.(1)
[1, 2, 3]

The Enumerable Abstraction

Enum.map([1, 2, 3], fn x -> x * 2 end)
[2, 4, 6]
Enum.map(1..3, fn x -> x * 2 end)
[2, 4, 6]
Enum.sum([1, 2, 3])
6
Enum.map(1..3, fn x -> x * 2 end)
[2, 4, 6]

Pattern Matching and Lists

list = [1, 2, 3]
[1, 2, 3]
[head | tail] = [1, 2, 3]
[1, 2, 3]
head
1
tail
[2, 3]
[] = [1, 2, 3]
[first, second | rest] = list
[1, 2, 3]
second
2
[first, second, third] = list
[1, 2, 3]

Advanced Pattern

list = [1, 2, 3]
[1, 2, 3]
[_, second, third | rest] = list
[1, 2, 3]
second
2
third
3
rest
[]
[1 | [_ | rest]] = list
[1, 2, 3]
rest
[3]

Recursion Over Lists

Simplest Recursive Algorithms

defmodule Calculation do
  def total([]), do: 0
  def total([head | tail]), do: head + total(tail)

  def total_with_accumulator([], partial_total) do
    partial_total
  end

  def total_with_accumulator([first | rest], partial_total) do
    total_with_accumulator(rest, partial_total + first)
  end
end
{:module, Calculation, <<70, 79, 82, 49, 0, 0, 7, ...>>, {:total_with_accumulator, 2}}
import Calculation

IO.inspect(Calculation.total([1, 2, 3]))
6
6

Tail Recursion With Accumulators

total_with_accumulator([1, 2, 3], 0)
6

Reduce and Anonymous Function

add = fn x, y -> x + y end
#Function<41.105768164/2 in :erl_eval.expr/6>
add.(3, 4)
7
inc = fn x -> add.(x, 1) end
#Function<42.105768164/1 in :erl_eval.expr/6>
inc.(4)
5
4 |> inc.() |> inc.()
6

Higher Order Function

add = fn list_item, acc -> list_item + acc end
#Function<41.105768164/2 in :erl_eval.expr/6>
Enum.reduce(list, 0, add)

# same with 
# 0
# |> add.(1)
# |> add.(2)
# |> add.(3)
6

Implement a Polygon

defmodule Point do
  def origin, do: {0, 0}
  def right({x, y}), do: {x + 1, y}
  def left({x, y}), do: {x - 1, y}
  def up({x, y}), do: {x, y - 1}
  def down({x, y}), do: {x, y + 1}
  def mirror({x, y}, w), do: {w - x, y}
  def flip({x, y}, h), do: {x, h - y}
  def transpose({x, y}), do: {y, x}
  def move({x, y}, {cx, cy}), do: {x + cx, y + cy}

  def rotate(point, 0, _w, _h) do
    point
  end

  def rotate(point, 90, w, _h) do
    point
    |> transpose
    |> mirror(w)
  end

  def rotate(point, 180, w, h) do
    point
    |> flip(h)
    |> mirror(w)
  end

  def rotate(point, 270, _w, h) do
    point
    |> flip(h)
    |> transpose
  end
end
{:module, Point, <<70, 79, 82, 49, 0, 0, 12, ...>>, {:rotate, 4}}
import Point

defmodule Polygon do
  def rectangle({x, y}, height, width) do
    [
      {x, y},
      {x + width, y},
      {x + width, y + height},
      {x, y + height}
    ]
  end

  def square(point, length) do
    rectangle(point, length, length)
  end

  # def mirror(polygon, w) do
  #   Enum.map(polygon, fn point -> Point.mirror(point, w) end)
  # end
  def mirror(polygon, w), do: Enum.map(polygon, &amp;Point.mirror(&amp;1, w))
  def flip(polygon, h), do: Enum.map(polygon, &amp;Point.flip(&amp;1, h))
  def transpose(polygon), do: Enum.map(polygon, &amp;Point.transpose/1)

  def rotate(polygon, degrees, w, h) do
    Enum.map(polygon, &amp;Point.rotate(&amp;1, degrees, w, h))
  end

  def move(polygon, vector) do
    Enum.map(polygon, &amp;Point.move(&amp;1, vector))
  end
end
{:module, Polygon, <<70, 79, 82, 49, 0, 0, 12, ...>>, {:move, 2}}
r = Polygon.rectangle(Point.origin(), 4, 2)
[{0, 0}, {2, 0}, {2, 4}, {0, 4}]
r |> Polygon.rotate(270, 2, 4)
[{4, 0}, {4, 2}, {0, 2}, {0, 0}]