Seid froh, dass Python eine "blockierende" `input`-Funktion hat.
Vor zwei Jahren hatte ich mal einen Basic-Interpreter für den Spieleklassiker Hammurabi in Python gebaut. Das Spiel funktioniert in der Konsole, stellt dem Benutzer fragen, erwartet seine Antwort per `input` und verarbeitet sie entsprechend. Nichts besonderes sollte man meinen.
Hier ist ein simples Beispiel für ein Spiel, das in einer Schleife Eingaben verarbeitet:
Code: Alles auswählen
def zahlenraten(n):
while True:
z = input("Rate die Zahl: ")
if n == z: print "Richtig!"; return
print "Die Zahl ist", "kleiner" if n < z else "größer"
Nennen wir diese Funktion `async_input`. In Python könnte ich sie so simulieren:
Code: Alles auswählen
def async_input(prompt, callback):
callback(input(prompt))

Code: Alles auswählen
var buffer = "", pending = null, eof = false;
// by default, encoding is binary but we want unicode strings
process.stdin.setEncoding('utf8');
// collect chunks of data (typically lines, but nobody guarantees that)
process.stdin.on('data', function (data) {
buffer += data;
if (pending) read(pending);
});
// somebody pressed ^D or if stdin has been redirected, EOF
process.stdin.on('end', function () {
buffer += '\n';
if (pending) read(pending);
eof = true;
});
// by default, the stream is paused, so after setting up, resume it
process.stdin.resume();
/**
* Reads the next line from stdin.
* @param {function(string|null)} callback
*/
function read(callback) {
if (eof) return callback(null);
var i = buffer.indexOf('\n');
if (i !== -1) {
var line = buffer.slice(0, i);
buffer = buffer.slice(i + 1);
pending = null;
callback(line);
} else {
pending = callback;
}
}
module.exports = read;
Code: Alles auswählen
def zahlenraten(n):
z = input("Rate die Zahl: ")
if n == z: print "Richtig!"
else:
print "Die Zahl ist", "kleiner" if n < z else "größer"
zahlenraten(n)
Code: Alles auswählen
def zahlenraten(n):
def callback(z):
if n == z: print "Richtig!"
else:
print "Die Zahl ist", "kleiner" if n < z else "größer"
zahlenraten(n)
async_input("Rate die Zahl: ", callback)

Code: Alles auswählen
function zahlenraten(n) {
print("Rate die Zahl: ");
read(function (z) {
z = +z;
if (z === n) print("Richtig!");
else {
print("Die Zahl ist " + (n < z ? "kleiner" : "größer"));
zahlenraten(n);
}
});
Bei meinem Basic-Interpreter wollte ich jetzt nicht das gesamte Programm transformieren. Glücklicherweise ist `input` dort keine Funktion (die ich tief verschachtelt in einer Behandlung von Ausdrücken hätte verarbeiten müssen) sondern eine Anweisung. Diese verarbeite ich in einer `while`-Schleife ähnlich der, die wir schon gesehen haben:
Code: Alles auswählen
class Basic:
...
def run(self):
while True:
token = self.next()
if token == "end": break
if token == "input": self.doINPUT()
if token == "goto": self.doGOTO()
...
def doINPUT(self):
name = self.next()
self.expect(":")
self.variables[name] = float(raw_input("? "))
def doGOTO(self):
n = self.next()
self.expect(":")
self.goto(n)
Code: Alles auswählen
class Basic:
...
def run(self, cont):
token = self.next()
if token == "end": return cont()
cont = bind(self.run, cont)
if token == "input": self.doINPUT(cont)
if token == "goto": self.doGOTO(cont)
...
def doINPUT(self, cont):
def callback(z):
self.variables[name] = float(z)
cont()
name = self.next()
self.expect(":")
async_input("? ", callback)
def doGOTO(self, cont):
n = self.next()
self.expect(":")
self.goto(n)
cont()
Code: Alles auswählen
class Basic:
...
def run(self):
while not self.step(): pass
def step(self):
token = self.next()
if token == "end": return True
if token == "input": return self.doINPUT()
if token == "goto": return self.doGOTO()
...
So sieht das neue `doINPUT` aus - man beachte, wie ich dort mit `self.run()` die Verarbeitung wieder starte:
Code: Alles auswählen
def doINPUT(self):
def callback(z):
self.variables[name] = float(z)
self.run()
name = self.next()
self.expect(":")
async_input("? ", callback)
return True
In JavaScript (ich habe mir erlaubt, die `do`-Methode zu einer `switch`-Anweisung zu machen) sieht das Endergebnis dann so aus:
Code: Alles auswählen
Basic.prototype.run = function () {
while (!this.step());
};
Basic.prototype.step = function () {
var token = this.next();
switch (token) {
case 'END': return true;
case 'INPUT':
var name = this.next(), that = this;
this.expect(":");
print("? ");
read(function (z) {
that.variables[name] = z;
that.run();
});
return true;
case 'GOTO:
var line = this.next();
this.expect(":");
this.goto(line);
return;
...
}
};
Alles in allem ist Python die bessere Sprache für Experimente mit Konsolen-Anwendungen solange es nur JavaScript-Implementierungen wie Node.js gibt, die ausschließlich asynchrones (evented) I/O anbieten.
Stefan