EMZETT.
Login

Kurz: quadrat(x) = x^2 # Kurzform gruss(name = “Welt”; anrede = “Hallo”) = “anrede, (name)!” # Standardwert und Schlüsselwort-Argument

Teil des Kurses Julia

Kapitel 3 von 8 im Kurs Julia (Abschnitt „Das Herz von Julia“). Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

Funktionen

function addiere(a, b)
    return a + b
end
 
quadrat(x) = x^2                       # Kurzform
gruss(name = "Welt"; anrede = "Hallo") = "$anrede, $(name)!"    # Standardwert und Schlüsselwort-Argument
 
function minmax(v)
    return minimum(v), maximum(v)      # mehrere Werte (Tupel)
end
 
summe(zahlen...) = sum(zahlen)         # beliebig viele Argumente
 
println(addiere(2, 3), " ", quadrat(7))
println(gruss(), " ", gruss("Mia"), " ", gruss("Tom"; anrede = "Hi"))
lo, hi = minmax([4, 8, 15, 16, 23, 42])
println(lo, " ", hi)
println(summe(1, 2, 3, 4), " ", summe([5, 6]...))
 
# Anonyme Funktionen und Höhere Funktionen
println(map(x -> x * 2, [1, 2, 3]))
println(filter(iseven, 1:10))
println(reduce(+, 1:5), " ", foldl(-, 1:4), " ", sum(abs2, [3, 4]))
println((x -> x + 1)(41))
println(map(sqrt, [4, 9, 16]), " ", sqrt.([4, 9, 16]))      # Punktschreibweise: elementweise
println([1, 2, 3] .+ 10, " ", [1, 2, 3] .* [4, 5, 6], " ", (1:3) .^ 2)
println((sin ∘ cos)(0.0))                                   # Funktionskomposition
println([1, 2, 3] |> sum |> sqrt)                           # Pipe
 
# Funktionen mit "!" verändern ihre Argumente
liste = [3, 1, 2]
sort!(liste)
push!(liste, 10)
println(liste)
 
# Rekursion
fakultaet(n) = n <= 1 ? 1 : n * fakultaet(n - 1)
fib(n) = n < 2 ? n : fib(n - 1) + fib(n - 2)
println(fakultaet(10), " ", fakultaet(big(25)), " ", [fib(i) for i in 0:10])
 
# Closure
function zaehler()
    n = 0
    return () -> (n += 1)
end
z = zaehler()
z(); z()
println(z())

Ausgabe:

