Читать книгу JavaScript – Das Handbuch für die Praxis - David Flanagan - Страница 16
Beispiel 1-1: Berechnung von Häufigkeitshistogrammen mit JavaScript
Оглавление/**
* Dieses Node-Programm liest Text aus der Standardeingabe, berechnet für
* jedes Zeichen in diesem Text die Häufigkeit seines Vorkommens und zeigt
* ein Histogramm der am häufigsten verwendeten Zeichen. Zur Ausführung ist
* Node 12 oder höher erforderlich.
*
* In einer Unix-artigen Umgebung können Sie das Programm wie folgt aufrufen:
* node charfreq.js < corpus.txt
*/
// Diese Klasse erweitert Map so, dass die get()-Methode statt null den angegebenen
// Wert zurückgibt, falls der Schlüssel key nicht in der Map vorkommt.
class DefaultMap extends Map {
constructor(defaultValue) {
super(); // Ruft den Konstruktor der Superklasse auf.
this.defaultValue = defaultValue; // Standardwert merken.
}
get(key) {
if (this.has(key)) { // Wenn der Schlüssel bereits in der Map
// vorkommt,
return super.get(key); // wird dessen Wert aus der Superklasse
// zurückgegeben.
}
else {
return this.defaultValue; // Gibt ansonsten den Standardwert zurück.
}
}
}
// Diese Klasse berechnet und zeigt Buchstabenhäufigkeitshistogramme.
class Histogram {
constructor() {
this.letterCounts = new DefaultMap(0); // Zuordnung von Buchstaben
// zu Zählwerten.
this.totalLetters = 0; // Wie viele Buchstaben insgesamt?
}
// Diese Funktion aktualisiert das Histogramm mit den Zeichen des Texts.
add(text) {
// Entfernt Leerzeichen aus dem Text und konvertiert in Großbuchstaben.
text = text.replace(/\s/g, "").toUpperCase();
// Schleife über die Zeichen des Texts.
for(let character of text) {
let count = this.letterCounts.get(character); // Alten Zählwert auslesen.
this.letterCounts.set(character, count+1); // Inkrementieren.
this.totalLetters++;
}
}
// Konvertiert das Histogramm in eine Zeichenfolge, die eine ASCII-Grafik anzeigt.
toString() {
// Konvertiert die Map in ein Array von Schlüssel/Wert-Arrays.
let entries = […this.letterCounts];
// Sortiert das Array zuerst nach Anzahl und dann alphabetisch.
entries.sort((a,b) => { // Eine Funktion zur Festlegung der
// Sortierreihenfolge.
if (a[1] === b[1]) { // Wenn die Zählwerte identisch ist.
return a[0] < b[0] ? -1 : 1; // Alphabetisch sortieren.
} else { // Wenn die Zählwerte abweichen.
return b[1] - a[1]; // Sortierung nach der höchsten Anzahl.
}
});
// Rechnet die Ergebnisse in Prozentzahlen um.
for(let entry of entries) {
entry[1] = entry[1] / this.totalLetters*100;
}
// Alle Einträge unter 1 % fallen lassen.
entries = entries.filter(entry => entry[1] >= 1);
// Konvertiert jeden Eintrag in eine Textzeile.
let lines = entries.map(
([l,n]) => `${l}: ${"#".repeat(Math.round(n))} ${n.toFixed(2)}%`
);
// Und gibt die verketteten Zeilen, getrennt durch Zeilenumbruchzeichen,
// zurück.
return lines.join("\n");
}
}
// Diese asynchrone (ein Promise zurückgebende) Funktion erzeugt ein Histogrammobjekt,
// liest Textblöcke asynchron aus der Standardeingabe und fügt diese Blöcke dem
// Histogramm hinzu. Wenn das Ende des Streams erreicht wird, gibt es das Histogramm
// zurück.
async function histogramFromStdin() {
process.stdin.setEncoding("utf-8"); // Unicode-Strings lesen, nicht Bytes.
let histogram = new Histogram();
for await (let chunk of process.stdin) {
histogram.add(chunk);
}
return histogram;
}
// Die folgende letzte Codezeile ist der Hauptteil des Programms.
// Darin wird aus der Standardeingabe ein Histogrammobjekt erstellt und ausgegeben.
histogramFromStdin().then(histogram => { console.log(histogram.toString()); });