Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • compilerbau-sose-2022/rust-aufgaben/cb-1
  • jacobphi/cb-1
  • gertullj/cb-1
  • guerlice/cb-1
4 results
Show changes
Commits on Source (3)
......@@ -5,4 +5,4 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
\ No newline at end of file
[dependencies]
# Übungsblatt 1
## Generelles
Die ersten beiden Aufgaben dienen zur Vertiefung Ihrer praktischen Erfahrungen in Rust. Sie implementieren einen Stack und eine Baumstruktur.
## Allgemeine Hinweise
Für diese und alle folgenden Praktikumsaufgaben gilt, dass Einsendungen, die in der jeweils mitgegebenen Testumgebung nicht laufen, mit null Punkten bewertet werden! Das beinhaltet insbesondere alle Programme, die sich nicht fehlerfrei kompilieren lassen. Da Cargo für die Ausführung verantwortlich ist, sollte das Projekt bei Ihnen am Ende mit `cargo test` ohne Fehler und Warnungen durchlaufen.
Für diese und alle folgenden Praktikumsaufgaben gilt, dass Einsendungen, die in der jeweils mitgegebenen Testumgebung nicht laufen, entsprechend Punktabzug erhalten! Das beinhaltet insbesondere alle Programme, die sich nicht fehlerfrei kompilieren lassen. Da Cargo für die Ausführung verantwortlich ist, sollte das Projekt bei Ihnen am Ende mit `cargo test` ohne Fehler und Warnungen durchlaufen.
## Abgabemodus
Die Lösung ist in einem eigenen Git-Repository abzugeben.
Sie können in ihrer Lösung jedoch beliebige Hilfstypen und Module selbst definieren.
Die grundlegende Struktur des hier mitgegebenen Templates sollte jedoch nicht verändert werden.
Insbesondere ist es wichtig, dass die öffentliche Schnittstelle der Library, welche über die Signaturen der Methoden und Funktionen und deren absoluten Pfad definiert wird.
Insbesondere ist es wichtig, dass die öffentliche Schnittstelle der Library, welche über die Signaturen der Methoden und Funktionen und deren absoluten Pfad definiert wird, nicht verändert wird.
Zur Lösung der Aufgaben steht für Sie dieses Repository mit
- vorgegebenen Modulen [stack](src/stack.rs) und [syntree](src/syntree.rs)
- vorgegebenen Modulen [stack](src/stack.rs) und [syntax_tree](src/syntax_tree.rs)
- der vorgegebenen Schnittstelle der Library in [lib](src/lib.rs)
- eine Reihe von Testfällen (Unit-Tests) innerhalb der Module
- einer Reihe von Testfällen (Unit-Tests) innerhalb der Module
zur Verfügung.
> Sie können die Implementierung mit `cargo test` prüfen. Mit `cargo test -- --nocapture` werden Konsolenausgaben auch bei korrekten Tests angezeigt.
......@@ -65,29 +66,46 @@ Implementieren Sie eine Datenstruktur, die beliebig verzweigte Bäume speichern
Die hier von Ihnen zu implementierende Datenstruktur dient der Repräsentation eines abstrakten Syntaxbaumes.
```rust
struct SynTree;
impl<'a, T> SynTree<T> {
/// Initialisiert einen neuen Syntaxbaum
pub fn new(value: T, id: ID) -> Syntree<T> { todo!() }
/// Fügt dem angegebenen Elternknoten den mitgegebenen Syntaxbaum als letztes Kind hinzu
pub fn push_node(&mut self, parent_id: ID, new_node: Syntree<T>) -> Result<(), String> { todo!() }
/// Fügt dem angegebenen Elternknoten den mitgegebenen Syntaxbaum als erstes Kind hinzu
pub fn prepend_node(&mut self, parent_id: ID, new_node: Syntree<T>) -> Result<(), String> { todo!() }
/// Fügt dem angegebenen Elternknoten den mitgegebenen Syntaxbaum als Kind an der indizierten Stelle hinzu
pub fn insert_node(
struct SyntaxTree;
impl<T> SyntaxTree<T> {
/// Create a SyntaxTree with a root node that carries the given value
pub fn new(value: T) -> SyntaxTree<T> {
todo!()
}
/// Add another SyntaxTree as last child of this tree
pub fn push_node(&mut self, new_node: SyntaxTree<T>) {
todo!()
}
/// Add another SyntaxTree as first child of this tree
pub fn prepend_node(&mut self, new_node: SyntaxTree<T>) {
todo!()
}
/// Insert the given SyntaxTree into the children of this tree at the given index
pub fn insert_node(&mut self, index: usize, new_node: SyntaxTree<T>) {
todo!()
}
/// Perform a depth-first search with the given predicate.
/// The method returns a reference to the first SyntaxTree instance for which the predicate
/// return true. If no instance is found, None is returned.
pub fn find_node(&self, predicate: fn(&SyntaxTree<T>) -> bool) -> Option<&SyntaxTree<T>> {
todo!()
}
/// Perform a depth-first search with the given predicate.
/// The method returns a mutable reference to the first SyntaxTree instance for which the predicate
/// return true. If no instance is found, None is returned.
pub fn find_node_mut(
&mut self,
parent_id: ID,
index: usize,
new_node: Syntree<T>,
) -> Result<(), String> { todo!() }
/// Suche nach dem ersten Knoten mit der angegebenen ID und liefere eine mutable Referenz zurück
pub fn seek_node_mut(&'a mut self, id: &ID) -> Option<&'a mut Syntree<T>> { todo!() }
predicate: fn(&SyntaxTree<T>) -> bool,
) -> Option<&mut SyntaxTree<T>> {
todo!()
}
}
```
- Die zu implementierende Datenstruktur Syntree soll Baumknoten in beliebig komplexen Konfigurationen speichern können und davon beliebig viele.
- Vervollständigen Sie die Implementierung in der Datei [syntree.rs](src/syntree.rs).
\ No newline at end of file
- Die zu implementierende Datenstruktur SyntaxTree soll Baumknoten in beliebig komplexen Konfigurationen speichern können und davon beliebig viele.
- Vervollständigen Sie die Implementierung in der Datei [syntax_tree](src/syntax_tree.rs).
mod stack;
mod syntree;
mod syntax_tree;
pub trait Stack {
fn init() -> Self;
......@@ -14,8 +14,8 @@ pub trait Stack {
}
pub use stack::ListStack;
pub use syntree::Syntree;
pub use syntree::ID;
pub use syntax_tree::SyntaxTree;
pub use syntax_tree::ID;
#[cfg(test)]
mod tests {}
......@@ -73,17 +73,19 @@ mod tests {
use crate::stack::ListStack;
use crate::Stack;
use std::fmt::Debug;
use std::thread::sleep;
use std::time::{Duration, Instant};
#[test]
fn fill_and_clear() {
println! {"Testing ListStack"}
fill_and_clear_impl(ListStack::init());
fn vec_fill_and_clear() {
println! {"Testing Vec<T>"}
fill_and_clear_impl(Vec::init());
}
#[test]
fn linked_fill_and_clear() {
println! {"Testing ListStack"}
fill_and_clear_impl(ListStack::init());
}
fn fill_and_clear_impl<T: Stack + Debug>(mut stack: T) {
stack.push_val(1);
assert_eq!(stack.top_val(), Some(&1));
......@@ -103,56 +105,4 @@ mod tests {
assert!(stack.is_empty())
}
const BENCHMARK_SIZE: i32 = 10_000_000;
#[test]
fn benchmark() {
let (pushed, popped) = bench(ListStack::init());
println!(
"Own implementation took {}ms for push and {} for pop.",
pushed, popped
);
let (pushed, popped) = bench(Vec::init());
println!(
"Vec wrapper took {}ms for push and {} for pop.",
pushed, popped
);
}
fn bench<T: Stack>(mut stack: T) -> (u128, u128) {
let start = Instant::now();
for i in 1..BENCHMARK_SIZE {
stack.push_val(i);
}
println!("Pushed all elements");
let pushed = start.elapsed().as_millis();
let start = Instant::now();
while stack.pop_val().is_some() {}
println!("Popped all elements");
let popped = start.elapsed().as_millis();
(pushed, popped)
}
#[test]
fn test_mem() {
let stack = ListStack::init();
mem_test(stack);
println!("Finished memory test for ListStack");
println!("Sleeping for 10 seconds.");
sleep(Duration::from_secs(10));
let stack = Vec::init();
mem_test(stack);
println!("Finished memory test for Vec<T>");
}
fn mem_test<T: Stack>(mut stack: T) {
for i in 1..BENCHMARK_SIZE {
stack.push_val(i);
stack.pop_val();
}
println!("Completed memory test elements");
}
}
use std::fmt::{Display, Formatter};
pub type ID = usize;
static mut LAST_ID: usize = 0;
#[derive(Clone, Debug, PartialEq)]
pub struct SyntaxTree<T> {
id: ID,
value: T,
children: Vec<SyntaxTree<T>>,
}
/// Simple ID provider
fn next_id() -> ID {
unsafe {
let id = LAST_ID;
LAST_ID += 1;
id
}
}
impl<T> SyntaxTree<T> {
/// Create a SyntaxTree with a root node that carries the given value
pub fn new(value: T) -> SyntaxTree<T> {
todo!()
}
/// Add another SyntaxTree as last child of this tree
pub fn push_node(&mut self, new_node: SyntaxTree<T>) {
todo!()
}
/// Create a new SyntaxTree with a root node that carries the given value. Add the created tree
/// as last child of this tree.
pub fn push_value(&mut self, value: T) {
self.push_node(SyntaxTree::new(value));
}
/// Add another SyntaxTree as first child of this tree
pub fn prepend_node(&mut self, new_node: SyntaxTree<T>) {
todo!()
}
/// Create a new SyntaxTree with a root node that carries the given value. Add the created tree
/// as first child of this tree.
pub fn prepend_value(&mut self, value: T) {
self.prepend_node(SyntaxTree::new(value));
}
/// Insert the given SyntaxTree into the children of this tree at the given index
pub fn insert_node(&mut self, index: usize, new_node: SyntaxTree<T>) {
self.children.insert(index, new_node);
}
/// Create a new SyntaxTree with a root node that carries the given value.
/// Insert the created SyntaxTree into the children of this tree at the given index
pub fn insert_value(&mut self, index: usize, value: T) {
self.insert_node(index, SyntaxTree::new(value));
}
/// Perform a depth-first search with the given predicate.
/// The method returns a reference to the first SyntaxTree instance for which the predicate
/// return true. If no instance is found, None is returned.
pub fn find_node(&self, predicate: fn(&SyntaxTree<T>) -> bool) -> Option<&SyntaxTree<T>> {
if predicate(self) {
Some(self)
} else {
todo!()
}
}
/// Perform a depth-first search with the given predicate.
/// The method returns a mutable reference to the first SyntaxTree instance for which the predicate
/// return true. If no instance is found, None is returned.
pub fn find_node_mut(
&mut self,
predicate: fn(&SyntaxTree<T>) -> bool,
) -> Option<&mut SyntaxTree<T>> {
todo!()
}
/// Return a reference to the value carried by the root of this tree
pub fn value(&self) -> &T {
&self.value
}
/// Return the id of the root of this tree
pub fn id(&self) -> ID {
self.id
}
/// Return a reference to the children of this tree
pub fn children(&self) -> &Vec<SyntaxTree<T>> {
&self.children
}
}
impl<T: Display> SyntaxTree<T> {
pub fn print(&self) -> String {
if self.children.is_empty() {
format!("{}", self.value)
} else {
format!(
"{}\n[\n{}\n]",
self.value,
&self
.children
.iter()
.map(|tn| tn.print_inner(1))
.collect::<Vec<String>>()
.join(",\n")
)
}
}
pub fn print_inner(&self, indent: usize) -> String {
let mut indentation = String::new();
for _ in 0..indent {
indentation.push_str(" ");
}
if self.children.is_empty() {
format!("{}{}", &indentation, self.value)
} else {
format!(
"{}{}\n{}[\n{}\n{}]",
&indentation,
self.value,
&indentation,
&self
.children
.iter()
.map(|tn| tn.print_inner(indent + 1))
.collect::<Vec<String>>()
.join(",\n"),
indentation,
)
}
}
}
impl<T: Display> Display for SyntaxTree<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "({})", self.print())
}
}
#[cfg(test)]
mod tests {
use super::*;
fn fill_tree_numbers() -> SyntaxTree<i32> {
let mut tree = SyntaxTree::new(0);
for child in 1..3 {
let mut child = SyntaxTree::new(child);
for grandchild in 1..3 {
let id = grandchild * 10;
child.prepend_node(SyntaxTree::new(id));
}
tree.push_node(child);
}
tree
}
fn fill_tree_words() -> SyntaxTree<String> {
let mut tree = SyntaxTree::new(to_s("root"));
for (child_id, child) in ["first", "second", "third"].iter().map(to_s).enumerate() {
let child_id = child_id;
let mut child = SyntaxTree::new(child);
if child_id == 0 {
let mut descendant1 = SyntaxTree::new(to_s("A"));
let mut descendant2 = SyntaxTree::new(to_s("B"));
let descendant3 = SyntaxTree::new(to_s("C"));
descendant2.push_node(descendant3);
descendant1.push_node(descendant2);
child.push_node(descendant1);
}
tree.push_node(child);
}
tree
}
#[test]
fn number_tree() -> Result<(), String> {
let tree = fill_tree_numbers();
println!("{}", tree);
assert_eq!(
String::from(
"0\n[\n 1\n [\n 20,\n 10\n ],\n 2\n [\n 20,\n 10\n ]\n]"
),
tree.print()
);
Ok(())
}
#[test]
fn word_tree() -> Result<(), String> {
let tree = fill_tree_words();
println!("{}", tree);
assert_eq!(
String::from("root\n[\n first\n [\n A\n [\n B\n [\n C\n ]\n ]\n ],\n second,\n third\n]"),
tree.print()
);
Ok(())
}
#[test]
fn find_node_by_value() -> Result<(), String> {
let tree = fill_tree_numbers();
assert!(tree.find_node(|n| n.value == 0).is_some());
let left = tree.find_node(|n| n.value == 1).unwrap();
assert!(left.find_node(|n| n.value == 10).is_some());
assert!(left.find_node(|n| n.value == 20).is_some());
let right = tree.find_node(|n| n.value == 2).unwrap();
assert!(right.find_node(|n| n.value == 10).is_some());
assert!(right.find_node(|n| n.value == 20).is_some());
Ok(())
}
#[test]
fn find_node_mut_by_value() -> Result<(), String> {
let mut tree = fill_tree_numbers();
assert!(tree.find_node_mut(|n| n.value == 0).is_some());
let left = tree.find_node_mut(|n| n.value == 1).unwrap();
left.value = 2;
println!("tree in test: {left}");
assert!(left.find_node(|n| n.value == 10).is_some());
assert!(left.find_node(|n| n.value == 20).is_some());
let right = tree.find_node(|n| n.value == 2).unwrap();
assert!(right.find_node(|n| n.value == 10).is_some());
assert!(right.find_node(|n| n.value == 20).is_some());
Ok(())
}
fn to_s<T: Display>(value: T) -> String {
format!("{}", value)
}
}
use std::fmt::{Display, Formatter};
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct ID(usize);
#[derive(Debug, PartialEq)]
pub struct Syntree<T> {
id: ID,
value: T,
children: Vec<Syntree<T>>,
}
// Complete the implementation
// Hint: Start with seek_node_mut
impl<'a, T> Syntree<T> {
pub fn new(value: T, id: ID) -> Syntree<T> {
todo!()
}
pub fn push_node(&mut self, parent_id: ID, new_node: Syntree<T>) -> Result<(), String> {
todo!()
}
pub fn prepend_node(&mut self, parent_id: ID, new_node: Syntree<T>) -> Result<(), String> {
todo!()
}
pub fn insert_node(
&mut self,
parent_id: ID,
index: usize,
new_node: Syntree<T>,
) -> Result<(), String> {
todo!()
}
// Anmerkung: `'a` Is ein Lebenszeit angabe für die Referenzen
// Hier wird einfach nur explizit gesagt: Solange `self` lebt, lebt auch die Referenz im Rückgabewert
pub fn seek_node(&'a self, id: &ID) -> Option<&'a Syntree<T>> {
if self.id == *id {
Some(self)
} else {
for child in &self.children {
if let Some(result) = child.seek_node(id) {
return Some(result);
}
}
None
}
}
pub fn seek_node_mut(&'a mut self, id: &ID) -> Option<&'a mut Syntree<T>> {
todo!()
}
}
impl<T: Display> Syntree<T> {
pub fn print(&self) -> String {
if self.children.is_empty() {
format!("{}", self.value)
} else {
format!(
"{}-[{}]",
self.value,
&self
.children
.iter()
.map(|tn| tn.print())
.collect::<Vec<String>>()
.join(",")
)
}
}
}
impl<T: Display> Display for Syntree<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "({})", self.print())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fill_tree() -> Result<(), String> {
let mut tree = Syntree::new(0, ID(0));
for child in 1..3 {
let child_id = ID(child);
let mut child = Syntree::new(child, child_id);
for grandchild in 1..3 {
let id = grandchild * 10;
child.prepend_node(child_id, Syntree::new(id, ID(id)))?;
}
tree.push_node(ID(0), child)?;
}
println!("{}", tree);
assert_eq!(String::from("0-[1-[20,10],2-[20,10]]"), tree.print());
Ok(())
}
#[test]
fn fill_tree_words() -> Result<(), String> {
let mut tree = Syntree::new(to_s("root"), ID(0));
for (child_id, child) in ["first", "second", "third"].iter().map(to_s).enumerate() {
let child_id = ID(child_id);
let mut child = Syntree::new(child, child_id);
if child_id.0 == 0 {
let descendant = Syntree::new(to_s("A"), ID(4));
child.push_node(child_id, descendant)?;
let descendant = Syntree::new(to_s("B"), ID(5));
child.push_node(ID(4), descendant)?;
let descendant = Syntree::new(to_s("C"), ID(6));
child.push_node(ID(5), descendant)?;
}
tree.push_node(ID(0), child)?;
}
println!("{}", tree);
assert_eq!(
String::from("root-[first-[A-[B-[C]]],second,third]"),
tree.print()
);
Ok(())
}
fn to_s<T: Display>(value: T) -> String {
format!("{}", value)
}
#[test]
fn node_id_not_found() -> Result<(), String> {
let mut tree = Syntree::new(0, ID(0));
let child_id = ID(1);
let child = Syntree::new(1, child_id);
tree.push_node(ID(0), child)?;
let child_id = ID(2);
let child = Syntree::new(3, child_id);
assert!(tree.push_node(ID(5), child).is_err());
Ok(())
}
}