Malerwerkst.at

Rust - Advent of Code 2015 - Tag 5

Introduction

R.


Rust AoC-2015

Rust - Advent of Code 2015 - Tag 5

Posted by R. on .
Featured

Rust AoC-2015

Rust - Advent of Code 2015 - Tag 5

Posted by R. on .

Nach einer kleinen "kreativen Pause" wird es mal wieder Zeit für ein paar Zeilen Code in Rust. Beginnen wir wieder mit der Aufgabenstellung:

Aufgabenstellung

Santa needs help figuring out which strings in his text file are naughty or nice.

A nice string is one with all of the following properties:

It contains at least three vowels (aeiou only), like aei, xazegov, or aeiouaeiouaeiou. It contains at least one letter that appears twice in a row, like xx, abcdde (dd), or aabbccdd (aa, bb, cc, or dd). It does not contain the strings ab, cd, pq, or xy, even if they are part of one of the other requirements.

For example:

  • ugknbfddgicrmopn is nice because it has at least three vowels (u...i...o...), a double letter (...dd...), and none of the disallowed substrings.
  • aaa is nice because it has at least three vowels and a double letter, even though the letters used by different rules overlap.
  • jchzalrnumimnmhp is naughty because it has no double letter.
  • haegwjzuvuyypxyu is naughty because it contains the string xy.
  • dvszwmarrgswjxmb is naughty because it contains only one vowel.

How many strings are nice?

Gemäß der Aufgabenstellung sollen wir also verschiedene Textzeilen nach erlaubten und verbotenen Mustern durchsuchen. Was könnte sich besser dafür eignen, als Regex (= regular expression bzw. Reguläre Ausdrücke)?

Mit drei einfachen Abfragen in der passenden Regex-Syntax können wir die Zeichenketten auf ihre "Niceheit" hin untersuchen.

  • Regel 1: Eine gute Zeichenkette enthält drei Vokale - (.*[aeiou].*){3,}
  • Regel 2: Wenigstens ein Buchstabe muss doppelt vorkommen - (\w)\\1
  • Regel 3: Bestimmte Buchstabenkombinationen dürfen nicht vorkommen - ab|cd|pq|xy

Im Gegensatz zu vielen Skriptsprachen, unterstützt Rust Regex nicht von Haus aus. Man ist auf die Hilfe eines externen Moduls angewiesen, in diesem Fall des regex-crates. Leider bildet diese Implementierung nicht den gesamten "Sprachumfang" der regulären Ausdrücke ab - so fehlen zum Beispiel das gerade für Regel 2 notwendige backreferencing oder look ahead / look behind Konstrukte.
Ansonsten ist die Implementierung recht vollständig und leicht anzuwenden. Die Umsetzung für Regel 1 sieht dann in Rust ungefähr so aus:

extern crate regex;  
use regex::Regex;

let g1 = Regex::new(r"(.*[aeiou].*){3,}").unwrap();  
if g1.is_match("Test-String mit mehr als drei Vokalen") {  
    println!("String enthält mehr als 3 Vokale");
}

Da ich Regel 2 nicht mit regulären Ausdrücken abbilden konnte, was den Code merklich verkürzt hätte, musste ich dafür eine eigene Funktion schreiben, die einen boolschen Wert zurück liefert, je nachdem ob ein übergebener String, eine gewisse Anzahl an sich wiederholenden Buchstaben aufweist. Insbesondere hier wäre ich interessiert, ob es da nicht eine elegantere / einfachere Möglichkeit gibt!

Der komplette Lösungscode könnte dann so aussehen:

Code

extern crate regex;

use std::fs::File;  
use std::io::Read;  
use regex::Regex;  
use std::collections::HashSet;

fn get_data(fname: &str) -> String {  
    let mut file = match File::open(fname) {
        Err(e) => panic!("file error: {}", e),
        Ok(file) => file,
    };

    let mut data = String::new();

    match file.read_to_string(&mut data) {
        Err(e) => panic!("read error: {}", e),
        Ok(_) => {}
    }
    data
}

fn has_multiple(in_str: &str, anz: usize) -> bool {  
    let mut got_mult = false;
    let mut set = HashSet::new();

    for i in 0..in_str.len() - (anz - 1) {
        set.clear();
        for c in in_str[i..i + anz].chars() {
            set.insert(c);
        }
        if set.len() == 1 {
            got_mult = true;
            break;
        }
    }
    got_mult
}

fn main() {  
    let input_str = get_data("src/input.txt");

    let g1 = Regex::new(r"(.*[aeiou].*){3,}").unwrap();
    let bad = Regex::new(r"ab|cd|pq|xy").unwrap();

    let mut count = 0;

    for l in input_str.lines() {
        if g1.is_match(l) && !bad.is_match(l) {
            if has_multiple(l, 2) {
                count += 1;
            }
        }
    }

    println!("{}", count);
}

Wie immer an dieser Stelle der Aufruf, gerne den ein oder anderen Kommentar hier zu lassen! :-)

R.

View Comments...