Skip to content
Snippets Groups Projects

Exercise 3

Merged YingXing requested to merge exercise-2 into main
Files
2
+ 757
0
%% Cell type:markdown id:f59de749-6cd1-4f77-b89e-57b110d83858 tags:
## Konstruktoren
Bevor wir in diesem Notebook weitermachen, benötigen wir noch eine letzte Zutat aus Strukturen: benutzerdefinierte Konstruktoren.
Ein Konstruktor ist eine spezielle Funktion, die aufgerufen wird, um ein Objekt zu erstellen. Wir nehmen als Beispiel eine `Datum` Struktur.
%% Cell type:code id:c171ec1a-902b-4211-b250-373953f4c58c tags:
``` julia
struct Date
day::Integer
month::Integer
year::Integer
end
```
%% Cell type:markdown id:c39605b9-2667-4c9d-8b1f-f6505baa0838 tags:
Wir können ein neues Datum instanziieren, indem wir den Standardkonstruktor aufrufen.
%% Cell type:code id:63630a93-6d93-489a-9656-9ac6b5be86f5 tags:
``` julia
date = Date(1, 11, 2023)
@show date;
```
%% Cell type:markdown id:2cad81b4-b9df-42f3-a89b-8340036bbe8e tags:
Wenn wir jedoch mit dem Standardverhalten des Konstruktors nicht zufrieden sind, können wir unseren eigenen Konstruktor definieren, der ein benutzerdefiniertes Verhalten implementiert. Zum Beispiel könnten wir an einem Konstruktor interessiert sein, der eine Zeichenkette im Format `TT.MM.JJJJ` als Eingabe akzeptiert.
%% Cell type:code id:34e84e19-734a-42c0-b263-2dc25128c0cd tags:
``` julia
function Date(str::String)
parts = split(str, ".")
if length(parts) != 3
throw(ArgumentError("Invalid date format. Use DD.MM.YYYY"))
end
d = parse(Int, parts[1])
m = parse(Int, parts[2])
y = parse(Int, parts[3])
return Date(d, m, y)
end
date = Date("01.11.2023")
@show date;
```
%% Cell type:markdown id:915b71b1-c188-4c82-a2ff-810a58f39720 tags:
Alternativ können Konstruktoren auch innerhalb der Struktur definiert werden. Der Vorteil dieser Strategie ist, dass wir den Standardkonstruktor durch benutzerdefiniertes Verhalten ersetzen.
%% Cell type:code id:6dda7612-0cd2-471a-93e4-119a4e2f8f93 tags:
``` julia
struct ValidatedDate
day::Integer
month::Integer
year::Integer
function ValidatedDate(day, month, year)
@assert(day > 0, "day is negative")
@assert(month > 0, "month is negative")
@assert(year > 0, "year is negative")
new(day, month, year)
end
end
date = ValidatedDate(-1, 10, 2023)
```
%% Cell type:markdown id:89ab4f63-bd67-4cca-919a-f0d8f2bc88d1 tags:
### Übungen
- Schreibe einen benutzerdefinierten Konstruktor, der nur den Monat und das Jahr als Eingabeargumente akzeptiert und den Tag auf 1 setzt.
%% Cell type:code id:884e42ed-d340-410c-ad42-950e173807c8 tags:
``` julia
# TODO: implement your code here.
```
%% Cell type:markdown id:459a7d50-1138-4fc1-af46-a0bdb4335287 tags:
- Passe den `ValidatedDate` inneren Konstruktor so an, dass er überprüft, ob der angegebene Tag im angegebenen Monat existiert. Nehmen Sie an, dass der Februar immer 28 Tage hat.
%% Cell type:code id:eeb10ef4-0bb5-46e0-96b4-0fb98e4e1b3d tags:
``` julia
# TODO: implement your code here.
```
%% Cell type:markdown id:169512ca-868f-406d-b8c8-8e9174d25b64 tags:
- Optional: Passe das vorherige Ergebnis an und berücksichtige auch Schaltjahre.
`Jedes Jahr, das genau durch vier teilbar ist, ist ein Schaltjahr, mit Ausnahme von Jahren, die genau durch 100 teilbar sind. Diese Jahrhundertjahre sind jedoch Schaltjahre, wenn sie genau durch 400 teilbar sind. Zum Beispiel sind die Jahre 1700, 1800 und 1900 keine Schaltjahre, aber die Jahre 1600 und 2000 sind es.`
%% Cell type:code id:8b1dad51-d7cf-43aa-a2c8-9b3eaa4a1f4d tags:
``` julia
# TODO: implement your code here.
```
%% Cell type:markdown id:3ba487cb tags:
# Datentypen und Multiple Dispatch
In diesem Arbeitsblatt werden wir die grundlegenden Konzepte von Julias Typsystem erforschen und Multiple Dispatch verstehen, wobei wir uns konzentrieren auf:
- Abstrakte und Konkrete Typen
- Dynamische Typen
- Multiple Dispatch
Diese Konzepte verbessern Julias Leistung, insbesondere bei mathematischer und wissenschaftlicher Datenverarbeitung.
%% Cell type:markdown id:fe56dbef tags:
## Abstrakte and konkrete Typen
%% Cell type:markdown id:01604e4c tags:
Bevor wir den Multiple Dispatch von Funktionen und den Dispatch nach Typen besprechen, werfen wir kurz einen Blick auf Julias Typsystem.
Typen in Julia fallen in zwei Kategorien: **Abstrakt** und **konkret**.
- **Abstrakte Typen**: Können nicht instanziiert werden und dienen dazu, allgemeine Kategorien von Objekten darzustellen.
- **Konkrete Typen**: Können instanziiert werden und werden verwendet, um Objekte zu erstellen.
%% Cell type:markdown id:41e57dbd tags:
In Julia gibt es mehrere eingebaute Funktionen, die sich auf das Abfragen und Arbeiten mit Typen beziehen.
Hier eine Auswahl einiger wichtiger:
- `<:`: Der Subtyp-Operator, der verwendet wird, um zu überprüfen, ob ein Typ ein Subtyp eines anderen Typs ist.
- `isabstracttype(T)`: Überprüfen, ob T ein abstrakter Typ ist.
- `isconcretetype(T)`: Überprüfen, ob T ein konkreter Typ ist.
- `subtypes(T)`: Eine Liste aller unmittelbaren Subtypen des Typs T erhalten.
- `supertype(T)`: Den direkten Supertyp des Typs T erhalten.
%% Cell type:markdown id:da96b9b9 tags:
Abstrakte Typen wie `Integer` oder `Number` sind Supertypen von vielen anderen Typen, zum Beispiel:
%% Cell type:code id:50b00765 tags:
``` julia
@show Int32 <: Integer # Read:Int32 is a sub-type of Integer
@show UInt16 <: Integer # UInt16 is a sub-type of Integer
@show Float32 <: Integer # Float32 is not a sub-type of Integer
@show Float32 <: Number # Float32 is a sub-type of Number
@show Integer <: Number; # Integer is a sub-type of Nummber
```
%% Cell type:code id:329380e7 tags:
``` julia
# by transitivity:
@show Int32 <: Number
@show UInt16 <: Number
@show Number <: Number;
```
%% Cell type:markdown id:027b76c8 tags:
### Eigenschaften von Typen
Wir können Typ-Eigenschaften auf verschiedene Weisen überprüfen:
%% Cell type:code id:49228132 tags:
``` julia
@show isconcretetype(Int32);
```
%% Cell type:code id:9ffd1775 tags:
``` julia
@show isabstracttype(Integer);
```
%% Cell type:markdown id:2a316d56 tags:
Eine ausgefallene Möglichkeit ist sogar, einen Typ-Baum anzuzeigen:
%% Cell type:code id:f5a1fd53 tags:
``` julia
"""
print_type_tree(T, level=0, max_level=typemax(Int), prefix="", subtype_prefix="")
Print a tree structure that visualizes the type hierarchy rooted at `T`.
# Parameters
- `T`: The type which serves as the root of the tree to print.
- `level`: (Optional, default=`0`) An integer specifying the current recursion depth - typically not provided by the user.
- `max_level`: (Optional, default=`typemax(Int)`) An integer specifying the maximum recursion depth, i.e., how many levels deep into the type hierarchy to print.
- `prefix`: (Optional, default=`""`) A string used internally to format the tree structure - typically not provided by the user.
- `subtype_prefix`: (Optional, default=`""`) A string used internally to format the tree structure - typically not provided by the user.
# Usage
```julia
print_type_tree(Number, max_level=7)
```
"""
function print_type_tree(T, level=0, max_level=typemax(Int), prefix="", subtype_prefix="")
# Stop recursion if max level is reached
if level >= max_level
return
end
# Print current type
println(prefix, subtype_prefix, T)
# Obtain subtypes
subs = subtypes(T)
# Recursively print subtypes with adjusted prefix
for (i, subtype) in enumerate(subs)
new_prefix = " "
subtype_prefix = i < length(subs) ? "├── " : "└── "
# If the subtype has no further subtypes, avoid using the "|" in the prefix
if isempty(subtypes(subtype))
print_type_tree(subtype, level+1, max_level, prefix * " ", subtype_prefix)
else
print_type_tree(subtype, level+1, max_level, prefix * new_prefix, subtype_prefix)
end
end
end
```
%% Cell type:code id:41f4de49 tags:
``` julia
print_type_tree(Number)
```
%% Cell type:markdown id:bc4662dc tags:
**Übung**: Schreiben Sie Code, um alle Subtypen von `Number` zu bestimmen, unabhängig davon, ob sie abstrakt oder konkret sind.
%% Cell type:code id:39b8339f tags:
``` julia
# TODO: implement your code
```
%% Cell type:markdown id:3ad36e86 tags:
Um den Supertyp eines Typs zu erhalten, kann man `supertype` verwenden:
%% Cell type:code id:e666712d tags:
``` julia
@show supertype(Int64)
@show supertype(Signed)
@show supertype(Float16)
@show supertype(Float32)
@show supertype(Bool);
```
%% Cell type:code id:147db9b0 tags:
``` julia
function print_supertypes(T::Type)
println("Supertypes of $T:")
while T != Any
print(T, " ---> ")
T = supertype(T)
end
println(T) # Print Any, which is the ultimate supertype of all types
end
```
%% Cell type:code id:ed566f15 tags:
``` julia
print_supertypes(Int64);
```
%% Cell type:code id:1462bc0c tags:
``` julia
print_supertypes(Float64);
```
%% Cell type:markdown id:d16d0857 tags:
### Benutzerdefinierte Abstrakte und Konkrete Typen
%% Cell type:markdown id:882efff3 tags:
Man kann benutzerdefinierte abstrakte Typen mit `abstract type` definieren:
%% Cell type:code id:3ebf46f6 tags:
``` julia
abstract type Animal end # Abstract type
```
%% Cell type:markdown id:d63c0261 tags:
Dann können auch einige konkrete Typen erstellt werden:
%% Cell type:code id:eaab065e tags:
``` julia
struct Dog <: Animal # Concrete type inheriting from Animal
name::String
age::Int
end
struct Cat <: Animal # Another concrete type inheriting from Animal
name::String
age::Int
end
```
%% Cell type:markdown id:091a7905 tags:
In diesem Beispiel erstellen wir zwei konkrete Tiere, `Dog` und `Cat`.
Man kann `subtypes` verwenden, um alle Subtypen eines abstrakten oder konkreten Typs zu erhalten.
%% Cell type:code id:b8b7fca8 tags:
``` julia
subtypes(Animal)
```
%% Cell type:markdown id:a2f3177d tags:
Wiederum können wir mit `isabstracttype` und `isconcretetype` überprüfen:
%% Cell type:code id:3eced325 tags:
``` julia
@show isabstracttype(Animal)
@show isabstracttype(Dog)
@show isabstracttype(Cat)
@show isconcretetype(Animal)
@show isconcretetype(Dog)
@show isconcretetype(Cat);
```
%% Cell type:markdown id:4b08f9f7 tags:
Der Typ-Baum von `Animal` ist:
%% Cell type:code id:40debabd tags:
``` julia
print_type_tree(Animal)
```
%% Cell type:markdown id:87e2a153 tags:
Jetzt erstellen wir zwei Instanzen von den konkreten Typen:
%% Cell type:code id:6f0f092f tags:
``` julia
a_dog = Dog("Buddy", 3)
a_cat = Cat("Kitty", 2)
@show a_dog a_cat;
```
%% Cell type:markdown id:41ea3797 tags:
In Julia wird die Methode `isa` verwendet, um zu bestimmen, ob eine Instanz von einem bestimmten Typ ist, egal ob er abstrakt oder konkret ist:
%% Cell type:code id:263461b1 tags:
``` julia
@show isa(a_dog, Dog)
@show isa(a_dog, Animal)
@show isa(a_dog, Cat)
@show isa(a_cat, Cat);
```
%% Cell type:markdown id:ee934bc9 tags:
Die Methode `typeof` wird verwendet, um den Typ einer Instanz zu erhalten:
%% Cell type:code id:208aed02 tags:
``` julia
@show typeof(a_dog)
@show typeof(a_cat);
```
%% Cell type:markdown id:8dc6aef6 tags:
Wir können auch alle Supertypen von `Dog` und `Cat` erhalten:
%% Cell type:code id:b3ca83e6 tags:
``` julia
print_supertypes(Dog)
```
%% Cell type:code id:662a732f tags:
``` julia
print_supertypes(Cat)
```
%% Cell type:markdown id:d850444b tags:
### Übungen
Schreiben Sie Code, um einige abstrakte und konkrete Subtypen von `Animal` zu implementieren, und verwenden Sie `print_type_tree`, um den Typ-Baum anzusehen. Zum Beispiel können Sie den abstrakten Typ `Bird` implementieren und dann einige Arten von Vögeln.
%% Cell type:code id:2fbfbb88 tags:
``` julia
# TODO: implement your code
```
%% Cell type:markdown id:0add9f3d tags:
Welche der folgenden Typen sind Untertypen eines anderen? Versuchen Sie zuerst zu raten und überprüfen Sie dann mithilfe des Operators `<:`.
```julia
Float64 AbstractFloat Integer
Number AbstractArray Complex
Real Any Nothing
%% Cell type:code id:688afde5-1123-4adc-affd-687b966f387e tags:
``` julia
# TODO: implement your code
```
%% Cell type:markdown id:77bb1fe2 tags:
In Julia sind konkrete Typen immer ein Blatt des Typbaums, d.h., sie können nicht voneinander abgeleitet werden. Für jemanden, der mit C++ oder Python vertraut ist (wie einige von uns), mag dies zunächst einschränkend erscheinen, aber es entfernt eine Menge unnötiger Komplexität aus der Typenhierarchie. Wir werden jetzt keine weiteren Informationen geben, der Grund wird am Ende dieses Notebooks klarer sein.
Weitere Details finden Sie unter: [https://docs.julialang.org/en/v1/base/base/#Properties-of-Types](https://docs.julialang.org/en/v1/base/base/#Properties-of-Types).
%% Cell type:markdown id:64fc3a0b tags:
## Mehrfachdispatch (*multiple dispatch*)
Mehrfachdispatch ist wahrscheinlich das Schlüsselmerkmal von Julia, das es im Vergleich zu vielen anderen Sprachen unterscheidet und ihm die Fähigkeit verleiht, gleichzeitig flexibel und leistungsstark zu sein.
Um das Konzept des Mehrfachdispatchs vollständig zu verstehen, werden wir eine Analogie verwenden. Wir werden so tun, als ob eine Programmiersprache ein Werkzeug zum Schreiben eines Kochbuches ist. Ein Rezept kombiniert eine Zubereitungsmethode (zum Beispiel Backen, Braten) mit einer Zutat (zum Beispiel Kartoffeln, Karotten, Fisch).
- Die erste Möglichkeit besteht darin, das Buch nach Zubereitungsmethoden zu organisieren: Jedes Kapitel erklärt ausführlich eine Zubereitungsmethode. Zum Beispiel haben wir **Kapitel 1: Backen**, **Kapitel 2: Braten** und so weiter. Der Nachteil dieses Ansatzes ist, dass wir immer dann, wenn wir eine neue Zutat hinzufügen, mehrere Kapitel ändern müssen. Dieser Ansatz, der sich auf die Aktion und nicht auf die Zutaten konzentriert, ist typisch für funktionale Programmiersprachen.
- Die zweite Möglichkeit besteht darin, das Buch nach Zutaten zu organisieren: Jedes Kapitel konzentriert sich auf eine Zutat. Zum Beispiel haben wir **Kapitel 1: Kartoffeln**, **Kapitel 2: Fisch** und so weiter. Der Nachteil dieses Ansatzes ist, dass wir immer dann, wenn wir ein neues Rezept hinzufügen, erneut mehrere Kapitel ändern müssen. Dieser Ansatz konzentriert sich auf die Zutaten (Daten) und ist typisch für objektorientierte Programmierung, bei der wir etwas Ähnliches haben könnten wie:
```julia
struct Kartoffeln
function backen()
function braten()
end
struct Fisch
function backen()
function braten()
end
```
- Julia verfolgt einen dritten Ansatz namens **Mehrfachdispatch**, bei dem die Aktion von den Daten entkoppelt wird. In unserem hypothetischen Kochbuch haben wir Kapitel wie **Kapitel 1: Kartoffeln backen**, **Kapitel 2: Kartoffeln braten**, **Kapitel 3: Fisch backen**, **Kapitel 4: Fisch braten** und so weiter. Jedes dieser Kapitel enthält etwas Ähnliches wie:
```julia
function backen(kartoffeln::Kartoffeln)
function braten(kartoffeln::Kartoffeln)
function backen(fisch::Fisch)
function braten(fisch::Fisch)
```
Auf diese Weise erfordert das Hinzufügen eines neuen Rezepts für eine bestimmte Art von Lebensmittel keine Änderungen an bereits geschriebenen Dingen.
Quelle: [https://docs.julialang.org/en/v1/base/base/#Properties-of-Types](https://docs.julialang.org/en/v1/base/base/#Properties-of-Types)
%% Cell type:markdown id:0198d051 tags:
Kehren wir zur `mymult`-Funktion zurück und sehen, wie wir den Mehrfachdispatch verwenden können, um neue Funktionen zu implementieren:
%% Cell type:code id:c43dc846 tags:
``` julia
mymult(x, y) = x * y
```
%% Cell type:markdown id:a088c5fd tags:
Wir konnten diese Funktionen sicher mit einer Vielzahl von Typenkombinationen verwenden, aber einige Dinge funktionieren noch nicht:
%% Cell type:code id:65dbe501 tags:
``` julia
mymult(2, " abc")
```
%% Cell type:markdown id:1eda2f65 tags:
Angenommen, wir möchten den String `str` $n$-mal aneinanderreihen, indem wir ihn mit einer ganzen Zahl $n$ multiplizieren. In Julia ist diese Funktionalität bereits durch den Potenzoperator implementiert:
%% Cell type:code id:8c18999b tags:
``` julia
"abc"^4
```
%% Cell type:markdown id:13b4953e tags:
Aber der Argumentation halber nehmen wir an, wir möchten, dass `mymult("abc", 4)` und `mymult(4, "abc")` auf die gleiche Weise funktionieren. Wir definieren zwei spezielle Methoden:
%% Cell type:code id:fa0af502 tags:
``` julia
mymult(str::String, n::Integer) = str^n
mymult(n::Integer, str::String) = mymult(str, n)
```
%% Cell type:markdown id:ed42c2c2 tags:
In beiden Fällen bedeutet die Syntax `str::String` und `n::Integer`, dass die jeweilige Methode nur während der Zuordnung berücksichtigt wird, wenn das Argument `str` vom Typ `String` ist und `n` ein `Integer` (oder Untertyp) ist. Da Julia immer zur spezifischsten Methode dispatcht, falls mehrere Methoden übereinstimmen, ist dies alles, was wir tun müssen:
%% Cell type:code id:5fb0009b tags:
``` julia
mymult(2, " abc")
```
%% Cell type:code id:ee10ad21 tags:
``` julia
@which mymult(2, " abc")
```
%% Cell type:code id:91192540 tags:
``` julia
@which mymult("def ", UInt16(3))
```
%% Cell type:markdown id:a4ccf591 tags:
Beachten Sie, dass die vollständig generische Form:
```julia
mymult(x, y) = x * y
```
tatsächlich eine Abkürzung für:
```julia
mymult(x::Any, y::Any) = x * y
```
ist.
%% Cell type:markdown id:ed16db85 tags:
### Methoden
In Julia werden verschiedene Versionen einer Funktion, die mit unterschiedlichen Arten von Argumenten arbeiten, als **Methoden** bezeichnet. Sie können die Liste der Methoden sehen, indem Sie dies ausführen:
%% Cell type:code id:e52b3aee-2cbc-4cec-b131-204f9889b939 tags:
``` julia
methods(mymult)
```
%% Cell type:markdown id:c3f6143a tags:
Wenn Sie `methods` auf eine Kernfunktion wie `*` ausführen, erhalten Sie eine ziemlich lange Liste.
%% Cell type:code id:de3e4302 tags:
``` julia
methods(*)
```
%% Cell type:markdown id:9bd61de6 tags:
Das Makro `@which` kann verwendet werden, um herauszufinden, welche spezifische Methode gerade ausgeführt wird.
%% Cell type:code id:be365ee3 tags:
``` julia
println(@which "Hello"*"World!")
println(@which mymult("a", 3))
```
%% Cell type:markdown id:8fb4d5c3 tags:
Wir können auch einige Methoden für `Animal`, `Dog` und `Cat` hinzufügen:
%% Cell type:code id:bfb965d6 tags:
``` julia
# Define a generic function speak
speak(animal::Animal) = "Some generic animal noise"
# Use multiple dispatch to define method for specific types
speak(animal::Dog) = "Woof! I am $(animal.name), a dog."
speak(animal::Cat) = "Meow! I am $(animal.name), a cat."
```
%% Cell type:markdown id:c8c559e0 tags:
Dann testen wir es.
%% Cell type:code id:14bda70c tags:
``` julia
@show speak(a_dog)
@show speak(a_cat);
```
%% Cell type:markdown id:12b5d667 tags:
### Abstrakte Typen und Mehrfachdispatch
Um die Rolle abstrakter Typen im Mehrfachdispatch zu zeigen, kehren wir zu unserem Lebensmittelbeispiel zurück. Zunächst müssen wir die Hierarchie der Lebensmitteltypen klären. Dafür können wir abstrakte Typen verwenden.
%% Cell type:code id:bd602d8f-8074-4da6-9b05-1b9b091b7e43 tags:
``` julia
abstract type Food end
abstract type Vegetables <: Food end
struct Potatoes <: Vegetables end
struct Carrots <: Vegetables end
abstract type Fish <: Food end
struct Salmon <: Fish end
struct Shrimps <: Fish end # not biologically a fish, but we can consider them so from a culinary point of view
```
%% Cell type:code id:3a9a5ca1-d549-49d9-8a91-6c9c58374efb tags:
``` julia
print_type_tree(Food)
```
%% Cell type:markdown id:0e8f258e tags:
Nun haben wir ein Bratrezept gefunden, das für jede Art von Gemüse recht gut funktioniert. Dann schreiben wir so etwas wie:
%% Cell type:code id:c6e81e79-7d93-40bb-9e18-3c307c4ca8ea tags:
``` julia
function frying(vegetable::Vegetables)
# make tempura
# fry
end
```
%% Cell type:markdown id:517c0e6a tags:
Wenn wir unser Gemüse braten möchten, führen wir das folgendermaßen aus:
%% Cell type:code id:c9b3b8b9-91ba-4d30-8e71-dca2a478d4df tags:
``` julia
potatoes = Potatoes()
carrots = Carrots()
println(@which frying(potatoes))
println(@which frying(carrots))
```
%% Cell type:markdown id:694b2921 tags:
Aber jetzt haben wir ein noch spezifischeres Rezept gefunden, das noch besser für Kartoffeln funktioniert. Was wir tun werden, ist das Schreiben einer neuen Funktion, die spezifisch für Kartoffeln ist.
%% Cell type:code id:87cc4c94-1dc6-4515-8b23-135f7428ba1d tags:
``` julia
function frying(potatoes::Potatoes)
# directly fry in oil
end
```
%% Cell type:code id:c4a8bcc4-c551-47d2-ab9f-bdf8e42142c4 tags:
``` julia
println(@which frying(potatoes))
```
%% Cell type:markdown id:7a6463aa tags:
Dieses Beispiel zeigt wirklich die Stärke von Julia. Mehrfachdispatch ist aus folgenden Gründen gut:
- **Flexibilität:** Es ist möglich, neue Dinge auf sehr schnelle Weise auszuprobieren. Wir implementieren einen neuen Datentyp und verwenden die Methoden, die bereits für abstrakte Typen vorhanden sind.
- **Anpassungsfähigkeit:** Das Implementieren benutzerdefinierter Verhaltensweisen für unsere Datentypen ist einfach. Wir müssen einfach eine benutzerdefinierte Methode hinzufügen.
- **Effizienz:** Wir können die spezifischen Methoden so abstimmen, dass sie mit unserem spezifischen Datentyp super schnell sind.
%% Cell type:markdown id:0687c04d tags:
### Übung
Fügen Sie einige Methoden für `Animal`, `Dog` und `Cat` sowie andere konkrete Typen aus der letzten Übung hinzu.
%% Cell type:code id:6a45fa48 tags:
``` julia
# TODO: implement your code here.
```
%% Cell type:markdown id:4fb9457b tags:
## Dynamische Typisierung und Typableitung
%% Cell type:markdown id:f27eca4d tags:
In der Programmiersprachen-Theorie fallen Typsysteme traditionell in zwei Kategorien. In **dynamisch typisierten** Sprachen wird der Typ eines Werts oder Ausdrucks nur zur Laufzeit abgeleitet, was in der Regel flexibleren Code ermöglicht. Beispiele hierfür sind Python oder MATLAB. Im Gegensatz dazu erfordern sogenannte **statisch typisierte** Sprachen (denken Sie an FORTRAN oder C++), dass die Typen bereits vor der Laufzeit bekannt sind, wenn das Programm kompiliert wird. Dies ermöglicht eine gründlichere Überprüfung auf Fehler (die sich in nicht übereinstimmenden Typen äußern können), und es führt normalerweise zu einer Leistungssteigerung, da mehr Informationen über die Speicherlayout des Programms zur Kompilierzeit bekannt sind. Als Ergebnis können Aspekte wie Vektorisierung, kontinuierliche Ausrichtung von Daten und Vorbelegung von Speicher leichter genutzt werden.
Julia ist irgendwie beides.
%% Cell type:markdown id:3e5a5e29 tags:
Julias starke Betonung von Typen ist einer der Gründe für ihre Leistungsfähigkeit und Flexibilität.
Wenn der Code vor der Ausführung vorkompiliert wird, hat der Compiler Informationen über den Typ aller Variablen im Programm. Er wird dann die beste mögliche Methode für jede dieser Variablen suchen. Wenn eine spezifische und hoch effiziente Methode gefunden wird, wird diese verwendet. Wenn eine spezifische Methode fehlt, wird die nächste Möglichkeit im Typbaum verwendet, die immer noch funktioniert, wenn auch nicht so effizient.
**Hinweis:** Diese Suche ist der Grund, warum es nicht möglich ist, abstrakte Typen zu instanziieren. Die Verwendung von Variablen abstrakter Typen würde diesen Vorgang unnötig kompliziert machen. Außerdem spiegelt es auch die Realität wider: Ein generisches Gemüse existiert physisch nicht, wir haben nur Kartoffeln, Karotten, Auberginen und so weiter.
%% Cell type:markdown id:869f5fbc tags:
### Drei weitere Fakten über Julia-Typen:
- In Julia sind alle Typen gleich. Zum Beispiel gibt es keinen Unterschied zwischen `Int32` und `String`, obwohl der erste eine direkte Zuordnung zu Low-Level-Anweisungen in der CPU hat und der letztere nicht (im Gegensatz zu z.B. C++).
- Der Typ `Nothing` mit der einzigen Instanz `nothing` entspricht in Julia `void` in C++ oder `None` in Python. Er repräsentiert oft, dass eine Funktion nichts zurückgibt oder dass eine Variable nicht initialisiert wurde.
- `Any` ist die Wurzel des Typbaums: Jeder Typ in Julia ist ein Untertyp von `Any`.
%% Cell type:markdown id:1ca89804 tags:
##### Für weitere Details
https://docs.julialang.org/en/v1/manual/types/
%% Cell type:markdown id:3e8c5b51-3ef6-4d40-9803-dcc2ab6eda3f tags:
## Übung
Diese Übung befasst sich mit dem Schreiben eines benutzerdefinierten Datentyps für Polynome, einer Reihe von Konstruktoren dafür und Funktionen zum Durchführen von Operationen an ihnen. **Hinweis:** Das Schreiben benutzerdefinierter Polynome auf diese Weise ist weder effizient noch bequem, aber es ist ein gutes Beispiel für die bisher behandelten Themen.
- Implementieren Sie einen neuen abstrakten Datentyp `AbstractPolynomial`. Dann implementieren Sie einige konkrete Untertypen, `PolynomialDegree0`, `PolynomialDegree1`, `PolynomialDegree2` und `PolynomialArbitraryDegree`. **Tipp:** Verwenden Sie eine Zuordnungstabelle, um die Koeffizienten auf konsistente Weise zwischen den verschiedenen Untertypen zu speichern: Sie können die Potenz von `x` als Schlüssel für den Koeffizientenwert verwenden.
- Schreiben Sie einen Konstruktor für jeden von ihnen, der die Koeffizienten als Eingabe erhält. **Tipp:** Verwenden Sie `args::Real...`, um eine beliebige Anzahl von Argumenten zu sammeln. Die Angabe des Typs ist wichtig, um Unklarheiten beim Aufruf mit einem Argument zu vermeiden (versuchen Sie, `Real` nicht anzugeben, um das Problem zu sehen).
- Schreiben Sie eine Funktion `Polynomial` mit mehreren Methoden: Abhängig von der Anzahl der Argumente sollte sie den richtigen Konstruktor aufrufen. **Tipps:** Sie können die Kurzform `Polynomial(c0) = PolynomialDegree0(c0)` verwenden, um den Code kürzer zu schreiben, und Sie sollten erneut `args...` verwenden, um eine beliebige Anzahl von Argumenten zu unterstützen.
- Implementieren Sie eine Methode der `+` Funktion, die zwei `AbstractPolynomial` zusammenzählt. Es sollte mit jeder Kombination der konkreten Typen funktionieren. Es sollte auch eine Schleife über die Koeffizienten verwenden. **Hinweis:** Das Implementieren einer Methode für eine Funktion der Kernbibliothek erfordert zunächst das Importieren von `import Base.:+`.
- Implementieren Sie einige weitere Methoden der `+`-Bibliothek, die Polynome niedriger Ordnung zusammenzählen. Diese sollten keine Schleife verwenden.
- Zuletzt führen Sie eine Leistungsbewertung der `+` Funktion für zwei `PolynomialDegree1`-Polynome und für zwei `PolynomialArbitraryDegree`-Polynome vom Grad 1 durch. **Tipps:** Generieren Sie zufällige Koeffizienten mit `rand()`, wiederholen Sie den Vorgang einige Tausend Mal, um eine messbare Laufzeit zu erhalten, verwenden Sie die Makro `@time`, um die Zeit zu messen.
%% Cell type:code id:be3f23e1-b0f8-498f-afd2-9618cb64febb tags:
``` julia
# TODO: implement your code here.
```
Loading