瀏覽代碼

Merge remote-tracking branch 'origin/hw2'

Manuel Vögele 8 年之前
父節點
當前提交
16dd66140f
共有 16 個檔案被更改,包括 816 行新增31 行删除
  1. 1
    0
      .travis.yml
  2. 0
    22
      OVERVIEW.md
  3. 73
    3
      README.md
  4. 1
    1
      ci/check-basic-style.py
  5. 14
    4
      ci/check-files.py
  6. 4
    1
      ci/run-travis.sh
  7. 21
    0
      files/hw2.txt
  8. 40
    0
      hw2/README.md
  9. 72
    0
      hw2/simu1/QUESTIONS.md
  10. 38
    0
      hw2/task1/README.md
  11. 36
    0
      hw2/task1/tests/task1.rs
  12. 317
    0
      hw2/task2/README.md
  13. 41
    0
      hw2/task2/tests/output.bats
  14. 95
    0
      hw2/task2/tests/task2.rs
  15. 24
    0
      hw2/task3/README.md
  16. 39
    0
      hw2/task3/tests/task3.rs

+ 1
- 0
.travis.yml 查看文件

@@ -1,6 +1,7 @@
1 1
 language: rust
2 2
 rust: stable
3 3
 cache: cargo
4
+before_script: (cargo install rustfmt || true)
4 5
 sudo: false
5 6
 git:
6 7
   depth: 1

+ 0
- 22
OVERVIEW.md 查看文件

@@ -1,22 +0,0 @@
1
-# Übersicht zum Ablauf der Übungsaufgaben im HTWG Modul BSYS
2
-
3
-## Vorbereitung
4
-
5
-
6
-
7
-### Fork des htwg-syslab-bsys GitHub Repository
8
-
9
-Um auf Ihr htwg-syslab-bsys beheimatete Repository für das BSYS Labor zu erhalten, müssen Sie einige Punkte vorab durchführen:
10
-
11
-1. Legen Sie sich - soweit noch nicht vorhanden - einen GitHub Account an.
12
-1. Über den Einladungslink im Moodle-Forum erstellen Sie Ihre Labor-Gruppe unter htwg-syslab-bsys auf github.
13
-
14
->Es macht keinen Sinn eine 1er Gruppe anzumelden, wenn Sie noch keinen Partner gefunden haben, da Sie immer beide RZ-IDs zum Anmelden benötigen. Bitte suchen Sie sich zeitnah einen Partner, mit dem Sie das Labor bestreiten wollen.
15
-
16
-1. ***WICHTIG***: Sie legen sich nun eine Gruppe für Ihr 2er Team an.
17
-    - Name der Gruppe: Benutzen die als Namen den String Ihre Gruppe aus Moodle, also z.B. `grp0`. Achten Sie genau auf die Schreibweise (Gross/Klein-Schreibung)
18
-    - Der 2. Teilnehmer meldet sich zu dieser neuen Gruppe an.
19
-1. Sie landen auf der "Ready to go" Seite. Dort nehmen Sie bitte zuerst die Einladung an, indem Sie auf den *@htwg-syslab-bsys* Link klicken. Nach der Annahme der Einladung können Sie auf Ihr 'Assignment'-Repository per Browser zugreifen
20
-1. ***WICHTIG***: Einer(!) in Ihrem 2er Team forkt nun das Projekt, so dass der Fork Ihres Projektes in IHREM Github Account liegt. Mit diesem geforkten Repository werden Sie zunächst arbeiten. Damit der Partner in Ihrem Team auch mit diesem Repository arbeiten kann, müssen Sie ihn in den Einstellungen zu Ihrem Projekt auf github dazu autorisieren.
21
-    - Führen Sie keine Änderungen auf dem Repository im htwg-syslab-bsys Bereich durch, sondern NUR im geforkten Rep in Ihrem privaten github Account.
22
-

+ 73
- 3
README.md 查看文件

