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())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")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)]))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
Aufgabe
Definiere flaeche für die Typen Kreis und Rechteck (als structs) mit Mehrfachverteilung.