
# Funktionen und Operatoren

Funktionen verarbeiten ihre Argumente zu einem Ergebnis, das sie beim Aufruf zurückliefern.

## Formen

Funktionen können in verschiedenen Formen definiert werden:

I.  Als `function ... end`-Block
```{julia}
function hyp(x,y)
    sqrt(x^2+y^2)
end
```
II.  Als "Einzeiler" 
```{julia}
hyp(x, y) = sqrt(x^2 + y^2)
```
III. Als anonyme Funktionen

```{julia}
(x, y) -> sqrt(x^2 + y^2)
```

  
### Block-Form und `return`


- Mit `return` wird die Abarbeitung der Funktion beendet und zum aufrufenden Kontext zurückgekehrt.
- Ohne `return` wird der Wert des letzten Ausdrucks als Funktionswert zurückgegeben.

Die beiden Definitionen

```julia
function xsinrecipx(x)
    if x == 0 
        return 0.0
    end 
    return x * sin(1/x)
end
```
und ohne das zweite explizite `return` in der letzten Zeile:  

```julia
function xsinrecipx(x)
    if x == 0 
        return 0.0
    end 
    x * sin(1/x)
end
```

sind also äquivalent.


- Eine Funktion, die "nichts" zurückgibt (_void functions_ in der C-Welt), gibt den Wert `nothing` vom Typ `Nothing` zurück. (So wie ein Objekt vom Typ `Bool` die beiden Werte `true` und `false` haben kann, so kann ein Objekt vom Typ `Nothing` nur einen einzigen Wert, eben `nothing`, annehmen.)
- Eine leere `return`-Anweisung ist äquivalent zu `return nothing`. 


```{julia}
function fn(x)
    println(x)
    return 
end

a = fn(2)
```


```{julia}
a
```


```{julia}
@show a typeof(a);
```


### Einzeiler-Form

Die Einzeilerform ist eine ganz normale Zuweisung, bei der links eine Funktion steht.

```julia
hyp(x, y) = sqrt(x^2 + y^2)
```

Julia kennt zwei Möglichkeiten, mehrere Anweisungen zu einem Block zusammenzufassen, der an Stelle einer Einzelanweisung stehen kann: 

- `begin ... end`-Block
- Eingeklammerte durch Semikolon getrennte Anweisungen.

In beiden Fällen ist der Wert des Blockes der Wert der letzten Anweisung.

Damit funktioniert auch
```julia
hyp(x, y) = (z = x^2; z += y^2; sqrt(z))
```

und
```julia
hyp(x, y) = begin 
                z = x^2
                z += y^2 
                sqrt(z) 
            end
```

### Anonyme Funktionen

Anonyme FUnktionen kann man  der Anonymität entreisen, indem man ihnen einen Namen zuweist. 
```julia
hyp = (x,y) -> sqrt(x^2 + y^2)
```
Ihre eigentliche Anwendung ist aber im Aufruf einer *(higher order)* Funktion, die eine Funktion als Argument erwartet. 

Typische Anwendungen sind `map(f, collection)`, welches eine Funktion auf alle Elemente einer Kollektion anwendet. In Julia funktioniert auch `map(f(x,y), collection1, collection2)`:

```{julia}
map( (x,y) -> sqrt(x^2 + y^2), [3, 5, 8], [4, 12, 15])
```

```{julia}
map( x->3x^3, 1:8 )
```

Ein weiteres Beispiel ist `filter(test, collection)`, wobei ein Test eine Funktion ist, die ein `Bool` zurückgibt.
```{julia}
filter(x -> ( x%3 == 0 && x%5 == 0), 1:100  )
```

## Argumentübergabe


