Die Antwort auf eine globale Variable wie diese ist schlecht. Sie sollten nur die Antwort zurückgeben und den Anrufer tun lassen, was er damit will. Wenn Sie spezielle (~ globale) Variablen verwenden, sollten Sie Sternchen um den Namen setzen (*ANSWER*
statt ANSWER
).
FORCE-OUTPUT
wird benötigt, um sicherzustellen, dass der Benutzer die Eingabeaufforderung tatsächlich sieht, bevor er antworten muss. Wenn ich die zweite Version mit SBCL in einem Terminal starte, friert das Programm einfach ein, um auf Eingaben zu warten, ohne etwas zu sagen.
*QUERY-IO*
sollte verwendet werden, um Dinge vom Benutzer abzufragen, weil einige Umgebung möglicherweise anders als andere Ausgabe behandeln möchten. Zum Beispiel könnte jemand einen GUI-Wrapper für Ihr Programm schreiben, der die Abfragen in grafische Dialoge verwandelt. Oder sie möchten es als Teil eines Skripts ausführen und die Eingabe aus einer Zeichenfolge bereitstellen.
(defun prompt-read (prompt)
(format *query-io* "~%~a: " prompt)
(force-output *query-io*)
(read-line *query-io*))
(defun hello()
(format t "~&Hello ~a!~%" (prompt-read "What's your name")))
(defmacro with-input ((input) &body body)
`(let ((*query-io* (make-two-way-stream (make-string-input-stream ,input)
(make-string-output-stream))))
,@body))
(defun test()
(with-input ("jkiiski")
(hello))
(with-input ("rnso")
(hello)))
(test)
; Hello jkiiski!
; Hello rnso!
bearbeiten
Ein komplexeres Beispiel SBCLs Grauströme verwenden.
(defclass foo-stream (sb-gray:fundamental-character-input-stream)
((output-input-script :initarg :script :accessor foo-stream-script)
(output-stream :initarg :out :accessor foo-stream-out)
(current-input :initform nil :accessor foo-stream-current-input)))
(defmethod sb-gray:stream-read-char ((stream foo-stream))
(with-accessors ((input foo-stream-current-input)
(out foo-stream-out)
(script foo-stream-script)) stream
(when (or (null input)
(not (listen input)))
(let ((output (string-trim '(#\space #\newline)
(get-output-stream-string out))))
(setf input (make-string-input-stream
(format nil "~a~%"
(cdr (assoc output script :test #'string=)))))))
(read-char input)))
(defun prompt-read (prompt)
(format *query-io* "~%~a: " prompt)
(force-output *query-io*)
(read-line *query-io*))
(defun hello()
(format t "~&Hello ~a!~%" (prompt-read "What's your name"))
(format t "~&I'm ~a too!" (prompt-read "How are you"))
(format t "~&~a~%" (if (string-equal (prompt-read
"Do you want to delete all your files")
"yes")
"Deleting all files... (not really)"
"Not deleting anything.")))
(defmacro with-input-script ((script) &body body)
(let ((out-sym (gensym "out")))
`(let* ((,out-sym (make-string-output-stream))
(*query-io* (make-two-way-stream
(make-instance 'foo-stream
:out ,out-sym
:script ,script)
,out-sym)))
,@body)))
(defun test()
(with-input-script ('(("What's your name:" . "jkiiski")
("How are you:" . "great")
("Do you want to delete all your files:" . "No")))
(hello))
(with-input-script ('(("What's your name:" . "Foo Bar")
("How are you:" . "fine")
("Do you want to delete all your files:" . "Yes")))
(hello)))
(test)
; Hello jkiiski!
; I'm great too!
; Not deleting anything.
; Hello Foo Bar!
; I'm fine too!
; Deleting all files... (not really)