2013-11-29 15 views
5

read-line e read-char entrambi richiedono di premere il tasto Invio dopo aver digitato qualcosa. Esiste un meccanismo in Common Lisp che consenta al programma di continuare immediatamente sulla stampa di un singolo carattere, senza richiedere il passaggio aggiuntivo di premere Invio?Leggere un carattere senza richiedere il pulsante Invio premuto

Sto cercando di creare un'interfaccia di immissione del testo rapida e dinamica per un programma in modo che gli utenti possano spostarsi rapidamente e fare cose diverse premendo numeri o lettere corrispondenti ai menu sullo schermo. Tutte le pressioni extra del tasto Invio interrompono seriamente il flusso di lavoro. Questo sarebbe anche simile a un tipo di interrogazione "y/n" da un prompt, in cui è sufficiente premere "y" o "n".

Sto usando SBCL, se questo fa la differenza. Forse questo è specifico per l'implementazione, poiché ho provato entrambi gli esempi su this page e non funziona (devo ancora premere Invio); ecco il primo:

(defun y-or-n() 
(clear-input *standard-input*) 
(loop as dum = (format t "Y or N for yes or no: ") 
    as c = (read-char) 
    as q = (and (not (equal C#\n)) (not (equal C#\y))) 
    when q do (format t "~%Need Y or N~%") 
    unless q return (if (equal C#\y) 'yes 'no))) 

risposta

8

read-char non richiede di premere Invio. Ad esempio,

CL-USER> (with-input-from-string (x "hello") 
      (print (read-char x))) 

#\h 

Allo stesso modo, se si invia un input in SBCL dalla riga di comando, verrà letto senza una nuova riga:

$ echo -n hello | sbcl --eval "(print (read-char))" 
… 
#\h 

Dopo aver letto e la stampa #\h, SBCL ha visto la ello :

* 
debugger invoked on a UNBOUND-VARIABLE in thread #<THREAD 
                "initial thread" RUNNING 
                {1002979011}>: 
    The variable ELLO is unbound. 

credo che questo sia sufficiente per confermare che non è che ha bisogno di un read-char newline, ma piuttosto che il buffering dell'input è il problema. Penso che questo sia lo stesso problema (o non problema) descritto in un thread comp.lang.lisp del 2008: Re: A problem with read-char. L'utente chiede:

E 'possibile effettuare read-char si comportano come getch in С quando si lavora con flusso interattivo (standard di ingresso)? In SBCL read-char vuole "invio" chiave per unhang da REPL, in C getchar restituisce immediatamente dopo l'utente premere il tasto sulla tastiera. Probabilmente è possibile eseguire il codice che utilizza read-char con accesso diretto alla console, a parte REPL?

Ci sono state quattro risposte (vedere thread index per visualizzarle tutte). Questi spiegano perché si osserva questo comportamento (cioè, che il processo Lisp non riceve input raw dal terminale, ma piuttosto input bufferizzato). Pascal Bourguignon described the problem, e un modo per gestire questa situazione con CLISP (ma non prevede più di tanto aiuto, a parte i soliti buoni consigli) di lavorare intorno a questo in SBCL:

La differenza è che le maledizioni mette il terminale in modalità raw per poter ricevere i caratteri dalla tastiera uno alla volta, invece di lasciando il terminale in modalità cotta, dove il driver unix bufferizza le righe e gestisce il backspace, tra le altre sottigliezze.

...

Ora, io non so SBCL, (consultare il manuale del SBCL). Ho solo le note di implementazione di CLISP caricate nel mio wetware. In CLISP è possibile utilizzare utilizzando la macro EXT: WITH-KEYBOARD (mentre l'output di base include di curses forniti dal pacchetto SCREEN).

Rob Warnock's response incluso un codice soluzione per CMUCL che potrebbe o non potrebbe funzionare per SBCL:

una volta ho scritto quanto segue per CMUCL per un'applicazione che voleva essere in grado di digitare una singola risposta carattere ad un richiamo senza rovinare schermo del terminale:

(defun read-char-no-echo-cbreak (&optional (stream *query-io*)) 
    (with-alien ((old (struct termios)) 
       (new (struct termios))) 
    (let ((e0 (unix-tcgetattr 0 old)) 
      (e1 (unix-tcgetattr 0 new)) 
      (bits (logior tty-icanon tty-echo tty-echoe 
         tty-echok tty-echonl))) 
     (declare (ignorable e0 e1)) ;[probably should test for error here] 
     (unwind-protect 
      (progn 
      (setf (slot new 'c-lflag) (logandc2 (slot old 'c-lflag) bits)) 
      (setf (deref (slot new 'c-cc) vmin) 1) 
      (setf (deref (slot new 'c-cc) vtime) 0) 
      (unix-tcsetattr 0 tcsadrain new) 
      (read-char stream)) 
     (unix-tcsetattr 0 tcsadrain old))))) 

SBCL ha probabilmente discostato notevolmente da CMUCL in questa zona, ma 0.123.516,410617 millionsqualcosa di simile dovrebbe essere fattibile con SBCL. Inizia cercando nel forse i pacchetti SB-POSIX SB-UNIX o ...

User vippstar's response fornito un link per quello che potrebbe essere la soluzione più portatile

Dal momento che si vuole fare qualcosa che potrebbe non essere portabile a un microcontrollore (ma il benifit è l'interfaccia utente molto più avanzata), utilizzare una libreria non standard , come ad esempio CL-ncurses.

+2

Eccellente, quasi quello che stavo pensando quando ho visto la domanda. Ora non devo scrivere una risposta. – Vatine

0

Ho trovato cl-charms, che sembra essere una biforcazione delle cl-maledizioni abbandonate. Tuttavia, il programma di esempio incluso charms-paint utilizza CPU al 100% per eseguire l'applicazione di disegno banale. Il problema sembra essere che il ciclo principale è occupato e attende input.

Problemi correlati