Kurz: quadrat(x) = x^2 # Kurzform gruss(name = “Welt”; anrede = “Hallo”) = “anrede, (name)!” # Standardwert und Schlüsselwort-Argument
Teil des Kurses Julia
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]
3Der 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 MethodenMehrfachverteilung (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 ... endoderf(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
Wonach wählt Julia bei Mehrfachverteilung die Methode?
- Nach den Typen aller Argumente (richtig)
- Nur nach dem ersten Argument
- Nach dem Funktionsnamen allein
- Zufällig
Was bedeutet ! am Ende eines Funktionsnamens (sort!)?
- Die Funktion verändert ihr Argument (Konvention) (richtig)
- Die Funktion ist gefährlich
- Die Funktion ist privat
- Die Funktion ist asynchron
Weiter im Kurs
Zurück: Typen, Strings und Kontrollfluss
Weiter: Arrays, Tupel, Dicts und Mengen
Alle Kapitel: Julia im Überblick