@@ -1,3 +1,13 @@
1
+- [Vorbereitung](#vorbereitung)
2
+    - [User A and User B @ Github:](#user-a-and-user-b-github)
3
+- [Git and GitHub Preparations](#git-and-github-preparations)
4
+    - [User A @ GitHub](#user-a-github)
5
+    - [User A @ Container:](#user-a-container)
6
+    - [User B @ Container:](#user-b-container)
7
+- [Schritte zum Pull-Request](#schritte-zum-pull-request)
8
+- [Travis-CI](#travis-ci)
9
+- [gitignore](#gitignore)
10
+
1 11
 # Laborzugang einrichten
2 12
 
3 13
 1. Beachten Sie die Ankündigungen im Moodle Forum. Dort werden die Termine zur Anmeldung als 2er Gruppe sowie die Freischaltung Ihrer Workstation bekannt gegeben. Beachten Sie bitte unbedingt die Deadlines dazu!
@@ -8,7 +18,7 @@
8 18
 
9 19
 # Aufgaben zum BSYS Labor
10 20
 
11
-In diesem Ordner werden die Aufgaben (Homework,`hw`) veröffentlicht, die bearbeitet werden müssen, um den Schein in BSYS zu bekommen. In der Datei `OVERVIEW.md` in diesem Ordner sind weitere Informationen enthalten.
21
+In diesem Ordner werden die Aufgaben (Homework,`hw`) veröffentlicht, die bearbeitet werden müssen, um den Schein in BSYS zu bekommen.
12 22
 
13 23
 Eine Homework besteht aus einer oder mehreren Tasks. Sie finden somit die zu einer Homework gehörenden Aufgaben in den `README.md`, Dateien der zughörigen `hwN/taskN/` Ordner.
14 24
 
@@ -46,7 +56,13 @@ Mit dem Befehl `git clone` kann ein Repository auf den lokalen Rechner (bzw in d
46 56
 git clone git@github.com:UserA/bsys-ws17-grpN
47 57
 ```
48 58
 
49
-Zuletzt müssen die Repositories *upstream* und *template* noch als zusätzliches Remote registriert werden. Ein Remote ist ein Repository auf einem anderen Computer, von dem Commits heruntergeladen bzw. auf das Commits hochgeladen werden können. [Weitere Informationen zu remotes](https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes)
59
+Zuletzt müssen im lokalen Klon noch zusätzliche Remotes hinzugefügt werden. Ein Remote ist ein Repository auf einem anderen Computer, von dem Commits heruntergeladen bzw. auf das Commits hochgeladen werden können. [Weitere Informationen zu remotes](https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes).
60
+
61
+Bei den zusätzlichen Remotes handelt es sich um folgende:
62
+* Das *template* Remote zeigt auf das Basis Repository der Homeworks. Hier werden im weiteren Verlauf die neuen Homeworks eingepflegt und können von Ihnen abgeholt werden.
63
+* Das *upstream* Remote zeigt auf das Repository von dem Ihr Fork ausgeht. Die lokale Master-Branch wird nach jeder erfolgreichen Abgabe vom *upstream* Repository aktualisiert.
64
+
65
+Folgende Befehle fügen die Remotes hinzu:
50 66
 ```bash
51 67
 git remote add template git@github.com:htwg-syslab-bsys-ws17/bsys-ws17-template.git
52 68
 git remote add upstream git@github.com:htwg-syslab-bsys-ws17/bsys-ws17-grpN.git
@@ -56,5 +72,59 @@ git remote add upstream git@github.com:htwg-syslab-bsys-ws17/bsys-ws17-grpN.git
56 72
 
57 73
 Selbes Vorgehen wie User A im vorherigen Abschnitt
58 74
 
75
+# Abgabe der Homeworks
76
+
77
+Sobald Sie alle Tasks einer Homework bearbeitet haben, erstellen Sie einen sogenannten  *Pull Request* (PR) von Ihrem Branch, den Sie zum Review abgeben wollen. Der Ablauf wird hier erklärt, gleich zu Anfang in der hw0 geübt und sollte spätestens dabei klar werden.
78
+
79
+Die Lösungen müssen in einer bestimmten Ordnerstruktur liegen, die wie folgt
80
+aussieht:
81
+
82
+```
83
+...
84
+hw1/
85
+    README.md
86
+    task1/
87
+    task2/
88
+    simu1/
89
+hw2/
90
+    README.md
91
+    task1/
92
+    task2/
93
+    simu1/
94
+...
95
+```
96
+
97
+In den `taskN`-Unterordnern muss Ihre Lösung für die entsprechende Programmieraufgabe
98
+liegen. in den `simuN`-Unterordnern liegen Ihre Antworten zu den Simulationsaufgaben. Im jeweiligen **README.md** der Homeworks (`hwN`) finden Sie dazu die nötigen Informationen und Aufgaben.
99
+
100
+## Schritte zum Pull-Request
101
+1. Überprüfen Sie den Inhalt Ihres Repository. Achten Sie darauf, dass alle Dateien und die jeweiligen Versionen der Dateien die Sie abgeben wollen nicht nur lokal in Ihrem Home Verzeichnis liegen sondern auch in Ihrem Repository auf github.
102
+1. Überprüfen Sie die Ausgabe Ihrer Programme. Stimmen die Ausgaben mit den geforderten Ausgaben überein?
103
+1. Lassen Sie alle Tests laufen indem Sie im Wurzelverzeichnes des Repositories folgenden Befehl ausführen (nur Linux und Verwandte!):
104
+
105
+```
106
+./ci/run-all.sh
107
+```
108
+
109
+1. Sobald Sie alle Aufgaben bearbeitet haben, und zur Bewertung die Aufgabe abgeben wollen, erstellen Sie einen Branch für den Pull-Request.
110
+1. Wählen Sie dann auf github diesen Branch aus und erstellen Sie einen Pull-Reqeust auf diesen Branch.
111
+1. Bei Ihrem Pull-Request laufen automatische Tests durch, die Ihr Programm testen. Dies sind nicht alle Tests von `ci/run-all.sh`, daher MÜSSEN Sie unbedingt selbst im lokalen Verzeichnis auf Ihrer Workstation den `ci/run-all.sh` Test ausführen!
112
+1. Falls Ihnen ein Fehler unterlaufen ist, so können Sie auch nach dem Pull-Request noch Änderungen am Code vornehmen. Das sollte jedoch der Ausnahmefall bleiben. Überprüfen Sie daher VOR Ihrem Pull-Request, ob die nötigen Aufgaben bearbeitet wurden und ob die Tests alle durchlaufen.
113
+
114
+
115
+## Travis-CI
116
+
117
+Um das Arbeiten zu erleichtern, ist für alle Lösungsrepositories ein Continuous
118
+Integration Service aufgesetzt worden. Jedes mal, wenn ein Pull Request (PR) erstellt oder aktualisiert wird, laufen eine Reihe von Tests für Ihre Programmieraufgaben durch, die den Codestil prüfen, alle Rust Dateien kompilieren und alle Unit-Tests ausführen.
119
+
120
+Jeder PR hat also einen Status: *passed*, *failed* oder *pending*. Ihre PR zum
121
+Einreichen (Deadline) der Aufgaben muss den Status *passed* erreicht
122
+haben, also planen Sie genug Zeit zum Verbessern von kleinen Fehlern ein und erstellen den PR nicht erst kurz vor der Deadline. Zur Verbesserung fügen Sie einfach weitere Commits zu der Branch hinzu von der aus der Pull-Request erstellt wurde. Dieser wird auf GitHub dann automatisch aktualisiert.
123
+
124
+>Achtung: Damit das Testen in github nicht zu lange dauert, sind einige sehr lang laufende CI Tests deaktiviert. Bitte aktivieren Sie diese Tests NICHT für travis sondern führen Sie die Tests nur lokal aus. Github Classroom erlaubt nur immer eine laufende Instanz der Travis Tests. Erstellen Sie somit Ihren Pull-Request rechtzeitig, da ansonsten die Deadline aufgrund anderer laufender CI Tests von Ihnen u.U. nicht eingehalten werden kann.
125
+
126
+## gitignore
127
+
128
+Ebenfalls in Ihrem Repository ist bereits eine `.gitignore` Datei im Root Ordner. Damit werden von git gewisse Dateitypen und Directories in Ihren `hwN/` Verzeichnisse ignoriert, so dass Sie diese nicht Ihrem Repository hinzufügen. Achten Sie dennoch drauf, welche Dateien Sie in Ihr Repository hinzufügen, denn in `.gitignore` sind nicht alle Möglichkeiten abgefangen. Fügen Sie mit **git add** immer nur selektiv Dateien hinzu.
59 129
 
60
-[SYSLAB]: https://htwg-syslab.github.io
130
+[SYSLAB]: https://htwg-syslab.github.io

+ 1
- 1
ci/check-basic-style.py 查看文件

@@ -1,4 +1,4 @@
1
-#!/usr/bin/env python
1
+#!/usr/bin/env python2
2 2
 from __future__ import print_function
3 3
 
4 4
 # Checks

+ 14
- 4
ci/check-files.py 查看文件

@@ -1,12 +1,18 @@
1
-#!/usr/bin/env python
1
+#!/usr/bin/env python2
2 2
 from __future__ import print_function
3 3
 
4
+import re
5
+import sys
6
+
4 7
 from os import listdir
5 8
 from os.path import dirname, isfile, join, isdir
6 9
 
7 10
 ROOT_DIR = join(dirname(__file__), '..')
8 11
 FILES_FOLDER = join(ROOT_DIR, 'files')
9 12
 
13
+def natural_sort_key(value):
14
+    return [int(s) if s.isdigit() else s.lower() for s in re.split(r"(\d+)", value)]
15
+
10 16
 
11 17
 def get_file_lists():
12 18
     if not isdir(FILES_FOLDER):
@@ -41,13 +47,17 @@ def check_files(file_list):
41 47
             errors += 1
42 48
 
43 49
     if errors == 0:
50
+        return True
44 51
         print(" -> All Files found.")
52
+    return False
45 53
 
46 54
 
47 55
 def main():
48
-    for f, file_list in get_file_lists().items():
49
-        print(f)
50
-        check_files(file_list)
56
+    file_lists = get_file_lists()
57
+    newest_folder = sorted(file_lists.keys(), key=natural_sort_key)[-1]
58
+    print(newest_folder)
59
+    if not check_files(file_lists[newest_folder]):
60
+        sys.exit(1)
51 61
 
52 62
 
53 63
 if __name__ == '__main__':

+ 4
- 1
ci/run-travis.sh 查看文件

@@ -8,12 +8,15 @@ MY_PATH="$(dirname "$0")"
8 8
 # basic style check
9 9
 "$MY_PATH/check-basic-style.py"
10 10
 
11
+# rustfmt style check
12
+"$MY_PATH/rustfmt.sh"
13
+
11 14
 # check that everything compiles and all tests pass
12 15
 "$MY_PATH/test-rust.sh"
13 16
 
14 17
 # file existence
15 18
 echo "=== Checking for Missing Files ======================================="
16
-"$MY_PATH/check-files.py"
19
+"$MY_PATH/check-files.py" || true
17 20
 
18 21
 echo "++++++++++++++++++++++++++++++++++++++++++++++++++++"
19 22
 echo "+              Everything is fine!                 +"

+ 21
- 0
files/hw2.txt 查看文件

@@ -0,0 +1,21 @@
1
+./hw2/README.md
2
+./hw2/simu1/ANSWERS.md
3
+./hw2/simu1/QUESTIONS.md
4
+./hw2/simu1/null.c
5
+
6
+./hw2/task1/README.md
7
+./hw2/task1/Cargo.toml
8
+./hw2/task1/tests/task1.rs
9
+./hw2/task1/src/lib.rs
10
+
11
+./hw2/task2/README.md
12
+./hw2/task2/Cargo.toml
13
+./hw2/task2/Cargo.lock
14
+./hw2/task2/tests/task2.rs
15
+./hw2/task2/src/lib.rs
16
+./hw2/task2/src/main.rs
17
+
18
+./hw2/task3/README.md
19
+./hw2/task3/Cargo.toml
20
+./hw2/task3/tests/task3.rs
21
+./hw2/task3/src/lib.rs

+ 40
- 0
hw2/README.md 查看文件

@@ -0,0 +1,40 @@
1
+# hw2
2
+
3
+## Tasks
4
+
5
+To fullfill **hw2** you have to solve:
6
+
7
+- task1
8
+- task2
9
+- simu1
10
+
11
+Optional are (bonus +1P):
12
+
13
+- task3 (important: Rust idiomatic code please)
14
+
15
+## Files
16
+
17
+You find already files for each rust task. Please remember to use cargo to
18
+create the relevant projects for each task.
19
+
20
+## ASIDE: `simu1/` dir
21
+
22
+In this homework it's not a simulation homework sitting in `simu1/` but some
23
+questions to have fun with buggy c-files. As not to interfer with rust code,
24
+questions, c-code and your answers should go into `simu1/`.
25
+
26
+## Pull-Request
27
+
28
+Please merge any accepted reviews into your branch. If you are ready with the
29
+homework, all tests run, please create a pull request named **hw2**.
30
+
31
+## Gradings of hw2
32
+
33
+| Task     | max. Credits | Comment |
34
+| -------- | ------------ | ------- |
35
+| task1    | 1            |         |
36
+| task2    | 2            |         |
37
+| task3    | +1           |         |
38
+| simu1    | 1            |         |
39
+| Deadline | +1           |         |
40
+| =        | 6            |         |

+ 72
- 0
hw2/simu1/QUESTIONS.md 查看文件

@@ -0,0 +1,72 @@
1
+# Questions 14-Memory-API
2
+
3
+## Overview
4
+
5
+In this task, you will gain some familiarity with memory allocation. First,
6
+you’ll write some buggy programs (fun!). Then, you’ll use some tools to help you
7
+find the bugs you inserted. Then, you will realize how awesome these tools are
8
+and use them in the future, thus making yourself more happy and productive.
9
+
10
+The first tool you’ll use is **gdb**, the debugger. There is a lot to learn
11
+about this debugger; here we’ll only scratch the surface. Here you can find a
12
+[quick reference gdb][]. Also **ddd** is installed on lab workstations.
13
+
14
+The second tool you’ll use is [valgrind][]. This tool helps find memory leaks
15
+and other insidious memory problems in your program.
16
+
17
+Please answer the questions, by giving the result and an explanation, why you
18
+got the result.  Write your answers in markdown syntax in the new file
19
+`ANSWERS.md`. Also checkin your C-Files and one Makefile for all or your
20
+C-Files, so that all binaries are build. Do NOT checkin the binaries!
21
+
22
+The installed gcc wrapper on you workstations does some optimization to your code
23
+examples, which will not show some of the bugs with an "Segmentation fault". We
24
+have already disabled this behavior by setting:
25
+
26
+```text
27
+export hardeningDisable=all
28
+```
29
+
30
+in your labshell bsys environment. So, you don't have to do any further steps to
31
+produce unoptimized code with gcc, if you want to.
32
+
33
+If you struggle with your own systems about some strange gcc optimization
34
+behavior, maybe it helps to check for gcc-wrapper variables like these.
35
+
36
+## Questions
37
+
38
+1. First, write a simple program called `null.c` that creates a pointer to an
39
+   integer, sets it to `NULL`, and then tries to dereference it. Compile this
40
+   into an executable called **null**. What happens when you run this program?
41
+
42
+1. Next, compile this program with symbol information included (with the `-g`
43
+   flag). Doing so let’s put more information into the executable, enabling the
44
+   debugger to access more useful information about variable names and the like.
45
+   Run the program under the debugger by typing `gdb null` and then, once gdb is
46
+   running, typing `run`. What does gdb show you?
47
+
48
+1. Finally, use the valgrind tool on this program. We’ll use the memcheck tool
49
+   that is a part of valgrind to analyze what happens. Run this by typing in the
50
+   following: `valgrind --leak-check=yes ./null`. What happens when you run
51
+   this? Can you interpret the output from the tool?
52
+
53
+1. Write a simple program that allocates memory using `malloc()` but forgets to
54
+   free it before exiting. What happens when this program runs? Can you use
55
+   `gdb` to find any problems with it? How about `valgrind` (again with the
56
+   `--leak-check=yes` flag)?
57
+
58
+1. Write a program that creates an array of integers of size 100 using
59
+   `malloc()`; then, set `data[100]` to zero. What happens when you run this
60
+   program? What happens when you run this program using `valgrind`? Is the
61
+   program correct?
62
+
63
+1. Create a program that allocates an array of integers (as above),frees them,
64
+   and then tries to print the value of one of the elements of the array. Does
65
+   the program run? What happens when you use `valgrind` on it?
66
+
67
+1. Now pass a funny value to free (e.g., a pointer in the middle of the array
68
+   you allocated above). What happens? Do you need tools to find this type of
69
+   problem?
70
+
71
+[valgrind]: http://valgrind.org/downloads/current.html
72
+[quick reference gdb]: https://web.stanford.edu/class/cs107/gdb_refcard.pdf

+ 38
- 0
hw2/task1/README.md 查看文件

@@ -0,0 +1,38 @@
1
+# Homework hw2 task1
2
+
3
+## prepare your task
4
+
5
+Run the cargo command to prepare your task1/ directory as a library
6
+
7
+## task
8
+
9
+Calculate the Hamming difference between two DNA strands by implementing the
10
+library function *hamming_distance(s1: &str, s2: &str) -> Result<usize,
11
+String>*. Do not use any crates.
12
+
13
+A mutation is simply a mistake that occurs during the creation or copying of a
14
+nucleic acid, in particular DNA. Because nucleic acids are vital to cellular
15
+functions, mutations tend to cause a ripple effect throughout the cell. Although
16
+mutations are technically mistakes, a very rare mutation may equip the cell with
17
+a beneficial attribute. In fact, the macro effects of evolution are attributable
18
+by the accumulated result of beneficial microscopic mutations over many
19
+generations.
20
+
21
+The simplest and most common type of nucleic acid mutation is a point mutation,
22
+which replaces one base with another at a single nucleotide.
23
+
24
+By counting the number of differences between two homologous DNA strands taken
25
+from different genomes with a common ancestor, we get a measure of the minimum
26
+number of point mutations that could have occurred on the evolutionary path
27
+between the two strands.
28
+
29
+This is called the 'Hamming distance'.
30
+
31
+It is found by comparing two DNA strands and counting how many of the
32
+nucleotides are different from their equivalent in the other string.
33
+
34
+    GAGCCTACTAACGGGAT
35
+    CATCGTAATGACGGCCT
36
+    ^ ^ ^  ^ ^    ^^
37
+
38
+The Hamming distance between these two DNA strands is 7.

+ 36
- 0
hw2/task1/tests/task1.rs 查看文件

@@ -0,0 +1,36 @@
1
+extern crate task1;
2
+
3
+#[test]
4
+fn test_no_difference_between_empty_strands() {
5
+    assert_eq!(task1::hamming_distance("", "").unwrap(), 0);
6
+}
7
+
8
+#[test]
9
+fn test_no_difference_between_identical_strands() {
10
+    assert_eq!(task1::hamming_distance("GGACTGA", "GGACTGA").unwrap(), 0);
11
+}
12
+
13
+#[test]
14
+fn test_complete_hamming_distance_in_small_strand() {
15
+    assert_eq!(task1::hamming_distance("ACT", "GGA").unwrap(), 3);
16
+}
17
+
18
+#[test]
19
+fn test_small_hamming_distance_in_the_middle_somewhere() {
20
+    assert_eq!(task1::hamming_distance("GGACG", "GGTCG").unwrap(), 1);
21
+}
22
+
23
+#[test]
24
+fn test_larger_distance() {
25
+    assert_eq!(task1::hamming_distance("ACCAGGG", "ACTATGG").unwrap(), 2);
26
+}
27
+
28
+#[test]
29
+fn test_first_string_is_longer() {
30
+    assert!(task1::hamming_distance("AAA", "AA").is_err());
31
+}
32
+
33
+#[test]
34
+fn test_second_string_is_longer() {
35
+    assert!(task1::hamming_distance("A", "AA").is_err());
36
+}

+ 317
- 0
hw2/task2/README.md 查看文件

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

+ 41
- 0
hw2/task2/tests/output.bats 查看文件

@@ -0,0 +1,41 @@
1
+#!/usr/bin/env bats
2
+
3
+
4
+@test "task2: Check that we have a debug output" {
5
+    run stat "$BATS_TEST_DIRNAME/../target/debug/task2"
6
+    [ "$status" -eq 0 ]
7
+}
8
+
9
+@test "task2: Output Long String: The quick ...." {
10
+    run "$BATS_TEST_DIRNAME/../target/debug/task2" 'q' '♥ The quick brown fox jumps over the lazy dog. ♥'
11
+    [[ "${lines[0]}" =~ "You asked me to count all 'q' in '♥ The quick brown fox jumps over the lazy dog. ♥'" ]]
12
+    [[ "${lines[1]}" =~ "Found 1 'q' in '♥ The quick brown fox jumps over the lazy dog. ♥'" ]]
13
+}
14
+
15
+@test "task2: Output Short String: ababab " {
16
+   run "$BATS_TEST_DIRNAME/../target/debug/task2" 'a' 'ababab'
17
+   [[ "${lines[0]}" =~ "You asked me to count all 'a' in 'ababab'" ]]
18
+   [[ "${lines[1]}" =~ "Found 3 'a' in 'ababab'" ]]
19
+}
20
+
21
+@test "task2: Output Error 1" {
22
+   run "$BATS_TEST_DIRNAME/../target/debug/task2"
23
+   [[ "${lines[0]}" =~ "not enough arguments" ]]
24
+}
25
+
26
+@test "task2: Output Error 2" {
27
+   run "$BATS_TEST_DIRNAME/../target/debug/task2"
28
+   [[ "${lines[0]}" =~ "not enough arguments" ]]
29
+}
30
+
31
+# wc output with white spaces is trimmed by xargs
32
+@test "task2: Output must be exact 2 line long" {
33
+    run bash -c "'$BATS_TEST_DIRNAME/../target/debug/task2' 'a' 'b' 'c' | wc -l | xargs"
34
+    [ "$output" = "2" ]
35
+}
36
+
37
+# wc output with white spaces is trimmed by xargs
38
+@test "task2: Output must be exact 1 line long" {
39
+    run bash -c "'$BATS_TEST_DIRNAME/../target/debug/task2' 'a'  | wc -l | xargs"
40
+    [ "$output" = "1" ]
41
+}

+ 95
- 0
hw2/task2/tests/task2.rs 查看文件

@@ -0,0 +1,95 @@
1
+extern crate task2;
2
+use task2::Config;
3
+
4
+
5
+#[test]
6
+fn test_parse_config_1() {
7
+    let a = vec![
8
+        "Not interested".to_string(),
9
+        "e".to_string(),
10
+        "Numero Due".to_string(),
11
+    ];
12
+    let c = Config {
13
+        search: 'e',
14
+        line: "Numero Due".to_string(),
15
+    };
16
+    assert_eq!(Config::new(&a), Ok(c));
17
+}
18
+
19
+#[test]
20
+#[should_panic]
21
+fn test_parse_config_2() {
22
+    let a = vec![
23
+        "Not interested".to_string(),
24
+        "x".to_string(),
25
+        "Numero Due".to_string(),
26
+    ];
27
+    let c = Config {
28
+        search: 'e',
29
+        line: "Numero Due".to_string(),
30
+    };
31
+    assert_eq!(Config::new(&a), Ok(c));
32
+}
33
+
34
+#[test]
35
+fn test_parse_config_3() {
36
+    let a = vec![
37
+        "Not interested".to_string(),
38
+        "0".to_string(),
39
+        "0".to_string(),
40
+    ];
41
+    let c = Config {
42
+        search: '0',
43
+        line: "0".to_string(),
44
+    };
45
+    assert_eq!(Config::new(&a), Ok(c));
46
+}
47
+
48
+#[test]
49
+fn test_parse_config_err_1() {
50
+    let a = vec!["Not interested".to_string(), "e".to_string()];
51
+    assert_eq!(Config::new(&a), Err("not enough arguments"));
52
+}
53
+
54
+#[test]
55
+fn test_parse_config_err_2() {
56
+    let a = vec!["Not interested".to_string()];
57
+    assert_eq!(Config::new(&a), Err("not enough arguments"));
58
+}
59
+
60
+#[test]
61
+fn test_run_1() {
62
+    let c = Config {
63
+        search: 'e',
64
+        line: "Numero Due".to_string(),
65
+    };
66
+    assert_eq!(task2::run(&c), 2);
67
+}
68
+
69
+#[test]
70
+fn test_run_2() {
71
+    let c = Config {
72
+        search: '♥',
73
+        line: "♥ The quick brown fox jumps over the lazy dog. ♥".to_string(),
74
+    };
75
+    assert_eq!(task2::run(&c), 2);
76
+}
77
+
78
+#[test]
79
+#[should_panic]
80
+fn test_run_3() {
81
+    let c = Config {
82
+        search: 'q',
83
+        line: "♥ The quick brown fox jumps over the lazy dog. ♥".to_string(),
84
+    };
85
+    assert_eq!(task2::run(&c), 2);
86
+}
87
+
88
+#[test]
89
+fn test_run_4() {
90
+    let c = Config {
91
+        search: '!',
92
+        line: "♥ The quick brown fox jumps over the lazy dog. ♥".to_string(),
93
+    };
94
+    assert_eq!(task2::run(&c), 0);
95
+}

+ 24
- 0
hw2/task3/README.md 查看文件

@@ -0,0 +1,24 @@
1
+# Homework hw2 task3
2
+
3
+## prepare your task
4
+
5
+Run the cargo command to prepare your task3/ directory as a library
6
+
7
+## task
8
+
9
+Compute Pascal's triangle up to a given number of rows. Create the new type
10
+*PascalsTriangle* and implement the methods *new()* and *rows()*. See the
11
+`tests/task3.rs` for more informationen about parameters and returns of the
12
+methods. Do not use any crates.
13
+
14
+In Pascal's Triangle each number is computed by adding the numbers to the right
15
+and left of the current position in the previous row.
16
+
17
+```plain
18
+    1
19
+   1 1
20
+  1 2 1
21
+ 1 3 3 1
22
+1 4 6 4 1
23
+# ... etc
24
+```

+ 39
- 0
hw2/task3/tests/task3.rs 查看文件

@@ -0,0 +1,39 @@
1
+extern crate task3;
2
+
3
+use task3::*;
4
+
5
+#[test]
6
+fn no_rows() {
7
+    let pt = PascalsTriangle::new(0);
8
+    let expected: Vec<Vec<u32>> = Vec::new();
9
+    assert_eq!(expected, pt.rows());
10
+}
11
+
12
+
13
+#[test]
14
+fn one_row() {
15
+    let pt = PascalsTriangle::new(1);
16
+    let expected: Vec<Vec<u32>> = vec![vec![1]];
17
+    assert_eq!(expected, pt.rows());
18
+}
19
+
20
+#[test]
21
+fn two_rows() {
22
+    let pt = PascalsTriangle::new(2);
23
+    let expected: Vec<Vec<u32>> = vec![vec![1], vec![1, 1]];
24
+    assert_eq!(expected, pt.rows());
25
+}
26
+
27
+#[test]
28
+fn three_rows() {
29
+    let pt = PascalsTriangle::new(3);
30
+    let expected: Vec<Vec<u32>> = vec![vec![1], vec![1, 1], vec![1, 2, 1]];
31
+    assert_eq!(expected, pt.rows());
32
+}
33
+
34
+#[test]
35
+fn last_of_four_rows() {
36
+    let pt = PascalsTriangle::new(4);
37
+    let expected: Vec<u32> = vec![1, 3, 3, 1];
38
+    assert_eq!(expected, pt.rows().pop().unwrap());
39
+}

Loading…
取消
儲存