5 49
Hallo, Welt! Hallo, Mia! Hi, Tom!
4 42
10 11
[2, 4, 6]
[2, 4, 6, 8, 10]
15 -8 25
42
[2.0, 3.0, 4.0] [2.0, 3.0, 4.0]
[11, 12, 13] [4, 10, 18] [1, 4, 9]
0.8414709848078965
2.449489742783178
[1, 2, 3, 10]
3628800 15511210043330985984000000 [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
3

Der Punkt (broadcasting) wendet jede Funktion elementweise an: f.(v), v .+ w, a .== b. Das ist eines der mächtigsten Werkzeuge in Julia. Namen mit ! am Ende sind Konvention für Funktionen, die Argumente verändern.

Typen annotieren

Typangaben bei Argumenten sind optional, steuern aber die Mehrfachverteilung (siehe unten) und machen Fehler früh sichtbar:

halbiere(x::Int) = x ÷ 2
halbiere(x::Float64) = x / 2
verdopple(x::Number) = 2x
laenge(s::AbstractString) = length(s)
summe_liste(v::Vector{<:Real}) = sum(v)
 
println(halbiere(7), " ", halbiere(7.0), " ", verdopple(2.5), " ", laenge("Julia"))
println(summe_liste([1, 2, 3]), " ", summe_liste([1.5, 2.5]))
try
    halbiere("text")
catch e
    println(typeof(e), " für ", e.args)
end
println(length(methods(halbiere)), " Methoden")

Ausgabe:

3 3.5 5.0 5
6 4.0
MethodError für ("text",)
2 Methoden

Mehrfachverteilung (multiple dispatch)

In Julia wird die Methode anhand der Typen aller Argumente gewählt, nicht nur des ersten (wie bei Klassen in OOP). Dasselbe Konzept erklärt, warum Julia so schnell und zugleich so erweiterbar ist: Jede Funktion ist eine Sammlung von Methoden, und der Compiler erzeugt für jede Typkombination optimierten Code.

abstract type Tier end
struct Hund <: Tier; name::String end
struct Katze <: Tier; name::String end
struct Vogel <: Tier; name::String end
 
begruesse(t::Tier) = "$(t.name) sagt: ..."
begruesse(t::Hund) = "$(t.name) sagt: Wuff"
begruesse(t::Katze) = "$(t.name) sagt: Miau"
 
trifft(a::Hund, b::Katze) = "$(a.name) jagt $(b.name)"
trifft(a::Katze, b::Hund) = "$(a.name) faucht $(b.name)"
trifft(a::Tier, b::Tier) = "$(a.name) und $(b.name) ignorieren sich"
 
hund, katze, vogel = Hund("Rex"), Katze("Mimi"), Vogel("Tweety")
for t in (hund, katze, vogel)
    println(begruesse(t))
end
println(trifft(hund, katze))
println(trifft(katze, hund))
println(trifft(hund, vogel))
println(trifft(vogel, vogel))
 
# Eigene Typen mit Standardfunktionen "verheiraten"
struct Vektor2
    x::Float64
    y::Float64
end
import Base: +, *, show, ==, abs
+(a::Vektor2, b::Vektor2) = Vektor2(a.x + b.x, a.y + b.y)
*(k::Number, a::Vektor2) = Vektor2(k * a.x, k * a.y)
abs(a::Vektor2) = sqrt(a.x^2 + a.y^2)
==(a::Vektor2, b::Vektor2) = a.x == b.x && a.y == b.y
show(io::IO, a::Vektor2) = print(io, "(", a.x, ", ", a.y, ")")
 
v = Vektor2(3, 4)
println(v + v, " ", 2 * v, " ", abs(v), " ", v == Vektor2(3, 4))
println(sum([Vektor2(1, 1), Vektor2(2, 2), Vektor2(3, 3)]))

Ausgabe:

Rex sagt: Wuff
Mimi sagt: Miau
Tweety sagt: ...
Rex jagt Mimi
Mimi faucht Rex
Rex und Tweety ignorieren sich
Tweety und Tweety ignorieren sich
(6.0, 8.0) (6.0, 8.0) 5.0 true
(6.0, 6.0)

Weil Operatoren wie + ganz normale Funktionen sind, kann man sie für eigene Typen erweitern (Base-Funktionen mit import Base: + oder Base.:+). Pakete aus verschiedenen Quellen arbeiten so nahtlos zusammen.

Merke

  • function ... end oder f(x) = ...; mehrere Rückgabewerte als Tupel; ... sammelt/entpackt
  • Broadcasting mit Punkt: f.(v), v .+ w
  • Funktionen mit ! verändern ihre Argumente (Konvention)
  • Typannotationen (x::Int) steuern die Methodenauswahl
  • Mehrfachverteilung: Die Methode wird nach allen Argumenttypen gewählt, Operatoren sind erweiterbar

Übungsaufgabe

Definiere flaeche für die Typen Kreis und Rechteck (als structs) mit Mehrfachverteilung.

Quiz zur Selbstkontrolle

)?

  • Die Funktion wird auf jedes Element angewendet (Broadcasting) (richtig)
  • Ein Dezimalpunkt
  • Ein Kommentar
  • Eine Typangabe

Weiter im Kurs

Zurück: Typen, Strings und Kontrollfluss

Weiter: Arrays, Tupel, Dicts und Mengen

Alle Kapitel: Julia im Überblick