- Beim Funktionsaufruf werden von den als Funktionsargumente zu übergebenden Objekten keine Kopien gemacht. Die Variablen in der Funktion verweisen auf die Originalobjekte.  Julia nennt dieses Konzept _pass_by_sharing_.  
- Funktionen können also ihre Argumente wirksam modifizieren, falls es sich um _mutable_ Objekte, wie z.B. `Vector`, `Array` handelt.
- Es ist eine Konvention in Julia, dass die Namen von solchen argumentmodifizierenden Funktionen mit einem Ausrufungszeichen enden. Weiterhin steht dann üblicherweise das Argument, das modifiziert wird, an erster Stelle und es ist auch der Rückgabewert der Funktion. 

```{julia}
V = [1, 2, 3]

W = fill!(V, 17)
                        # '===' ist Test auf Identität
@show  V  W  V===W;     # V und W benennen dasselbe Objekt 
```

```{julia}
function fill_first!(V, x)
        V[1] = x
        return V    
end                     

U = fill_first!(V, 42)

@show V   U   V===U;
```



## Varianten von Funktionsargumenten

- Es gibt Positionsargumente (1. Argument, 2. Argument, ....) und _keyword_-Argumente, die beim Aufruf durch ihren Namen angesprochen werden müssen. 
- Sowohl Positions- als auch _keyword_-Argumente können _default_-Werte haben. Beim Aufruf können diese Argumente weggelassen werden.
- Die Reihenfolge der Deklaration muss sein:
    1. Positionsargumente ohne Defaultwert, 
    2. Positionsargumente mit Defaultwert,
    3.   --- Semikolon ---,
    4. kommagetrennte Liste der Keywordargumente (mit oder ohne  Defaultwert) 
- Beim Aufruf können _keyword_-Argumente an beliebigen Stellen in beliebiger Reihenfolge stehen. Man kann sie wieder durch ein Semikolon von den Positionsargumenten abtrennen, muss aber nicht.

```{julia}
fa(x, y=42; a) = println("x=$x, y=$y, a=$a")

fa(6, a=4, 7)
fa(6, 7; a=4)
fa(a=-2, 6)
```

Eine Funktion nur mit _keyword_-Argumenten wird so deklariert:

```{julia}
fkw(; x=10, y) = println("x=$x, y=$y")

fkw(y=2)
```



## Funktionen sind ganz normale Objekte

 - Sie können zugewiesen werden


```{julia}
f2 = sqrt
f2(2)
```


- Sie können  als Argumente an Funktionen übergeben werden.


```{julia}
# sehr naive numerische Integration

function Riemann_integrate(f, a, b; NInter=1000)
    delta = (b-a)/NInter
    s = 0
    for i in 0:NInter-1
        s += delta * f(a + delta/2 + i * delta)
    end
    return s
end


Riemann_integrate(sin, 0, π)
```


- Sie können von Funktionen erzeugt und als Ergebnis `return`t werden.



```{julia}
function generate_add_func(x)
    function addx(y)
        return x+y
    end
    return addx
end
```


```{julia}
h =  generate_add_func(4)
```


```{julia}
h(1)
```


```{julia}
h(2), h(10)
```

Die obige Funktion `generate_add_func()`   lässt sich auch kürzer definieren. Der innere Funktionsname `addx()` ist sowieso lokal und außerhalb nicht verfügbar. Also kann man  eine anonyme  Funktion verwenden.

```{julia}
generate_add_func(x) = y -> x + y
```


## Zusammensetzung von Funktionen: die Operatoren $\circ$ und `|>`

- Die Zusammensetzung _(composition)_ von Funktionen kann auch mit dem Operator $\circ$ (`\circ + Tab`) geschrieben werden 

$$(f\circ g)(x) = f(g(x))$$


```{julia}
(sqrt ∘ + )(9, 16)
```


```{julia}
f = cos ∘ sin ∘ (x->2x)
f(.2)
```


```{julia}
@show map(uppercase ∘ first, ["ein", "paar", "grüne", "Blätter"]);
```


- Es gibt auch einen Operator, mit dem Funktionen "von rechts" wirken und zusammengesetzt werden können _(piping)_


