Tracks
/
Factor
Factor
/
Übungen
/
Hauptbuch-Wächter
Hauptbuch-Wächter

Hauptbuch-Wächter

Lernübung

Einführung

Du kennst schon die Grundlagen: Zeichenklassen wie \d und [A-Z], Quantifizierer wie {4} und + und die Wörter matches?, all-matching-subseqs und re-replace. Diese Übung ergänzt die Teile regulärer Ausdrücke, die Struktur und Kontext beschreiben. Sie stecken im selben Vokabular regexp, geschrieben mit demselben Literal R/ /.

Anker

Anker finden eine Position statt eines Zeichens:

^    ! the start of the string
$    ! the end of the string
\b   ! a word boundary

Weil $ das Ende des Strings bedeutet, schreibst du \$, wenn du ein wörtliches Dollarzeichen meinst.

Eine Wortgrenze (\b) ist die Stelle, an der ein Wortzeichen auf ein Nicht-Wortzeichen trifft. Wortzeichen sind Buchstaben, Ziffern und der Unterstrich; alles andere (Leerzeichen, Satzzeichen und die beiden Enden des Strings) gilt als Grenze. \bcat\b findet cat also nur, wenn es allein steht:

USING: regexp ;

"cat nap" R/ \bcat\b/ re-contains? .   ! => t
"scatter" R/ \bcat\b/ re-contains? .   ! => f   (cat sits inside a word)

Gruppierung und Alternation

Runde Klammern ( ) fassen einen Teil eines Musters zu einer Gruppe zusammen, damit ein Quantifizierer oder ein | („dieses oder jenes“) für die ganze Gruppe gilt:

USING: regexp ;

"abab" R/ (ab)+/ matches? .                ! => t   (+ applies to "ab")
"dog"  R/ cat|dog/ matches? .              ! => t   (either word)
"$100.50" R/ \$\d+(\.\d{2})?/ matches? .   ! => t
"$100"    R/ \$\d+(\.\d{2})?/ matches? .   ! => t   (the group is optional)

Das letzte Muster liest sich als „ein Dollarzeichen, eine oder mehrere Ziffern, dann eine optionale Gruppe aus einem Punkt und zwei Ziffern“.

Hinweis: In Factor strukturiert eine Gruppe das Muster nur. Anders als bei manchen Regex-Werkzeugen kannst du den Text, den eine Gruppe gefunden hat, nicht wieder herausziehen: Es gibt keine Extraktion von Capture-Gruppen, und Backreferences wie \1 werden nicht unterstützt. Um Text um einen Bezugspunkt herum herauszupicken, nutze stattdessen Lookaround (siehe unten).

Lookaround: über den Kontext finden, ohne ihn zu verbrauchen

Ein Lookaround prüft, was direkt vor oder direkt nach der aktuellen Stelle kommt, ohne diese Zeichen zum Treffer zu machen. Er ist „nullbreit“: Was er prüft, ist nicht Teil des Ergebnisses.

(?=...)    ! lookahead:           ... must follow
(?!...)    ! negative lookahead:  ... must NOT follow
(?<=...)   ! lookbehind:          ... must come before
(?<!...)   ! negative lookbehind: ... must NOT come before

Lookaround ist selektiv: Dieselben Zeichen passen oder passen nicht, je nachdem, was daneben steht. Ein einfaches \d+ schnappt sich jede Zahl; Lookbehind und Lookahead behalten nur die mit dem passenden Nachbarn:

USING: regexp ;

"3 for $10 or 10 for $30" R/ \d+/ all-matching-subseqs .
! => { "3" "10" "10" "30" }   (every number)

"3 for $10 or 10 for $30" R/ (?<=\$)\d+/ all-matching-subseqs .
! => { "10" "30" }            (only the prices — the bare 3 and 10 drop)

"buy 2 at 15% off, 4 at 30% off" R/ \d+(?=%)/ all-matching-subseqs .
! => { "15" "30" }            (only the discounts — the bare 2 and 4 drop)

Optionen

Füge einen Buchstaben nach dem schließenden / an, um zu ändern, wie sich das ganze Muster verhält. i macht es unabhängig von Groß- und Kleinschreibung:

USING: regexp ;

"REFUND"  R/ refund/i matches? .       ! => t
"Refund"  R/ refund/i matches? .       ! => t

Jeden Treffer umwandeln

all-matching-subseqs liefert dir den gefundenen Text. map-matches geht einen Schritt weiter: Es führt für jeden Treffer eine Quotation aus und sammelt die Ergebnisse. Die Quotation bekommt den start-Index des Treffers, seinen end-Index und den ganzen String; subseq macht aus diesen dreien den gefundenen Text, den du dann umwandelst:

map-matches ( string regexp quot: ( start end string -- obj ) -- seq )
USING: math regexp sequences ;

"a1 b22 c333" R/ \d+/ [ subseq length ] map-matches .
! => { 1 2 3 }     (the length of each run of digits)

Sein nur auf Nebenwirkungen ausgelegter Verwandter, each-match, führt für jeden Treffer eine Quotation aus, ohne etwas zu sammeln.

Anleitung

Du führst die Bücher für ein kleines Unternehmen, und jeden Tag kommen die Transaktionen als unordentliche Textzeilen herein. Du verwendest reguläre Ausdrücke, um Beträge zu validieren, Zahlen anhand des Symbols herauszuziehen, das sie kennzeichnet, und Zeilen zu markieren, die genauer angesehen werden müssen.

1. Validiere einen Betrag

Ein wohlgeformter Betrag besteht aus einem $, einer oder mehreren Ziffern und einem optionalen Cent-Teil aus einem Punkt gefolgt von genau zwei Ziffern: $100 oder $100.50, aber nicht $100.5. Definiere valid-amount?, das t zurückgibt, wenn der ganze String ein wohlgeformter Betrag ist.

"$100.50" valid-amount? .   ! => t
"$100"    valid-amount? .   ! => t
"$100.5"  valid-amount? .   ! => f

2. Ziehe die Dollar-Beträge heraus

Definiere dollar-amounts, das jede Zahl zurückgibt, die in der Zeile direkt auf ein $ folgt, nur die Ziffern, ohne das $, und zwar in der Reihenfolge ihres Auftretens.

"spent $100 and $25 today" dollar-amounts .
! => { "100" "25" }

3. Ziehe die Prozentangaben heraus

Definiere percentages, das jede Zahl zurückgibt, auf die unmittelbar ein % folgt.

"up 5% then down 12%" percentages .
! => { "5" "12" }

4. Markiere eine Zeile

Definiere flagged?, das t zurückgibt, wenn die Zeile refund oder chargeback in beliebiger Groß- und Kleinschreibung erwähnt.

"Issued a REFUND today" flagged? .   ! => t
"a normal sale"         flagged? .   ! => f
Über GitHub bearbeiten Der Link öffnet sich in einem neuen Fenster oder Tab
Factor Exercism

Bereit, mit Hauptbuch-Wächter zu starten?

Melde dich bei Exercism an, um Factor mit 47 Konzepte163 Übungen und echtem menschlichen Mentoring zu lernen und zu meistern, alles kostenlos.