EMZETT.
Login

Kurz: Enum arbeitet sofort, Stream baut eine Rechenvorschrift und berechnet erst bei Bedarf. So gehen auch unendliche und sehr große Folgen:

Teil des Kurses Elixir

Kapitel 4 von 8 im Kurs Elixir (Abschnitt „Daten verarbeiten“). Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

if, unless, cond, case, with

x = 7
if x > 5 do
  IO.puts("groß")
else
  IO.puts("klein")
end
unless x == 0, do: IO.puts("nicht null")
 
# cond: wie if/else-if
note =
  cond do
    x >= 9 -> "sehr gut"
    x >= 5 -> "gut"
    true -> "weiter üben"
  end
IO.puts(note)
 
# case: Pattern Matching mit Guards
beschreibe = fn wert ->
  case wert do
    {:ok, n} when n > 100 -> "großer Erfolg #{n}"
    {:ok, n} -> "Erfolg #{n}"
    {:error, grund} -> "Fehler: #{grund}"
    [] -> "leere Liste"
    [h | _] -> "Liste mit Kopf #{h}"
    %{name: name} -> "Map mit Name #{name}"
    _ -> "unbekannt"
  end
end
for v <- [{:ok, 500}, {:ok, 5}, {:error, "kaputt"}, [], [9, 8], %{name: "Mia"}, 3.5], do: IO.puts(beschreibe.(v))
 
# with: Kette von Matches, bricht beim ersten Fehlschlag ab
def_parse = fn s ->
  with {n, ""} <- Integer.parse(s),
       true <- n > 0 do
    {:ok, n * 2}
  else
    :error -> {:error, :keine_zahl}
    {_, _rest} -> {:error, :ungueltig}
    false -> {:error, :nicht_positiv}
  end
end
IO.inspect(Enum.map(["21", "abc", "-5", "7x"], def_parse))

Ausgabe:

groß
nicht null
gut
großer Erfolg 500
Erfolg 5
Fehler: kaputt
leere Liste
Liste mit Kopf 9
Map mit Name Mia
unbekannt
[ok: 42, error: :keine_zahl, error: :nicht_positiv, error: :ungueltig]

Schleifen gibt es nicht: Enum ersetzt sie

z = [1, 2, 3, 4, 5, 6]
IO.inspect(Enum.map(z, &(&1 * &1)))
IO.inspect(Enum.filter(z, &(&1 > 3)))
IO.inspect(Enum.reject(z, &(&1 > 3)))
IO.inspect(Enum.reduce(z, 0, fn x, acc -> acc + x end))
IO.inspect(Enum.sum(z))
IO.inspect(Enum.count(z, &(rem(&1, 2) == 0)))
IO.inspect(Enum.find(z, &(&1 > 3)))
IO.inspect(Enum.all?(z, &(&1 > 0)))
IO.inspect(Enum.any?(z, &(&1 > 5)))
IO.inspect(Enum.take(z, 2))
IO.inspect(Enum.drop(z, 4))
IO.inspect(Enum.take_while(z, &(&1 < 4)))
IO.inspect(Enum.chunk_every(z, 4))
IO.inspect(Enum.zip(z, ~w(a b c)))
IO.inspect(Enum.with_index(["a", "b"]))
IO.inspect(Enum.group_by(z, &rem(&1, 3)))
IO.inspect(Enum.split_with(z, &(&1 < 3)))
IO.inspect(Enum.frequencies(~w(a b a c a)))
IO.inspect(Enum.max(z))
IO.inspect(Enum.min_by(["ccc", "a", "bb"], &String.length/1))
IO.inspect(Enum.sort(z, :desc))
IO.inspect(Enum.sort_by(["bb", "a", "ccc"], &String.length/1))
IO.inspect(Enum.uniq([1, 2, 2, 3, 1]))
IO.inspect(Enum.flat_map([1, 2], &[&1, &1 * 10]))
IO.inspect(Enum.into([a: 1, b: 2], %{}))
IO.inspect(Enum.map(%{a: 1, b: 2}, fn {k, v} -> {k, v * 2} end))
IO.inspect(Enum.reduce(1..5, 1, &*/2))
IO.inspect(Enum.scan(z, &+/2))
IO.inspect(Enum.random([42]))
IO.inspect(Enum.reverse(z) |> Enum.join(","))
IO.inspect(Enum.at(z, -1))
IO.inspect(Enum.empty?([]))
IO.inspect(Enum.member?(z, 3))
IO.inspect(Enum.dedup([1, 1, 2, 2, 1]))
IO.inspect(Enum.unzip([{1, :a}, {2, :b}]))
IO.inspect(Enum.min_max(z))