```{julia}
25 |> sqrt
```


```{julia}
1:10 |> sum |> sqrt
```


- Natürlich kann man auch diese Operatoren 'broadcasten' (s. @sec-broadcast). Hier wirkt ein Vektor von Funktionen  elementweise auf einen Vektor von Argumenten:

```{julia}
["a", "list", "of", "strings"] .|> [length, uppercase, reverse, titlecase]
```

## Die `do`-Notation

Eine syntaktische Besonderheit zur Definition anonymer Funktionen als Argumente anderer Funktionen ist die `do`-Notation.

Sei `higherfunc(f,a,...)` eine Funktion, deren 1. Argument eine Funktion ist. 

Dann kann man `higherfunc()` auch ohne erstes Argument aufrufen und statt dessen die Funktion in einem unmittelbar folgenden `do`-Block definieren:

```julia
higherfunc(a, b) do x, y
   Körper von f(x,y)
end
```

Am Beispiel von `Riemann_integrate()` sieht das so aus:


```{julia}
# das ist dasselbe wie Riemann_integrate(x->x^2, 0, 2)

Riemann_integrate(0, 2) do x x^2 end
```


Der Sinn besteht natürlich in der Anwendung mit komplexeren Funktionen, wie diesem aus zwei Teilstücken zusammengesetzten Integranden:
```{julia}
r = Riemann_integrate(0, π) do x
        z1 = sin(x)
        z2 = log(1+x)
        if x > 1 
            return z1^2
        else
            return 1/z2^2
        end
    end
```

## Funktionsartige Objekte

Durch Definition einer geeigneten Methode für einen Typ kann man beliebige Objekte *callable* machen, d.h., sie anschließend wie Funktionen aufrufen.

```{julia}
# struct speichert die Koeffiziente eines Polynoms 2. Grades
struct Poly2Grad 
    a0::Float64
    a1::Float64
    a2::Float64
end

p1 = Poly2Grad(2,5,1)
p2 = Poly2Grad(3,1,-0.4)
```

Die folgende Methode macht diese Struktur `callable`.
```{julia}
function (p::Poly2Grad)(x)
    p.a2 * x^2 + p.a1 * x + p.a0
end
```
Jetzt kann man die Objekte, wenn gewünscht, auch wie Funktionen verwenden.

```{julia}
@show p2(5)  p1(-0.7)  p1;
```



## Operatoren und spezielle Formen

- Infix-Operatoren wie `+,*,>,∈,...` sind  Funktionen.



```{julia}
+(3, 7)
```


```{julia}
f = +
```


```{julia}
f(3, 7)
```

- Auch Konstruktionen wie `x[i]`, `a.x`, `[x; y]` werden vom Parser zu Funktionsaufrufen umgewandelt.

:::{.narrow}

