

# Grundlagen der Syntax

## Namen von Variablen, Funktionen, Typen etc.

- Namen können Buchstaben, Ziffern, den Unterstrich `_` und das Ausrufezeichen `!` enthalten.
- Das erste Zeichen muss ein Buchstabe oder ein Unterstrich sein.
- Groß- und Kleinbuchstaben werden unterschieden: `Nmax` und `NMAX` sind verschiedene Variablen.
- Als Zeichensatz wird [Unicode](https://home.unicode.org/) verwendet. Damit stehen über 150 Schriften und zahlreiche Symbole zur Verfügung.
- Es gibt eine kurze [Liste reservierter Schlüsselwörter](https://docs.julialang.org/en/v1/base/base/#Keywords): `if, then, function, true, false,...`

:::{.callout-tip}  
## Beispiel

zulässig: `i, x, Ω, x2, DieUnbekannteZahl, neuer_Wert, 🎷, Zähler, лічильник, einself!!!!,...`

unzulässig: `Uwe's_Funktion, 3achsen, A#B, $this_is_not_Perl, true,...` 
:::

----

:::{.callout-note }
## Anmerkung 

Neben den *reserved keywords* der Kernsprache sind zahlreiche weitere Funktionen und Objekte vordefiniert, wie z.B. die mathematischen Funktionen `sqrt(), log(), sin()`.
Diese Definitionen finden sich in dem Modul `Base`, welches Julia beim Start automatisch lädt. 
Namen aus `Base` können umdefiniert werden, solange sie noch nicht verwendet wurden:

```{julia}
#| error: true  
log = 3
1 + log
```
Jetzt ist natürlich der Logarithmus kaputt:
```{julia}
#| error: true
x = log(10)
```
(siehe auch <https://stackoverflow.com/questions/65902105/how-to-reset-any-function-in-julia-to-its-original-state>)
:::

## Anweisungen

- Im Normalfall enthält eine Zeile eine Anweisung.
- Wenn eine Anweisung am Zeilenende als unvollständig erkennbar ist durch 
    - offene Klammern
    - Operationszeichen,
    
  dann wird die nächste Zeile als Fortsetzung aufgefasst.
- Mehrere Anweisungen pro Zeile können durch Semikolon getrennt werden.
- Im interaktiven Betrieb (REPL oder Notebook) unterdrückt ein Semikolon nach der letzten Anweisung die Ausgabe des Ergebnisses dieser Anweisung.

:::{.callout-tip}
## Beispiel 

Im interaktiven Betrieb wird der Wert der letzten Anweisung auch ohne explizites `print()` ausgegeben:
```{julia}
println("Hallo 🌍!")
x = sum([i^2 for  i=1:10])
```
Das Semikolon unterdrückt das:
```{julia}
println("Hallo 🌍!")
x = sum([i^2 for  i=1:10]);
```
:::

---------


:::{.callout-warning }

Bei mehrzeiligen Anweisungen muss die fortzusetzende Zeile mit einer offenen Operation oder Klammer enden: 

```{julia}
x = sin(π/2) +
   3 * cos(0)
```

Also geht das Folgende schief, aber leider **ohne eine Fehlermeldung**!
```{julia}
#| error: true
#| warning: true
x = sin(π/2)
  + 3 * cos(0)
println(x)
```  
Hier wird das `+` in der zweiten Zeile als Präfix-Operator (Vorzeichen) interpretiert. Damit sind 1. und 2. Zeile jeweils für sich vollständige, korrekte Ausdrücke (auch wenn die 2. Zeile natürlich völlig nutzlos ist) und werden auch so abgearbeitet. 

Moral: Wenn man längere Ausdrücke auf mehrere Zeilen aufteilen will, sollte man immer eine Klammer aufmachen. Dann ist egal, wo der Zeilenumbruch ist:
```{julia}
x = ( sin(π/2) 
   + 3 * cos(0) )
println(x)
```     
:::


## Kommentare

Julia kennt 2 Arten von Kommentaren im Programmtext:

```{julia}
# Einzeilige Kommentare beginnen mit einem Doppelkreuz.

x = 2    # alles vom '#' bis zum Zeilenende ist ein Kommentar und wird ignoriert. x = 3
```

```{julia}
#= 
   Ein- und mehrzeilige Kommentare können zwischen #= ...  =# eingeschlossen werden.
   Dabei sind verschachtelte Kommentare möglich. 
   #= 
      d.h., anders als in C/C++/Java endet der Kommentar nicht mit dem ersten 
      Kommentar-Endezeichen, sondern die #=...=# - Paare wirken wie Klammern.    
   =# 
   Der automatische 'syntax highlighter' weiss das leider noch nicht, wie die wechselnde 
   Graufärbung dieses Kommentars zeigt.     
=#


x #= das ist ein seltener Variablenname! =#  = 3 
```


## Datentypen Teil I 

- Julia ist eine [stark typisierte](https://de.wikipedia.org/wiki/Starke_Typisierung) Sprache.   Alle Objekte haben einen Typ. Funktionen/Operationen erwarten Argumente mit dem richtigen Typ.
- Julia ist eine  [dynamisch typisierte](https://de.wikipedia.org/wiki/Dynamische_Typisierung) Sprache. Variablen haben keinen Typ. Sie sind Namen, die durch Zuweisung `x = ...` an Objekte gebunden werden können.
- Wenn man vom „Typ einer Variablen“ spricht, meint man den Typ des Objektes, das der Variablen gerade zugewiesen ist.
- Funktionen/Operatoren können verschiedene *methods* für verschiedene Argumenttypen implementieren.
- Abhängig von den konkreten Argumenttypen wird dann bei Verwendung einer Funktion entschieden, welche Methode benutzt wird ([*dynamic dispatch*](https://en.wikipedia.org/wiki/Dynamic_dispatch)).


Einfache Basistypen  sind z.B.:

```
Int64, Float64, String, Char, Bool
```


```{julia}
x = 2
x, typeof(x), sizeof(x) 
```

```{julia}
x = 0.2
x, typeof(x), sizeof(x) 
```

```{julia}
x = "Hallo!"
x, typeof(x), sizeof(x) 
```


```{julia}
x = 'Ω'
x, typeof(x), sizeof(x) 
```

```{julia}
x = 3 > π
x, typeof(x), sizeof(x) 
```

- `sizeof()` liefert die Größe eines Objekts oder Typs in Bytes (1 Byte = 8 Bit)
- 64bit  Ganzzahlen und 64bit Gleitkommazahlen entsprechen dem Befehlssatz moderner Prozessoren und sind daher die numerischen Standardtypen.
- Zeichen/*chars* `'A'` und Zeichenketten/*strings* `"A"` der Länge 1 sind verschiedene Objekte.   

## Ablaufsteuerung

### `if`-Blöcke

- Ein `if`-Block *kann*  **beliebig viele** `elseif`-Zweige und als letztes maximal  **einen** `else`-Zweig enthalten.
- Der Block hat einen Wert, den Wert der letzten ausgeführten Anweisung.

```{julia}
x = 33
y = 44
z = 34

if x < y && z != x                     # elseif- und else-Blöcke sind optional
    println("yes")
    x += 10
elseif x < z                           # beliebig viele elseif-Blöcke
    println(" x is smaller than z")
elseif x == z+1                           
    println(" x is successor of z")
else                                   # maximal ein else-Block 
    println("Alles falsch")
end                                    # Wert des gesamten Blocks ist der Wert der 
                                       # letzten ausgeführten Auswertung
```

Kurze Blöcke kann man in eine Zeile schreiben:
```{julia}
if x > 10 println("x is larger than 10") end 
```

Der Wert eines `if`-Blocks kann natürlich zugewiesen werden:
```{julia}
y = 33
z = if y > 10 
      println("y is larger than 10") 
      y += 1 
    end
z    
```

### Auswahloperator (ternary operator) `test ? exp1 : exp2`

```{julia}
x = 20
y = 15
z = x < y ? x+1 : y+1
```
ist äquivalent zu

```{julia}
z = if x < y
        x+1
    else
        y+1
    end
```

## Vergleiche, Tests, Logische Operationen

### Arithmetische Vergleiche

- `==`
- `!=`, `≠`
- `>`
- `>=`, `≥`
- `<`
- `<=`, `≤`

Wie üblich, ist der Test auf Gleichheit `==` vom Zuweisungsoperator `=` zu unterscheiden. Vergleichen lässt sich so gut wie alles:

```{julia}
"Aachen" < "Leipzig", 10 ≤ 10.01, [3,4,5] < [3,6,2]
```

Nun ja, fast alles:

```{julia}
3 < "vier"
```

Die Fehlermeldung zeigt ein paar Grundprinzipien von Julia:

- Operatoren sind auch nur Funktionen: `x < y`  wird zum Funktionsaufruf `isless(x, y)`.
- Funktionen (und damit Operatoren) können verschiedene *methods* für verschiedene Argumenttypen implementieren. 
- Abhängig von den konkreten Argumenttypen wird beim Aufruf der Funktion entschieden, welche Methode benutzt wird ([*dynamic dispatch*](https://en.wikipedia.org/wiki/Dynamic_dispatch)).

Man kann sich alle Methoden zu einer Funktion  anzeigen lassen. Das gibt einen Einblick in das komplexe Typssystem von Julia:

```{julia}
methods(<)
```

Zuletzt noch: Vergleiche können gekettet werden.

```{julia}
10 < x ≤ 100  # das ist äquivalent zu
              #   10 < x && x ≤ 100

```


### Tests 
Einge Funktionen vom Typ `f(c::Char) -> Bool`

```{julia}
isnumeric('a'), isnumeric('7'), isletter('a')
```


und vom Typ `f(s1::String, s2::String) -> Bool`

```{julia}
contains("Lampenschirm", "pensch"), startswith("Lampenschirm", "Lamb"), endswith("Lampenschirm", "rm")
```

- Die Funktion `in(item, collection) -> Bool` testet, ob `item` in `collection` ist. 
- Sie hat auch das Alias ` ∈(item, collection)` und 
- sowohl `in` als auch `∈` können auch als Infix-Operatoren geschrieben werden.

```{julia}
x = 3
x in [1, 2, 3, 4, 5]
``` 
```{julia}
x ∈ [1, 2, 33, 4, 5]
```

###  Logische Operationen: `&&`, `||`, `!`


```{julia}
3 < 4 && !(2 > 8) && !contains("aaa", "b")
```

#### Bedingte Auswertung (_short circuit evaluation_)

- in `a && b` wird `b` nur ausgewertet, wenn `a == true`
- in `a || b` wird `b` nur ausgewertet, wenn `a == false`

(i) Damit kann `if test statement end` auch als `test && statement` geschrieben werden.

(ii) Damit kann `if !test statement end`  als `test || statement` geschrieben werden.

Als Beispiel^[aus der [Julia-Dokumentation](https://docs.julialang.org/en/v1/manual/control-flow/#Short-Circuit-Evaluation)] hier eine Implementierung der Fakultätsfunktion *(factorial)*:
```{julia}
function fact(n::Int)
    n >= 0 || error("n must be non-negative")
    n == 0 && return 1
    n * fact(n-1)
end

fact(5)
```



Natürlich kann man alle diese Tests auch Variablen vom Typ `Bool` zuordnen und
diese Variablen können als Tests in `if`- und `while`-Blöcken verwendet werden:

```{julia}
x = 3 < 4
y = 5 ∈ [1, 2, 5, 7]
z = x && y
if z                        # äquivalent zu:  if 3 < 4  &&  5 in [1,2,5,7]
    println("Stimmt alles!")
end
```

- In Julia müssen alle Tests in einem logischen Ausdruck vom Typ `Bool` sein.
- Es gibt keine implizite Konvertierung à la *"0 is false and 1 (or anything != 0) is true"*
- Wenn `x` ein numerischer Typ ist, dann muss daher das C-Idiom `if(x)` als `if x != 0` geschrieben werden.
- Es gibt eine Ausnahme zur Unterstützung der _short circuit evaluation_:
     - bei den Konstrukten `a && b && c...` bzw `a || b || c...` muss der letzte Teilausdruck nicht vom Typ `Bool` sein, wenn diese Konstrukte nicht als Tests in  `if` oder `while` verwendet werden:


```{julia}
z = 3 < 4 && 10 < 5 &&  sqrt(3^3)
z, typeof(z)
```

```{julia}
z = 3 < 4 && 10 < 50 &&  sqrt(3^3)
z, typeof(z)
```

## Schleifen *(loops)*

###  Die `while` ("solange")-Schleife


Syntax:
```
while *condition*
   *loop body*
end
```
Eine Reihe von Anweisungen (der Schleifenkörper) wird immer wieder abgearbeitet, solange eine Bedingung erfüllt ist.


```{julia}
i = 1          # typischerweise braucht der Test der 
               # while-Schleife eine Vorbereitung ...
while i < 10   
   println(i)
   i += 2      # ... und ein update
end
```

Der Körper einer `while`- und `for`-Schleife  kann die Anweisungen `break` und `continue` enthalten. `break` stoppt die Schleife, `continue` überspringt den Rest des Schleifenkörpers und beginnt sofort mit dem nächsten Schleifendurchlauf.
```{julia}
i = 0

while i<10
   i += 1

   if i == 3
      continue     # beginne sofort nächsten Durchlauf,
   end             #  überspringe Rest des Schleifenkörpers

   println("i = $i")

   if i ≥ 5
      break        # breche Schleife ab
   end
end

println("Fertig!")
```

Mit `break` kann man auch Endlosschleifen verlassen:

```{julia}
i = 1

while true
    println(2^i)
    i += 1
    if i > 8 break end
end
```

###  `for`-Schleifen

Syntax:

```
for *var* in *iterable container*
   *loop body*
end
```

Der Schleifenkörper wird für alle Items aus einem Container durchlaufen. 

Statt `in` kann immer auch $\in$ verwendet werden. Im Kopf einer `for`-Schleife kann auch `=` verwendet werden.

```{julia}
for i ∈ ["Mutter", "Vater", "Tochter"]
    println(i)
end
```


Oft benötigt man einen numerischen Schleifenzähler. Dafür gibt es das *range*-Konstrukt. Die einfachsten Formen sind 
`Start:Ende` und `Start:Schrittweite:Ende`. 

```{julia}
endwert = 5

for i ∈ 1:endwert
    println(i^2)
end
```

```{julia}
for i = 1:5.5  print(" $i") end
```


```{julia}
for i = 1:2:14 print("  $i") end
```

```{julia}
for k = 14 : -2.5 : 1 print("   $k") end
```




#### Geschachtelte Schleifen _(nested loops)_

Ein `break` beendet die innerste Schleife.

```{julia}
for i = 1:3
    for j = 1:3
        println( (i,j) )
        if j == 2
            break
        end
    end
end
```

Man kann *nested loops* auch in einer `for`-Anweisung zusammenfassen. Dann beendet ein `break` die Gesamtschleife.

```{julia}
for i = 1:3, j=1:3     # im Prinzip dasselbe wie oben, aber:
    println( (i,j) )
    if j == 2
        break          # break bricht hier die Gesamtschleife ab
    end
end
```


:::{.callout-important .titlenormalxx}
## **Wichtig:** Die Semantik ist völlig anders, als bei C-artigen `for`-Schleifen!

**Bei jedem Schleifendurchlauf wird die Laufvariable neu mit dem nächsten Element aus dem Container initialisiert.**


```{julia}
for i = 1:5
    print(i," ... ")
    i += 2
    println(i)
end
```

-------

Die C-Semantik von `for(i=1; i<5; i++)`  entspricht der `while`-Schleife:
```
i = 1
while i<5
   *loop body*   # hier kann auch wirksam an i rumgepfuscht werden
   i += 1
end
```
:::

## Unicode 

Julia verwendet Unicode als Zeichensatz. Damit können für Variablen, Funktionen etc auch Bezeichner aus nicht-lateinischen Schriften (zB Kyrillisch, Koreanisch, Sanskrit, Runen,
Emoji,...) verwendet werden. Die Frage, wie man solche Zeichen in seinem Editor eingeben kann und ob der verwendete Bildschirm-Font sie darstellen kann, ist nicht Julias Problem.

- Einige Unicode-Zeichen, z.B. `≤, ≠, ≥, π, ∈, √`, können anstelle von `<=, !=, >=, pi, in, sqrt` verwendet werden. 

- über 3000 Unicode-Zeichen können in Julia in einer LaTeX-ähnlichen Weise mit der Tab-Vervollständigung eingegeben werden. 
  - `\alpha<TAB>` wird zu `α`, 
  - `\euler<TAB>` wird zu `ℯ`  (Eulersche Zahl `exp(1)`, [spezielles Schreibschrift-e, `U+0212F`](https://www.htmlsymbol.com/unicode-code/212f.html))
  - `\le<TAB>`  wird zu   `≤`,
  - `\in<TAB>`   wird zu `∈`,
  - `\:rainbow:<TAB>` wird zu `🌈`       
  [Hier geht es zur Liste.](https://docs.julialang.org/en/v1/manual/unicode-input/)

## Eigenheiten und Stolperfallen der Syntax

- Man kann den Multiplikationsoperator `*` nach einer numerischen Konstanten weglassen, wenn eine Variable, Funktion oder öffnende Klammer folgt.
  ```
   z = 3.4x + 2(x+y) + xy
  ```

ist daher korrektes Julia. Beachte allerdings, dass der Term `xy` als eine Variable mit dem Namen  xy interpretiert wird __und nicht__ als Produkt von x und y!

- Diese Regel hat ein paar Tücken:

Das funktioniert wie erwartet:
```{julia}
e = 7
3e 
```

Hier wird die Eingabe als Gleitkommazahl interpretiert -- und `3E+2` oder `3f+2` (Float32) ebenso. 

```{julia}
3e+2 
```

Ein Leerzeichen schafft Eindeutigkeit:

```{julia}
3e + 2 
```

Das funktioniert:

```{julia}
x = 4
3x + 3 
```

...und das nicht. `0x`, `0o`, `0b` wird als Anfang einer Hexadezimal-, Oktal- bzw. Binärkonstanten interpretiert.

```{julia}
3y + 0x  
```


- Es gibt noch ein paar andere Fälle, bei denen die sehr kulante Syntax zu Überraschungen führt.


```{julia}
Wichtig  = 21
Wichtig! = 42       # Bezeichner können auch ein ! enthalten
(Wichtig, Wichtig!)
```



```{julia}
Wichtig!=88
```


Julia interpretiert das als Vergleich `Wichtig != 88`.

Leerzeichen helfen:


```{julia}
Wichtig! = 88
Wichtig!
```

- Operatoren der Form `.*`, `.+`,... haben in Julia eine spezielle Bedeutung (*broadcasting*, d.h.,  vektorisierte Operationen).  

```{julia}
1.+2.
```

Wieder gilt: Leerzeichen schaffen Klarheit!

```{julia}
1. + 2.
```




