Listing 14
julia> f(x::Int32) = 3*x f (generic function with 1 method) julia> f(3.0) ERROR: MethodError: no method matching f(::Float64) [...]
Die Begriffe Funktion und Methode haben in Julia sehr präzise Bedeutungen, die deutlich von denen in anderen Programmiersprachen abweichen. Funktion meint den Bezeichner einer Funktion. Eine Methode hingegen meint die konkrete Implementation einer Funktion auf Argumente eines bestimmten Typs. Definieren Sie in der Sitzung aus Listing 14 f() auch noch für ein Argument vom Typ Float32, erhält die Funktion f() zwei Methoden (Listing 15).
Listing 15
[...]
julia> f(x::Float32) = 3*x
f (generic function with 2 methods)
Die Funktion methods() gibt Auskunft über die Anzahl und Signatur der zu einer Funktion gehörenden Methoden (Listing 16).
Listing 16
julia> methods(f)
# 2 methods for generic function "f":
[1] f(x::Float32) in Main at REPL[2]:1
[2] f(x::Int32) in Main at REPL[1]:1
Auch + ist eine Funktion, wie der Ausdruck julia> +(1, 2) verdeutlicht. Sie besitzt insgesamt 166 Methoden, die Sie mit julia> methods(+) auflisten. Möchten Sie den Typ des Rückgabewerts einer Funktion angeben, tippen Sie julia> f(x::Int32)::Int32 = 3*x.
Anders als bei dem von Java oder C++ bekannten Überladen entscheidet Julia nicht beim Kompilieren, welche Methode zum Zuge kommt, sondern zur Laufzeit. Dieses Verfahren heißt Multiple Dispatch (Mehrfachverteilung).
Methoden können sich dabei genauso auf konkrete wie auf abstrakte Typen und Mischungen von beiden beziehen. Ein abstrakter Typ ist zum Beispiel Signed, von dem alle ganzzahligen Typen mit Vorzeichen abgeleitet sind. Der abstrakte Typ Integer dient als Supertyp aller Ganzzahlen (Listing 17). Mit supertype() bringen Sie den direkten Vorfahren eines Typs in Erfahrung. Der Typ Any ist der Supertyp aller Typen.
Listing 17
julia> supertype(Int64) Signed julia> supertype(Int8) Signed julia> supertype(Signed) Integer
Listing 18 definiert neun verschiedene Methoden für eine Funktion g() und zeigt einige Beispiele für Methodenaufrufe. Versuchen Sie nachzuvollziehen, welche Methode für welche Argumente gültig ist.
Listing 18
g(x::String) = println("String")
g(x::Float32) = println("Float32")
g(x::Unsigned) = println("Unsigned")
g(x::Integer) = println("Integer")
g(x::AbstractFloat) = println("AbstractFloat")
g(x::Real) = println("Real")
g(x::Any, y::Float32) = println("Any, Float32")
g(x::Float32, y::Any) = println("Float32, Any")
g(x::Any, y::Any) = println("Any, Any")
g(10)
#> Integer
g(UInt(64))
#> Unsigned
g("hallo")
#> String
g(22.0)
#> AbstractFloat
g(7, 8.9)
#> Any, Any
g(7, Float32(8.9))
#> Any, Float32
Komplexere Funktionen
Listing 19 definiert mit make_adder() eine Funktion, die Funktionen mittels anonymer Definition erzeugt und als Rückgabewert zurückliefert. In den Zeilen 3 und 4 liefert make_adder() die Funktionen add7() und add12(). Die Zeilen 6 und 8 zeigen die Anwendung.
Zeile 11 speichert ein Array unter dem Namen arr. Die Funktion map() erwartet als erstes Argument eine anonyme Funktion oder einen Funktionsnamen und als zweites ein Array oder einen Vertreter eines anderen aufzählbaren Typs. Die Zeilen 13 und 16 zeigen die Anwendung von map() mit den zur Laufzeit erzeugten Funktionen und (in Zeile 19) die Anwendung mit einer anonymen Funktion.
Listing 19
make_adder(val) = x -> x+val add7 = make_adder(7) add12 = make_adder(12) add7(3) #> 10 add12(12) #> 24 arr = [1, 2, 3] map(add7, arr) #> [8, 9, 10] map(add12, arr) #> [13, 14, 15] map(x -> 2*x, arr) #> [2, 4, 6]
Königsdisziplin Arrays
Der flexible Umgang mit Arrays beliebiger Dimension gehört zu den starken Seiten von Julia. Die Funktionen zum Erzeugen und Modifizieren von Arrays böten Stoff für einen eigenen Artikel. Listing 19 stellte bereits die einfache Literal-Schreibweise für Arrays vor. Wenn Sie sie im interaktiven Modus verwenden, erhalten Sie ein Ergebnis wie in den Zeilen 1 bis 5 von Listing 20.