Ausgabe:

[1, 4, 9, 16, 25, 36]
[4, 5, 6]
[1, 2, 3]
21
21
3
4
true
true
[1, 2]
[5, 6]
[1, 2, 3]
[[1, 2, 3, 4], [5, 6]]
[{1, "a"}, {2, "b"}, {3, "c"}]
[{"a", 0}, {"b", 1}]
%{0 => [3, 6], 1 => [1, 4], 2 => [2, 5]}
{[1, 2], [3, 4, 5, 6]}
%{"a" => 3, "b" => 1, "c" => 1}
6
"a"
[6, 5, 4, 3, 2, 1]
["a", "bb", "ccc"]
[1, 2, 3]
[1, 10, 2, 20]
%{a: 1, b: 2}
[a: 2, b: 4]
120
[1, 3, 6, 10, 15, 21]
42
"6,5,4,3,2,1"
6
true
true
[1, 2, 1]
{[1, 2], [:a, :b]}
{1, 6}

for-Comprehensions

IO.inspect(for x <- 1..5, do: x * x)
IO.inspect(for x <- 1..10, rem(x, 3) == 0, do: x)
IO.inspect(for x <- 1..3, y <- ~w(a b), do: {x, y})
IO.inspect(for {k, v} <- %{a: 1, b: 2}, into: %{}, do: {v, k})
IO.inspect(for <<c <- "abc">>, do: c + 1)
IO.inspect(for x <- [1, 2, 3], y <- [1, 2, 3], x < y, do: {x, y})
IO.inspect(for {:ok, v} <- [{:ok, 1}, {:error, 2}, {:ok, 3}], do: v)
IO.inspect(for n <- 1..3, into: "", do: Integer.to_string(n))
IO.inspect(for x <- 1..4, reduce: 0 do acc -> acc + x end)

Ausgabe:

[1, 4, 9, 16, 25]
[3, 6, 9]
[{1, "a"}, {1, "b"}, {2, "a"}, {2, "b"}, {3, "a"}, {3, "b"}]
%{1 => :a, 2 => :b}
'bcd'
[{1, 2}, {1, 3}, {2, 3}]
[1, 3]
"123"
10

Stream: faule Auswertung

Enum arbeitet sofort, Stream baut eine Rechenvorschrift und berechnet erst bei Bedarf. So gehen auch unendliche und sehr große Folgen:

fib = Stream.unfold({0, 1}, fn {a, b} -> {a, {b, a + b}} end)
IO.inspect(Enum.take(fib, 12))
 
geradequadrate =
  Stream.iterate(1, &(&1 + 1))
  |> Stream.filter(&(rem(&1, 2) == 0))
  |> Stream.map(&(&1 * &1))
IO.inspect(Enum.take(geradequadrate, 5))
 
IO.inspect(Stream.cycle([:a, :b]) |> Enum.take(5))
IO.inspect(1..1_000_000 |> Stream.map(&(&1 * 2)) |> Stream.take_every(250_000) |> Enum.to_list())
IO.inspect(Stream.repeatedly(fn -> 7 end) |> Enum.take(3))
IO.inspect(Stream.zip(1..3, ~w(a b c)) |> Enum.to_list())
 
stream = Stream.map([1, 2, 3], fn x -> IO.puts("verarbeite #{x}"); x * 10 end)
IO.puts("noch nichts passiert")
IO.inspect(Enum.to_list(stream))

Ausgabe:

[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
[4, 16, 36, 64, 100]
[:a, :b, :a, :b, :a]
[2, 500002, 1000002, 1500002]
'\a\a\a'
[{1, "a"}, {2, "b"}, {3, "c"}]
noch nichts passiert
verarbeite 1
verarbeite 2
verarbeite 3
[10, 20, 30]

Merke

  • if, unless, cond, case, with sind Ausdrücke; case und with nutzen Pattern Matching
  • Keine Schleifen: Enum.map/filter/reduce/... und for-Comprehensions
  • Stream ist lazy, ideal für große oder unendliche Folgen
  • Die Pipe verbindet alles zu lesbaren Ketten

Übungsaufgabe

Berechne die Summe der Quadrate aller ungeraden Zahlen bis 100 mit einer Pipe und Enum.

Quiz zur Selbstkontrolle

Weiter im Kurs

Zurück: Funktionen, Module und der Pipe-Operator

Weiter: Structs, Protokolle, Behaviours und Fehler

Alle Kapitel: Elixir im Überblick