Przeglądaj źródła

Restructering Content

Michael Mächtel 8 lat temu
rodzic
commit
c08308869e
1 zmienionych plików z 297 dodań i 0 usunięć
  1. 297
    0
      hw2/task2/README.md

+ 297
- 0
hw2/task2/README.md Wyświetl plik

@@ -0,0 +1,297 @@
1
+# Homework hw2 task2
2
+
3
+## Inhalt
4
+
5
+- [Überblick](#%C3%BCberblick)
6
+- [Struktur](#struktur)
7
+- [Funktionen](#funktionen)
8
+  - [*parse_arguments()*](#parsearguments)
9
+  - [Programmargumente in Rust](#programmargumente-in-rust)
10
+  - [Warmup Übung](#warmup-%C3%BCbung)
11
+  - [Argumente parsen](#argumente-parsen)
12
+  - [Parsen ohne Fehlerbehandlung](#parsen-ohne-fehlerbehandlung)
13
+  - [Parsen mit Fehlerbehandlung](#parsen-mit-fehlerbehandlung)
14
+  - [Ablauf des Programms](#ablauf-des-programms)
15
+- [Restructuring](#restructuring)
16
+  - [Parsen der Config als Methode](#parsen-der-config-als-methode)
17
+  - [Tests](#tests)
18
+  - [Dokumentation](#dokumentation)
19
+
20
+## Überblick
21
+
22
+In dieser Aufgabe sollen Sie ein Programm entwickeln, welches 2 Parameter
23
+übergeben bekommt:
24
+
25
+- *1.* übergebene Parameter: Zu suchendes Zeichen
26
+- *2.* übergebene Parameter: String in dem die Zeichenkette gesucht werden soll.
27
+
28
+Das Programm gibt die Anzahl gefundener Treffer aus.
29
+
30
+```text
31
+You asked me to count all 'e' in "♥ The quick brown fox jumps over the lazy dog. ♥"
32
+Found 3 'e' in "♥ The quick brown fox jumps over the lazy dog. ♥"
33
+```
34
+
35
+Neben einer klaren Struktur für das Programm soll eine kompetente
36
+Fehlerbehandlung implementiert werden, sodass das Programm robust gegenüber
37
+Eingebefehlern wird.
38
+
39
+Ziele:
40
+
41
+- Parameter einlesen können
42
+- Weitere String-Funktionen kennen lernen
43
+- Fehlerbehandlung
44
+
45
+## Struktur
46
+
47
+Das Programm soll aus 3 Funktionen bestehen:
48
+
49
+- *main()*
50
+- *run()*
51
+- *parse_arguments()*
52
+
53
+Die *main()* Funktion ruft zuerst die *parse_arguments()* auf, um danach
54
+geeignet die *run()* Funktion zum Suchen der Zeichenkette zu starten.
55
+
56
+## Funktionen
57
+
58
+### Programmargumente in Rust
59
+
60
+Bisher waren in Ihren Programmen die Parameter fest codiert. Wie kann man in
61
+Rust die Parameter beim Aufruf des Programms auswerten?
62
+
63
+Dazu existiert das [std::env][std::env] Modul, welches Strukturen und Funktionen
64
+für die Prozessumgebung (process environment) bereit stellt.
65
+
66
+Die Funktion, die die Parameter zur weiteren Auswertung liefert heißt
67
+[`args()`][args]. Diese Funktion liefert einen Iterator mit Namen [`Arc`][Arc]
68
+zurück. Was Iteratoren genau sind und was damit alles anzustellen ist werden wir
69
+später im Semester kennen lernen. Zunächst kann man sich einen Iterator einfach
70
+als eine 'Datenquelle' vorstellen, die - wenn wir darauf zugreifen - eine Serie
71
+von Werten liefert. In unserem Fall liefert ein Zugriff auf [`args()`][args] die
72
+dem Programm übergebenen Parameter als Strings, sofern welche übergeben wurden.
73
+
74
+Und damit sind wir auch schon mitten in der Fehlerbehandlung. Auf mögliche
75
+Fehler wird in der Dokumentation bereits hingewiesen. Wir werden im weiteren von
76
+UNIX Systemen und korrektem unicode ausgehen, sodass uns ein paar
77
+Fehlerbehandlungen erspart bleiben.
78
+
79
+Um in unserem Programm auf die Parameter zugreifen zu können, werden wir diese
80
+in einer geeigneten Datentyp speichern. Es bieten sich Arrays oder Vektoren an.
81
+
82
+#### Warmup Übung
83
+
84
+1. Warum sollten Sie für die Argumente den Datentyp Vector vorziehen?
85
+1. Schreiben Sie die Funktion:
86
+
87
+```rust
88
+/// Prints elements of Vec
89
+///
90
+///
91
+/// This function will print all elements of Vec with "args found: <elem>" in
92
+/// each line
93
+///
94
+/// Returns nothing
95
+fn print_arguments(args: &Vec<String>)
96
+```
97
+
98
+Die Funktion gibt den Inhalt des übergeben Vektors zeilenweise aus.
99
+
100
+In der Main Funktion lesen Sie die bei Programmstart übergebenen Parameter ein
101
+und erstellen einen Vektor aus den eingelesenen Parametern, den Sie als Referenz
102
+übergeben.
103
+
104
+> Die Methode [*collect()*][collect] ist Teil des Iterator Traits und bietet
105
+> sich an um den Vector zu füllen. Überlegen Sie sich auch, warum es sinnvoll
106
+> ist den Vector an die Funktion *print_arguments* nur zu leihen ('&')?
107
+
108
+Wenn Sie nun Ihr Projekt mit `cargo run a b c`starten, so erhalten Sie folgende
109
+Ausgabe:
110
+
111
+```text
112
+    Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
113
+     Running `target/debug/task1 a b c`
114
+args found: target/debug/task1
115
+args found: a
116
+args found: b
117
+args found: c
118
+```
119
+
120
+### Argumente parsen
121
+
122
+Unsere Argumente speichern wir am Besten in einer eigenen Datenstruktur
123
+*Config*. Dann sind unsere Parameter als entsprechende Datentypen einfach
124
+verfügbar.
125
+
126
+```rust
127
+/// a struct to hold all of our configuration
128
+#[derive(Debug,PartialEq)]
129
+struct Config{
130
+    search: char,
131
+    line: String,
132
+}
133
+```
134
+
135
+> Mit der *derive* Anweisung lassen wir den Rust Compiler etwas Magie anwenden.
136
+> Denn durch diese Makro Anweisung erhält unsere Datenstruktur automatisch 2
137
+> Traits wodurch wiederum automatisch die nötigen Methoden für uns erzeugt
138
+> werden um unsere Struct zu printen und deren Inhalt vergleichen zu können
139
+> ("==" Abfrage). Letzteres brauchen wir für die Tests.
140
+
141
+#### Parsen ohne Fehlerbehandlung
142
+
143
+1. Implementieren Sie die Funktion
144
+
145
+```rust
146
+/// Parses relevant arguments, returning the filled Config
147
+///
148
+///
149
+/// This function will parse the relevant arguments from the
150
+/// given <Strings>.
151
+/// Returns Config
152
+fn parse_arguments_simple(args: &Vec<String>) -> Config
153
+```
154
+
155
+Sie müssen nun in der Funktion:
156
+
157
+- den 1. String in einen char verwandeln
158
+- den 2. String der Config  Struktur zuweisen
159
+
160
+>Beachten Sie, das die Strings, die die Argumente repräsentieren in der Funktion
161
+>geliehen sind und somit jemand anderen gehören. Ihre Config Struktur benötigt
162
+>somit EIGENE Strings.
163
+
164
+In *main()* geben Sie die geparsten Parameter aus, sodass Sie bei Aufruf Ihres
165
+Programms "cargo run a b c" folgende Ausgabe erhalten:
166
+
167
+```text
168
+    Finished dev [unoptimized + debuginfo] target(s) in 0.27 secs
169
+     Running `target/debug/task1 a b c`
170
+args found: target/debug/task1
171
+args found: a
172
+args found: b
173
+args found: c
174
+Config { search: 'a', line: "b" }
175
+```
176
+
177
+#### Parsen mit Fehlerbehandlung
178
+
179
+1. Unter Umständen ruft ein Benutzer Ihr Programm ohne die benötigten Parameter
180
+   auf. Welche Fehlermeldung erhalten Sie bei dem Aufruf Ihres Programms ohne
181
+   Parameter und warum?
182
+
183
+1. Ein Programmabsturz wäre die falsche Antwort darauf. Besser ist beim
184
+   Auftreten von Eingabefehler ein entsprechender Hinweis, wie das Programm zu
185
+   verwenden ist. Implementieren Sie die Funktion
186
+
187
+```rust
188
+/// Parses relevant arguments, returning the filled Config in Result
189
+///
190
+///
191
+/// This function will parse the relevant arguments from the
192
+/// given <Strings>.
193
+/// Returns Config or Error Message in Result
194
+fn parse_arguments(args: &Vec<String>) -> Result<Config, xx >
195
+```
196
+
197
+Implementieren Sie die folgende Fehlerbehandlung:
198
+
199
+- Werden vom Benutzer zuwenig übergeben, so gibt die Funktion den Fehlerstring
200
+  "not enough parameters" zurück. Werden vom Benutzer zu viele Parameter
201
+  übergeben, so wertet die Funktion nur die ersten beiden aus. In der API der
202
+  Funktion oben ist als Typ xx für den 'String' angedeutet und muss entsprechend
203
+  von Ihnen mit dem richtigen Typen versehen werden.
204
+
205
+- Tritt ein Fehler beim Wandeln des 1. Parameter in einen char auf, so geben Sie
206
+  den String "char mismatch" als Fehlerstring in Result zurück. Wird statt eines
207
+  chars als 1. Parameter ein String übergeben, so werten Sie nur den ersten
208
+  Buchstaben davon aus.
209
+
210
+Die main() Funktion kann keine Return Werte zurückliefern. Um aber der
211
+aufrufenden Shell den Abbruch des Programms mitteilen zu können steht die
212
+Funktion *[process::exit()]* aus der Standardbibliothek zu Verfügung. Durch die
213
+Rückgabe von '1' signalisieren Sie der Shell, dass das Programm abgebrochen
214
+wurde.
215
+
216
+### Zeichen im String suchen
217
+
218
+In Ihrer vorherigen Homework haben Sie bereits eine ähnliche Funktionalität
219
+implementiert, die Sie nun in der *run()* Funktion übernehmen können.
220
+
221
+### Ablauf des Programms
222
+
223
+Zur Abgabe des Programms soll die  *main()* Funktion nur:
224
+
225
+- *parse_arguments()* und
226
+- *run()*
227
+
228
+benutzen. In diesen beiden Funktionen darf kein *unwrap()* mehr verwendet
229
+werden.
230
+
231
+Da die Tests jedoch auch die Funktion *parse_arguments_simple()* benutzt, darf
232
+diese nicht auskommentiert werden.
233
+
234
+#### Dead Code
235
+
236
+Um die Compiler Warnungen vor nicht benutzen Funktionen auszuschalten, fügen Sie
237
+direkt in der Zeile vor der Funktion folgende Anweisung hinzu
238
+
239
+```Rust
240
+#[allow(dead_code)]
241
+fn parse_arguments_simple(args: &Vec<String>) -> Config {
242
+...
243
+```
244
+
245
+## Restructuring
246
+
247
+Bisher ist alles in unserm `src/main.rs` Modul, wodurch dieses im Laufe der Zeit
248
+immer unübersichtlicher werden würde. Ziel ist es nun, das meiste aus der
249
+`main.rs` auszulagern, sodass nur noch die *main()* Funktion in `main.rs` steht.
250
+
251
+Ausgelagert wird der Code in eine Bibliothek, genauer gesagt in die Datei
252
+`src/lib.rs`.
253
+
254
+>Um den Code aus `lib.rs` in `main.rs` verwenden zu können, müssen Sie ihre
255
+>derzeitige 'Create' mit den entsprechenden 'extern' und 'use' Anweisungen in
256
+>`main.rs`, erreichbar machen.
257
+
258
+### Parsen der Config als Methode
259
+
260
+Die *parse_arguments()* Methode steht noch in keinem Zusammenhang mit der
261
+Struktur, die die Methode mit Daten füllt.
262
+
263
+Schreiben Sie einen Konstruktor *new()* für die Datenstruktur *Config* und
264
+ersetzten Sie damit die *parse_arguments()* Funktion. Verwenden Sie nun Config
265
+geeignet in Ihrer *main()* Funktion.
266
+
267
+### Tests
268
+
269
+Die Test sind ausgelagert in die Datei `tests/task2.rs`
270
+
271
+### Dokumentation
272
+
273
+Dokumentieren Sie die Funktionen in Ihrer `lib.rs`.
274
+
275
+Da Sie im `src/`Verzeichnis eine `main.rs` und eine `lib.rs` vorliegen haben, beschwert sich **cargo doc**, dass es nicht weiss wie es die Dokumentation erstellen soll:
276
+
277
+```text
278
+error: cannot document a package where a library and a binary have the same name. Consider renaming one or marking the target as `doc = false`
279
+```
280
+
281
+Damit **cargo doc** Ihre Dokumentation für die Funktionen in `lib.rs` erstellt,
282
+benötigen Sie folgende Erweiterung in Ihrer `Cargo.toml` Datei.
283
+
284
+```text
285
+[[bin]]
286
+doc = false
287
+name = "task2"
288
+```
289
+
290
+Damit weiß cargo, dass es die Dokumentation aus `lib.rs` erstellen soll und
291
+nicht aus `main.rs`.
292
+
293
+[args]: https://doc.rust-lang.org/std/env/fn.args.html
294
+[std::env]: https://doc.rust-lang.org/std/env/index.html
295
+[Arc]: https://doc.rust-lang.org/std/env/struct.Args.html
296
+[collect]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect
297
+[process::exit()]: https://doc.rust-lang.org/std/process/fn.exit.html

Ładowanie…
Anuluj
Zapisz