|            |               |
| :-:        | :------------ |
| x[i]   |    getindex(x, i) |
| x[i] = z    |  setindex!(x, z, i) |
| a.x      |     getproperty(a, :x)   | 
| a.x = z |  setproperty!(a, :x, z)   |
| [x; y;...]   | vcat(x, y, ...)     |
  :Spezielle Formen [(Auswahl)](https://docs.julialang.org/en/v1/manual/functions/#Operators-With-Special-Names)

:::

(Der Doppelpunkt vor einer Variablen macht diese zu einem Symbol.)

:::{.callout-note}
Für diese Funktionen kann man eigene Methoden implementieren. Zum Beispiel könnten bei einem eigenen Typ das Setzen eines Feldes (`setproperty!()`) die Gültigkeit des Wertes prüfen oder weitere Aktionen veranlassen. 

Prinzipiell können `get/setproperty` auch Dinge tun, die gar nichts mit einem tatsächlich vorhandenen Feld der Struktur zu tun haben. 
:::

## Update-Form

Alle arithmetischen Infix-Operatoren haben eine update-Form: Der Ausdruck
```julia
x = x ⊙ y
```
kann auch geschrieben werden als
```julia
x ⊙= y
```

Beide Formen sind semantisch äquivalent. Insbesondere wird in beiden Formen der Variablen `x` ein auf der rechten Seite geschaffenes neues Objekt zugewiesen. 

Ein Speicherplatz- und Zeit-sparendes __in-place-update__ eines Arrays/Vektors/Matrix ist möglich entweder durch explizite Indizierung
```julia
for i in eachindex(x)
    x[i] += y[i]
end
```
oder durch die dazu semantisch äquivalente _broadcast_-Form (s. @sec-broadcast):
```julia
x .= x .+ y
```



## Vorrang und Assoziativität von Operatoren {#sec-vorrang}

Zu berechnende Ausdrücke 

```{julia}
 -2^3+500/2/10==8 && 13 > 7 + 1 || 9 < 2
```

werden vom Parser in eine Baumstruktur überführt.
```{julia}
using TreeView

walk_tree(Meta.parse("-2^3+500/2/10==8 && 13 > 7 + 1 || 9 < 2"))
```


- Die Auswertung solcher Ausdrücke wird durch 
   - Vorrang _(precedence)_ und 
   - Assoziativität geregelt.
- 'Vorrang' definiert, welche Operatoren stärker binden im Sinne von "Punktrechnung geht vor Strichrechnung".
- 'Assoziativität' bestimmt die Auswertungsreihenfolge bei gleichen oder gleichrangigen Operatoren.
- [Vollständige Dokumentation](https://docs.julialang.org/en/v1/manual/mathematical-operations/#Operator-Precedence-and-Associativity)

### Assoziativität

Sowohl Addition und Subtraktion als auch Multiplikation und Divison sind jeweils gleichrangig und linksassoziativ, d.h. es wird von links ausgewertet.

```{julia}
200/5/2      # wird von links ausgewertet als (200/5)/2 
```


```{julia}
200/2*5      # wird von links ausgewertet als (200/2)*5
```

Zuweisungen wie `=`, `+=`, `*=`,... sind gleichrangig und rechtsassoziativ.

```{julia}
x = 1
y = 10

# wird von rechts ausgewertet:  x += (y += (z = (a = 20)))

x += y +=  z = a = 20 

@show x y z a;
```

Natürlich kann man die Assoziativität in Julia auch abfragen. Die entsprechenden Funktionen werden nicht expizit aus dem `Base`-Modul exportiert, deshalb muss man den Modulnamen beim Aufruf angeben. 


```{julia}
for i in (:/, :+=, :(=), :^)
    a = Base.operator_associativity(i)
    println("Operation $i is  $(a)-assoziative")
end
```

Also ist der Potenzoperator rechtsassoziativ.
```{julia}
2^3^2    # rechtsassoziativ,  = 2^(3^2) 
```

### Vorrang

- Julia ordnet den Operatoren Vorrangstufen von 1 bis 17 zu: 



```{julia}
for i in (:+, :-, :*, :/, :^, :(=))
    p = Base.operator_precedence(i)
    println("Vorrang von  $i = $p") 
end
```


- 11 ist kleiner als 12, also geht 'Punktrechnung vor Strichrechnung'
- Der Potenz-Operator `^` hat eine höhere _precedence_.
- Zuweisungen haben die kleinste _precedence_


```{julia}
#   Zuweisung hat kleinsten Vorrang, daher Auswertung als  x = (3 < 4)

x = 3 < 4     
x
```


```{julia}
(y = 3) < 4   # Klammern schlagen natürlich jeden Vorrang
y
```

Nochmal zum Beispiel vom Anfang von  @sec-vorrang:

```{julia}
-2^3+500/2/10==8 && 13 > 7 + 1 || 9 < 2
```


```{julia}
for i ∈ (:^, :+, :/, :(==), :&&, :>, :|| )
    print(i, " ")
    println(Base.operator_precedence(i))
end
```
Nach diesen Vorrangregeln wird der Beispielausdruck also wie folgt ausgewertet:

```{julia}
((-(2^3)+((500/2)/10)==8) && (13 > (7 + 1))) || (9 < 2)
```
(Das entspricht natürlich dem oben gezeigten *parse-tree*)

Es gilt also für den Vorrang:

> Potenz > Multiplikation/Division > Addition/Subtraktion > Vergleiche > logisches && > logisches || > Zuweisung

Damit wird ein Ausdruck wie 
```julia
 a = x <= y + z && x > z/2 
``` 
sinnvoll ausgewertet als `a = ((x <= (y+z)) && (x < (z/2)))`


- Eine Besonderheit sind noch
 
    - unäre Operatoren, also insbesondere `+` und `-` als Vorzeichen  
    - _juxtaposition_, also Zahlen direkt vor Variablen oder Klammern ohne `*`-Symbol
   
   Beide haben Vorrang noch vor Multiplikation und Division. 

:::{.callout-important}
Damit ändert sich die Bedeutung von Ausdrücken, wenn man _juxtaposition_ anwendet:
```{julia}
1/2*π,  1/2π
```   
:::   
   
- Im Vergleich zum Potenzoperator `^` gilt (s. [https://discourse.julialang.org/t/confused-about-operator-precedence-for-2-3x/8214/7](https://discourse.julialang.org/t/confused-about-operator-precedence-for-2-3x/8214/7) ):
   
   >  Unary operators, including juxtaposition, bind tighter than ^ on the right but looser on the left.

Beispiele:
```{julia}
-2^2    # -(2^2)
```


```{julia}
x = 5
2x^2    # 2(x^2)
```


```{julia}
2^-2    # 2^(-2)
```


```{julia}
2^2x  # 2^(2x)
```


- Funktionsanwendung  `f(...)` hat  Vorrang vor allen Operatoren


```{julia}
sin(x)^2 === (sin(x))^2   # nicht sin(x^2)
```

### Zusätzliche Operatoren

Der [Julia-Parser](https://github.com/JuliaLang/julia/blob/master/src/julia-parser.scm#L13-L31) definiert für zahlreiche Unicode-Zeichen einen Vorrang auf Vorrat, so dass diese Zeichen von Paketen und selbstgeschriebenem Code als Operatoren benutzt werden können.  

So haben z.B.
```julia
 ∧ ⊗ ⊘ ⊙ ⊚ ⊛ ⊠ ⊡ ⊓ ∗ ∙ ∤ ⅋ ≀ ⊼ ⋄ ⋆ ⋇ ⋉ ⋊ ⋋ ⋌ ⋏ ⋒ ⟑ ⦸ ⦼ ⦾ ⦿ ⧶ ⧷ ⨇ ⨰ ⨱ ⨲ ⨳ ⨴ ⨵ ⨶ ⨷ ⨸ ⨻ ⨼ ⨽ ⩀ ⩃ ⩄ ⩋ ⩍ ⩎ ⩑ ⩓ ⩕ ⩘ ⩚ ⩜ ⩞ ⩟ ⩠ ⫛
```

den Vorrang 12 wie Multiplikation/Division (und sind wie diese linksassoziativ) 
und z.B.
```julia
 ⊕ ⊖ ⊞ ⊟ |++| ∪ ∨ ⊔ ± ∓ ∔ ∸ ≏ ⊎ ⊻ ⊽ ⋎ ⋓ ⧺ ⧻ ⨈ ⨢ ⨣ ⨤ ⨥ ⨦ ⨧ ⨨ ⨩ ⨪ ⨫ ⨬ ⨭ ⨮ ⨹ ⨺ ⩁ ⩂ ⩅ ⩊ ⩌ ⩏ ⩐ ⩒ ⩔ ⩖ ⩗  
```
haben den Vorrang 11 wie Addition/Subtraktion.



