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 TrueIn 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
