Julia Kontrollfluss
Kontrollflussanweisungen werden implementiert, indem eine oder mehrere Bedingungsanweisungen in einem Programm festgelegt werden. Wenn die Bedingung wahr ist, wird der angegebene Programmcode ausgeführt; wenn die Bedingung falsch ist, wird anderer angegebener Code ausgeführt.
Julia bietet eine große Anzahl von Kontrollflussanweisungen:
Zusammengesetzte Ausdrücke:beginund;。
Bedingte Ausdrücke:if-elseif-elseund?:(ternärer Operator).
Kurzschlussauswertung:logische Operatoren&&(und) und||(oder), sowie verkettete Vergleiche.
Schleifenanweisungen:Schleifen:whileundfor。
Ausnahmebehandlung:try-catch、errorundthrow。
-
Task (Koroutine):yieldto。
Zusammengesetzte Ausdrücke
begin ... endEin Ausdruck kann nacheinander mehrere Teilausdrücke auswerten und den Wert des letzten Teilausdrucks zurückgeben:
Beispiel
x = 1
y = 2
x + y
end
3
Weil diese Ausdrücke sehr kurz sind, können sie einfach in eine Zeile gesetzt werden, was der Ursprung der;Kette ist:
Beispiel
3
In der tatsächlichen Verwendung ist es nicht erforderlich, dass der begin-Block mehrzeilig ist oder dass die ;-Kette einzeilig ist:
Beispiel
3
julia> (x = 1;
y = 2;
x + y)
3
Bedingte Ausdrücke
Ein bedingter Ausdruck kann anhand des Werts eines booleschen Ausdrucks entscheiden, welcher Codeblock ausgeführt wird.
if-elseif-else-Syntax:
/*Wenn der boolesche Ausdruck1wahr ist, ausführen*/
elseif boolean_expression 2
/*Wenn der boolesche Ausdruck2wahr ist, ausführen*/
elseif boolean_expression 3
/*Wenn der boolesche Ausdruck3wahr ist, ausführen*/
else
/*Wenn keine der oben genannten Bedingungen wahr ist, ausführen*/
Eineif-Anweisung kann von einer optionalenelse if...else-Anweisung gefolgt werden, mit der mehrere Bedingungen getestet werden können.
Bei der Verwendung von if...else if...else-Anweisungen sind folgende Punkte zu beachten:
- Auf ein if kann null oder ein else folgen, und das else muss nach allen else ifs stehen.
- Ein if kann von null oder mehr else ifs gefolgt werden, und die else ifs müssen vor dem else stehen.
- Sobald ein else if erfolgreich übereinstimmt, werden die anderen else ifs oder else nicht getestet.
Nachfolgend eine Analyse der if-elseif-else-Bedingungssyntax:
Beispiel
println("x is less than y")
elseif x > y
println("x ist größer als y")
else
println("x ist gleich y")
end
Wenn der Ausdruck x < y wahr ist, wird der entsprechende Codeblock ausgeführt; andernfalls wird der bedingte Ausdruck x > y ausgewertet. Wenn er wahr ist, wird der entsprechende Codeblock ausgeführt; wenn kein Ausdruck wahr ist, wird der else-Codeblock ausgeführt.
Beispiel
if x < y
println("x is less than y")
elseif x > y
println("x ist größer als y")
else
println("x ist gleich y")
end
end
Teste (generische Funktion mit 1 Methode)
julia> test(1, 2)
x ist kleiner als y
julia> test(2, 1)
x ist größer als y
julia> test(1, 1)
x ist gleich y
Ternär
Der ternäre Operator?:ist ähnlich zur if-elseif-else-Syntax:
a ? b : cIn?Der vorherige Ausdruck a ist ein bedingter Ausdruck. Wenn Bedingung a wahr ist, wertet der ternäre Operator Ausdruck b vor dem : aus; wenn Bedingung a falsch ist, führt er Ausdruck c nach dem : aus.
Hinweis:?und:Leerzeichen davor und danach sind zwingend erforderlich. Ein Ausdruck wie a?b:c ist kein gültiger ternärer Ausdruck (aber Zeilenumbrüche nach ? und : sind erlaubt).
Beispiel
julia> println(x < y ? "kleiner als" : "nicht kleiner als")
kleiner als
julia> x = 1; y = 0;
julia> println(x < y ? "kleiner als" : "nicht kleiner als")
nicht kleiner als
Wenn der Ausdruck x < y wahr ist, liefert der gesamte ternäre Operator die Zeichenkette "kleiner als"; andernfalls liefert er die Zeichenkette "nicht kleiner als".
Verkettung und Verschachtelung ternärer Operatoren:
Beispiel
x > y ? "x ist größer als y" : "x ist gleich y")
Teste (generische Funktion mit 1 Methode)
julia> test(1, 2)
x ist kleiner als y
julia> test(2, 1)
x ist größer als y
julia> test(1, 1)
x ist gleich y
Um die verkettete Wertübergabe zu erleichtern, werden die Operatoren von rechts nach links verbunden.
Ähnlich wie bei if-elseif-else werden die Ausdrücke vor und nach:nur dann entsprechend ausgeführt, wenn der Bedingungsausdruck wahr oder falsch ist:
Beispiel
v (generische Funktion mit 1 Methode)
julia> 1 < 2 ? v("ja") : v("nein")
ja
"ja"
julia> 1 > 2 ? v("ja") : v("nein")
nein
"nein"
Kurzschlussauswertung
In Julia entsprechen die&&- und||-Operatoren dem logischen"und"und"oder"Operationen.
- Im Ausdruck
a && b, der Teilausdruckbwird nur ausgeführt, wennaisttrue; wenn a false ist, wird direkt false zurückgegeben. - Im Ausdruck
a || b, der Teilausdruckbwird nur ausgeführt, wennaistfalse; wenn a true ist, wird a direkt zurückgegeben.
&& Beispiel
┌ Warnung: Eine ungerade Zahl!
└ @ Main REPL[5]:1
julia> isodd(4) && @warn("Eine ungerade Zahl!")
false
|| Beispiel
wahr
julia> isodd(4) || @warn("Eine ungerade Zahl!")
┌ Warnung: Eine ungerade Zahl!
└ @ Main REPL[8]:1
Sowohl && als auch || hängen von der rechten Seite ab, aber && hat eine höhere Priorität als ||. Siehe das Beispiel:
Beispiel
t (generische Funktion mit 1 Methode)
julia> f(x) = (println(x); false)
f (generische Funktion mit 1 Methode)
julia> t(1) && t(2)
1
2
wahr
julia> t(1) && f(2)
1
2
false
julia> f(1) && t(2)
1
false
julia> f(1) && f(2)
1
false
julia> t(1) || t(2)
1
wahr
julia> t(1) || f(2)
1
wahr
julia> f(1) || t(2)
1
2
wahr
julia> f(1) || f(2)
1
2
false
Schleifenanweisungen
Schleifenanweisungen verwendenwhileundforzwei Schlüsselwörter zur Implementierung.
Nachfolgend ist ein Beispiel für einewhile-Schleife:
Beispiel
julia> while i <= 5
println(i)
global i += 1
end
1
2
3
4
5
Die while-Schleife wertet den Bedingungsausdruck (i <= 5) aus. Solange er wahr ist, führt sie den Rumpf der while-Schleife weiter aus. Wenn der Bedingungsausdruck falsch ist, das heißt, wenn i = 6, endet die Schleife.
Die for-Schleife ist bequemer zu verwenden. Das obige, mit einer for-Schleife implementierte Beispiel lautet wie folgt:
Beispiel
println(i)
end
1
2
3
4
5
Hier,1:5ist ein Range-Objekt, das die Zahlenfolge 1, 2, 3, 4, 5 repräsentiert.
Die for-Schleife iteriert über diese Werte und weist dabei der Variable i jeweils einen Wert zu.
Ein sehr wichtiger Unterschied zwischen der for-Schleife und der vorherigen while-Schleife ist der Gültigkeitsbereich (Scope), also die Sichtbarkeit von Variablen. Wenn die Variable i nicht in einem anderen Gültigkeitsbereich eingeführt wurde, ist sie innerhalb der for-Schleife nur innerhalb der for-Schleife sichtbar und außerhalb oder danach nicht sichtbar. Sie benötigen eine neue interaktive Sitzungsinstanz oder einen neuen Variablennamen, um dieses Verhalten zu testen:
Beispiel
println(j)
end
1
2
3
4
5
julia> j
ERROR: UndefVarError: j not defined
Im Allgemeinen können for-Schleifenkomponenten verwendet werden, um über jeden beliebigen Container zu iterieren. In diesem Fall wird im Vergleich zu = das andere (aber vollständig identische) Schlüsselwortinoder∈wird häufiger verwendet, weil es den Code klarer macht:
Beispiel
println(i)
end
1
4
0
julia> for s ∈ ["foo","bar","baz"]
println(s)
end
foo
bar
baz
Der Einfachheit halber können wir eine while-Schleife beenden, bevor die Testbedingung falsch wird, oder eine for-Schleife stoppen, bevor das Ende des iterierten Objekts erreicht ist. Dies kann mit dem Schlüsselwortbreak erfolgen.:
Beispiel
julia> while true
println(i)
if i >= 5
break
end
global i += 1
end
1
2
3
4
5
julia> for j = 1:1000
println(j)
if j >= 5
break
end
end
1
2
3
4
5
Ohne das Schlüsselwort break würde die obige while-Schleife niemals von selbst enden, und die for-Schleife würde bis 1000 iterieren. Diese Schleifen können alle break verwenden, um vorzeitig zu beenden.
In manchen Szenarien ist es notwendig, die aktuelle Iteration direkt zu beenden und sofort zur nächsten Iteration überzugehen. DascontinueSchlüsselwort kann verwendet werden, um dies zu erreichen:
Beispiel
if i % 3 != 0
continue
end
println(i)
end
3
6
9
Dies ist ein etwas konstruiertes Beispiel, denn wir könnten dieselbe Funktionalität prägnanter erreichen, indem wir die Bedingung negieren und den println-Aufruf in einen if-Block setzen. In praktischen Anwendungen gibt es mehr Code, der nach continue ausgeführt wird, und es kann mehrere Stellen geben, an denen continue aufgerufen wird.
Mehrere verschachtelte for-Schleifen können zu einer einzigen äußeren Schleife zusammengefasst werden, mit der sich das kartesische Produkt ihrer iterierten Objekte bilden lässt:
Beispiel
println((i, j))
end
(1, 3)
(1, 4)
(2, 3)
(2, 4)
Mit dieser Syntax können Iterationsvariablen weiterhin normal mit Schleifenvariablen zur Indizierung verwendet werden. Zum Beispiel istfor i = 1:n, j = 1:izulässig, aber die Verwendung einer break-Anweisung innerhalb einer Schleife bricht aus der gesamten verschachtelten Schleife aus, nicht nur aus der inneren Schleife. Jedes Mal, wenn die innere Schleife ausgeführt wird, werden den Variablen (i und j) ihre aktuellen Iterationswerte zugewiesen. Zuweisungen an i sind also für nachfolgende Iterationen nicht sichtbar:
Beispiel
println((i, j))
i = 0
end
(1, 3)
(1, 4)
(2, 3)
(2, 4)
Wenn dieses Beispiel neu geschrieben wird, indem für jede Variable das Schlüsselwort for verwendet wird, ist die Ausgabe anders: Die zweite und vierte Variable enthalten 0.
Sie können zip verwenden, um in einer einzigen for-Schleife gleichzeitig über mehrere Container zu iterieren:
Beispiel
println((j,k))
end
(1, 4)
(2, 5)
(3, 6)
julia> for x in zip(0:15, 100:110, 200:210)
println(x)
end
(0, 100, 200)
(1, 101, 201)
(2, 102, 202)
(3, 103, 203)
(4, 104, 204)
(5, 105, 205)
(6, 106, 206)
(7, 107, 207)
(8, 108, 208)
(9, 109, 209)
(10, 110, 210)
// Inkonsistente Anzahlen von Elementen behandeln
julia> for x in zip(0:10, 100:115, 200:210)
println(x)
end
(0, 100, 200)
(1, 101, 201)
(2, 102, 202)
(3, 103, 203)
(4, 104, 204)
(5, 105, 205)
(6, 106, 206)
(7, 107, 207)
(8, 108, 208)
(9, 109, 209)
(10, 110, 210)
Die Verwendung von zip erstellt einen Iterator, der ein Tupel von Unteriteratoren für die ihm übergebenen Container ist. Der zip-Iterator iteriert in der Reihenfolge über alle Unteriteratoren und wählt bei der ii-ten Iteration der for-Schleife das ii-te Element jedes Unteriterators aus. Sobald ein Unteriterator erschöpft ist, stoppt die for-Schleife.
for-Anweisungen können mehrere Schleifenbedingungen verschachteln, wobei Kommas,zur Trennung verwendet werden:
Beispiel
@show (n, m)
end
(n, m) = (1, 1)
(n, m) = (1, 2)
(n, m) = (1, 3)
(n, m) = (1, 4)
(n, m) = (1, 5)
(n, m) = (2, 1)
(n, m) = (2, 2)
(n, m) = (2, 3)
(n, m) = (2, 4)
(n, m) = (2, 5)
(n, m) = (3, 1)
(n, m) = (3, 2)
(n, m) = (3, 3)
(n, m) = (3, 4)
(n, m) = (3, 5)
(n, m) = (4, 1)
(n, m) = (4, 2)
(n, m) = (4, 3)
(n, m) = (4, 4)
(n, m) = (4, 5)
(n, m) = (5, 1)
(n, m) = (5, 2)
(n, m) = (5, 3)
(n, m) = (5, 4)
(n, m) = (5, 5)
Komprehensionen
Eine Komprehension ist eine besondere Datenverarbeitungsmethode, mit der aus einer Datenfolge eine neue Datenfolgenstruktur konstruiert werden kann.Das Format ist wie folgt:
[out_exp_res for out_exp in input_list]
oder
[AusdruckforVariableinListeifcondition]
[out_exp_res for out_exp in input_list if condition]
Beispiel
5-element Array{Int64,1}:
1
4
9
16
25
Wir können auch den Typ der Elemente angeben, die wir generieren möchten:
Beispiel
5-element Array{Complex,1}:
1 + 0im
4 + 0im
9 + 0im
16 + 0im
25 + 0im
Arrays durchlaufen
Manchmal möchten wir über jedes Element eines Arrays iterieren, einschließlich der Indexnummer des Elements.
Julia bietet dieenumerate(iter)Funktion; der Parameteriterist ein iterierbares Objekt. Diese Funktion erzeugt die Indexnummern sowie den Wert, der jedem Index entspricht.
Beispiel
julia> for (index, value) in enumerate(a)
println("$index $value")
end
1 a
2 b
3 c
julia> arr = rand(0:9, 4, 4)
4×4 Array{Int64,2}:
7 6 5 8
8 6 9 4
6 3 0 7
2 3 2 4
julia> [x for x in enumerate(arr)]
4×4 Array{Tuple{Int64,Int64},2}:
(1, 7) (5, 6) (9, 5) (13, 8)
(2, 8) (6, 6) (10, 9) (14, 4)
(3, 6) (7, 3) (11, 0) (15, 7)
(4, 2) (8, 3) (12, 2) (16, 4)
Ausnahmebehandlung
Während der Programmausführung kann eine Funktion, wenn eine unerwartete Bedingung auftritt, möglicherweise keinen sinnvollen Wert an ihren Aufrufer zurückgeben. In solchen Fällen ist es am besten, die unerwartete Bedingung das Programm beenden zu lassen und Debug-Fehlerinformationen auszugeben, was es Entwicklern erleichtert, ihren Code zu behandeln.
Durch dietry / catchAnweisung können Ausnahmen bequem behandelt werden.
Zum Beispiel löst im folgenden Code die Quadratwurzelfunktion sqrt eine Ausnahme aus. Durch try / catch können wir die Ausnahmeinformationen genau ausgeben.
Beispiel
sqrt("ten")
catch e
println("Sie müssen eine Zahl eingeben")
end
Sie müssen eine Zahl eingeben
finally-Klausel
Wenn Sie mit Zustandsänderungen programmieren oder Ressourcen wie Dateien verwenden, ist es oft notwendig, Aufräumarbeiten (wie das Schließen von Dateien) auszuführen, wenn der Code endet. Da Ausnahmen dazu führen können, dass einige Codeblöcke vor dem normalen Abschluss beendet werden, kann dies die oben genannte Arbeit erschweren. Das Schlüsselwort finally bietet eine Möglichkeit, einen bestimmten Codeabschnitt auszuführen, wenn ein Codeblock verlassen wird, unabhängig davon, wie er verlassen wird.
Hier ist ein Beispiel, das sicherstellt, dass eine geöffnete Datei geschlossen wird:
Beispiel
try
# Datei f bearbeiten
finally
close(f)
end
Wenn der Kontrollfluss den try-Block verlässt (zum Beispiel, wenn return auftritt oder wenn er normal endet), wird close(f) ausgeführt. Wenn der try-Block aufgrund einer Ausnahme verlassen wird, breitet sich die Ausnahme weiter aus. Der catch-Block kann zusammen mit try und finally verwendet werden. In diesem Fall wird der finally-Block ausgeführt, nachdem catch den Fehler behandelt hat.
throw-Funktion
Wir können throw verwenden, um Ausnahmen explizit zu erzeugen.
Wenn eine Funktion beispielsweise nur für nichtnegative Zahlen definiert ist, können Sie throw verwenden, um einen DomainError auszulösen, wenn der Eingabeparameter negativ ist.
Beispiel
f (generische Funktion mit 1 Methode)
julia> f(1)
0.36787944117144233
julia> f(-1)
FEHLER: DomainError mit -1:
Argument muss nichtnegativ sein
Stacktrace:
[1] f(::Int64) bei ./none:1
Beachten Sie, dass DomainError ohne Klammern keine Exception, sondern ein Exception-Typ ist. Wir müssen ihn aufrufen, um ein Exception-Objekt zu erhalten:
Beispiel
wahr
julia> typeof(DomainError) <: Exception
false
Darüber hinaus akzeptieren einige Exception-Typen ein oder mehrere Argumente für die Fehlerberichterstattung:
Beispiel
FEHLER: UndefVarError: x nicht definiert
Wir können diesen Mechanismus leicht mit einem benutzerdefinierten Exception-Typ implementieren, indem wir dem Muster von UndefVarError folgen:
Beispiel
var::Symbol
end
julia> Base.showerror(io::IO, e::MyUndefVarError) = print(io, e.var, " nicht definiert")
Task (Koroutine)
Task (Koroutine) bietet nichtlokale Ablaufsteuerung, die es ermöglicht, zwischen vorübergehend angehaltenen Berechnungsaufgaben zu wechseln. Wir werden dies im späteren Kapitel über asynchrone Programmierung ausführlich behandeln.
Andere Erweiterungen