Keyboard input/Obtain a Y or N response: Difference between revisions
Walterpachl (talk | contribs) (→version 1: NY shouldn't be a valid input) |
Walterpachl (talk | contribs) m (→version 1: --~~~~ didn't work here) |
||
Line 460: | Line 460: | ||
/*stick a fork in it, we're all done. */</lang> |
/*stick a fork in it, we're all done. */</lang> |
||
<pre> |
<pre> |
||
This program accepts NY as valid input. It shouldn't!! |
This program accepts NY as valid input. It shouldn't!! 03.02.2013 Walter Pachl |
||
</pre> |
</pre> |
||
Revision as of 18:02, 3 February 2013
You are encouraged to solve this task according to the task description, using any language you may know.
Obtain a valid Y or N response from the keyboard. The keyboard should be flushed, so that any outstanding keypresses are removed, preventing any existing Y or N keypress from being evaluated. The response should be obtained as soon as Y or N are pressed, and there should be no need to press an enter key.
Ada
<lang Ada> function Yes_Or_No (Prompt : String := "Your answer (Y/N): ") return Boolean is
Answer : Character; begin Ada.Text_IO.Put (Prompt); loop Ada.Text_IO.Get_Immediate (Answer); case Answer is when 'Y'|'y' => return True; when 'N'|'n' => return False; when others => null; end case; end loop; end Yes_Or_No;</lang>
AutoHotkey
<lang AutoHotkey>#NoEnv ; Recommended for increased performance Input, Response,,,y,n
- Waits until they press y or n, storing all characters.
StringRight, Out, Response, 1
- retrieves the ending character (which will be y or n)
Msgbox %out% If (Out = "y")
Msgbox You pressed Y
If (Out = "n")
Msgbox You pressed n
ExitApp </lang>
BASIC
Applesoft BASIC
<lang applesoftbasic>10 LET C = PEEK (49168): REM CLEAR KEYBOARD 20 PRINT "PRESS Y OR N TO CONTINUE" 30 GET K$ 40 IF K$ < > "Y" AND K$ < > "N" THEN 30 50 PRINT "THE RESPONSE WAS ";K$ </lang>
Locomotive Basic
<lang locobasic>10 CLEAR INPUT 20 PRINT "Press Y or N to continue" 30 a$=LOWER$(INKEY$) 40 IF a$="" THEN 30 50 IF a$="y" THEN PRINT "Yes":END 60 IF a$="n" THEN PRINT "No":END 70 PRINT "Try again" 80 GOTO 30</lang>
ZX Spectrum Basic
Note that this will also work in GW-BASIC and most QBasic-compatible BASICs if all instances of "GO TO
" are changed to "GOTO
".
<lang qbasic>10 IF INKEY$<>"" THEN GO TO 10: REM flush the keyboard buffer 20 PRINT "Press Y or N to continue" 30 LET k$ = INKEY$ 40 IF k$ <> "y" AND k$ <> "Y" AND k$ <> "n" AND k$ <> "N" THEN GO TO 30 50 PRINT "The response was "; k$</lang>
BBC BASIC
<lang bbcbasic> REPEAT UNTIL INKEY$(0) = ""
PRINT "Press Y or N to continue" REPEAT key$ = GET$ UNTIL key$="Y" OR key$="N" PRINT "The response was " key$</lang>
C
For POSIX compliant systems (in theory that includes WinNT family). <lang C>#include <stdio.h>
- include <stdio.h>
- include <termios.h>
- include <unistd.h>
- include <fcntl.h>
void set_mode(int want_key) { static struct termios old, new; if (!want_key) { tcsetattr(STDIN_FILENO, TCSANOW, &old); return; }
tcgetattr(STDIN_FILENO, &old); new = old; new.c_lflag &= ~(ICANON); tcsetattr(STDIN_FILENO, TCSANOW, &new); }
int get_key(int no_timeout) { int c = 0; struct timeval tv; fd_set fs; tv.tv_usec = tv.tv_sec = 0;
FD_ZERO(&fs); FD_SET(STDIN_FILENO, &fs);
select(STDIN_FILENO + 1, &fs, 0, 0, no_timeout ? 0 : &tv); if (FD_ISSET(STDIN_FILENO, &fs)) { c = getchar(); set_mode(0); } return c; }
int main() { int c; while(1) { set_mode(1); while (get_key(0)); /* clear buffer */ printf("Prompt again [Y/N]? "); fflush(stdout);
c = get_key(1); if (c == 'Y' || c == 'y') { printf("\n"); continue; }
if (c == 'N' || c == 'n') { printf("\nDone\n"); break; }
printf("\nYes or no?\n"); }
return 0; }</lang>
C#
<lang c sharp>using System;
namespace Y_or_N {
class Program { static void Main() { bool response = GetYorN(); }
static bool GetYorN() { ConsoleKey response; // Creates a variable to hold the user's response.
do { while (Console.KeyAvailable) // Flushes the input queue. Console.ReadKey();
Console.Write("Y or N? "); // Asks the user to answer with 'Y' or 'N'. response = Console.ReadKey().Key; // Gets the user's response. Console.WriteLine(); // Breaks the line. } while (response != ConsoleKey.Y && response != ConsoleKey.N); // If the user did not respond with a 'Y' or an 'N', repeat the loop.
/* * Return true if the user responded with 'Y', otherwise false. * * We know the response was either 'Y' or 'N', so we can assume * the response is 'N' if it is not 'Y'. */ return response == ConsoleKey.Y; } }
}</lang>
Common Lisp
<lang lisp>(defun rosetta-y-or-n ()
(clear-input *query-io*) (y-or-n-p))</lang>
Euphoria
<lang Euphoria>integer key
puts(1,"Your answer? (Y/N)\n") while get_key()!=-1 do end while
while 1 do
key = get_key() if key!=-1 and (key = 'Y' or key = 'y' or key = 'N' or key = 'n') then exit end if
end while
printf(1,"Your response was %s\n",key)</lang>
EGL
<lang EGL>handler YesOrNoHandler type RUIhandler{initialUI =[ui], onConstructionFunction = start}
ui Div { };
const KEY_N int = 78; const KEY_Y int = 89;
function start() document.onKeyDown = d_onKeyDown; end function d_onKeyDown(e Event in)
case (e.ch)
when (KEY_N)
ui.innerText = "N pressed."; when (KEY_Y) ui.innerText = "Y pressed."; end
e.preventDefault();
end
end</lang>
Forth
<lang Forth>: flush ( -- ) \ discard pending input
begin key? while key drop repeat ;
- y-or-n ( c-addr u -- f )
flush begin cr 2dup type key bl or \ note 1. dup [char] y = swap [char] n = over or \ note 2. if nip nip exit then drop again ;
\ Note 1. KEY BL OR returns a lowercase letter in the case that an \ uppercase letter was entered, an unchanged lowercase letter in the \ case that a lowercase letter was entered, and garbage otherwise. BL \ returns the ASCII code for a space, 32, which is incidentally the \ "bit of difference" between ASCII uppercase and lowercase letters.
\ Note 2. this line has the stack effect ( x -- f1 f2 ), where F1 is \ true only if x='y', and F2 is true only if x='y' OR if x='n'.
\ I think these expressions aren't too clever, but they _are_ rather \ optimized for the task at hand. This might be more conventional:
- y-or-n ( c-addr u -- f )
flush begin cr 2dup type key case [char] y of 2drop true exit endof [char] Y of 2drop true exit endof [char] n of 2drop false exit endof [char] N of 2drop false exit endof endcase again ;</lang>
GW-BASIC
<lang qbasic>10 IF INKEY$<>"" THEN GOTO 10: REM flush the keyboard buffer 20 PRINT "Press Y or N to continue" 30 LET k$ = INKEY$ 40 IF k$ <> "y" AND k$ <> "Y" AND k$ <> "n" AND k$ <> "N" THEN GOTO 30 50 PRINT "The response was "; k$</lang>
Icon and Unicon
This solution works in both Icon and Unicon. It also accepts y or n. <lang unicon>procedure main()
write("Response was ",getResponse("OK? (Y or N): "))
end
procedure getResponse(prompt)
while kbhit() do getch() # flush input writes(prompt) repeat if map(answer := getch()) == ("y"|"n") then break return answer
end</lang>
Inform 7
Keyboard input goes through a virtual machine that's only required to provide blocking input operations, so flushing the buffer isn't possible.
Inform 7 has a built-in function to ask the user for yes-or-no input, but it requires them to press enter afterward: <lang inform7>Qwantz is a room.
When play begins: say "A wizard has turned you into a whale. Is this awesome (Y/N)? "; if the player consents, say "Awesome!"; end the story.</lang>
To read a single key without waiting for enter, we can redefine the function by including a snippet of Inform 6 code: <lang inform7>To decide whether player consents: (- (YesOrNoKey()) -).
Include (- [ YesOrNoKey ch;
do { ch = VM_KeyChar(); } until (ch == 'y' or 'Y' or 'n' or 'N'); return ch == 'y' or 'Y';
]; -).</lang>
Liberty BASIC
<lang lb> nomainwin open "Y/N" for graphics_nsb_nf as #1
- 1 "trapclose Quit"
- 1 "down;setfocus;when characterInput KeyCheck"
- 1 "place 10 50;\Press Y or N"
Inkey$="" wait
sub KeyCheck hndle$,k$
k$=upper$(k$) #hndle$ "cls;place 10 50" select case k$ case "Y" #hndle$ "\ Yes" case "N" #hndle$ "\No" case else #hndle$ "\Incorrect input. Press Y or N" end select end sub
sub Quit hndle$
close #hndle$ end end sub </lang>
OpenEdge/Progress
<lang progress>DEF VAR lanswer AS LOGICAL INITIAL ?.
DO WHILE lanswer = ?:
READKEY. IF CHR( LASTKEY ) = "n" OR CHR( LASTKEY ) = "y" THEN lanswer = CHR( LASTKEY ) = "y".
END.
MESSAGE lanswer VIEW-AS ALERT-BOX.</lang>
Pascal
<lang pascal>Program ObtainYN;
uses
crt;
var
key: char;
begin
write('Your answer? (Y/N): '); repeat key := readkey; until (key in ['Y', 'y', 'N', 'n']); writeln; writeln ('Your answer was: ', key);
end.</lang> Output:
% ./ObtainYN Your answer? (Y/N): Your answer was: y
Perl
<lang perl>use Term::ReadKey;
ReadMode 4; # change to raw input mode
my $key = ;
while($key !~ /(Y|N)/i) {
1 while defined ReadKey -1; # discard any previous input print "Type Y/N: "; $key = ReadKey 0; # read a single character print "$key\n";
}
ReadMode 0; # reset the terminal to normal mode
print "\nYou typed: $key\n"; </lang>
PicoLisp
<lang PicoLisp>(de yesno ()
(loop (NIL (uppc (key))) (T (= "Y" @) T) (T (= "N" @)) ) )</lang>
PureBasic
Inkey() returns the character string of the key which is being pressed at the time. <lang PureBasic>PrintN("Press Y or N to continue")
Repeat
; Get the key being pressed, or a empty string. Key$=UCase(Inkey()) ; ; To Reduce the problems with an active loop ; a Delay(1) will release the CPU for the rest ; of this quanta if no key where pressed. Delay(1)
Until Key$="Y" Or Key$="N" PrintN("The response was "+Key$)</lang>
Python
<lang python>#!/usr/bin/env python
try:
from msvcrt import getch
except ImportError:
def getch(): import sys, tty, termios fd = sys.stdin.fileno() old_settings = termios.tcgetattr(fd) try: tty.setraw(sys.stdin.fileno()) ch = sys.stdin.read(1) finally: termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) return ch
print "Press Y or N to continue" while True:
char = getch() if char.lower() in ("y", "n"): print char break</lang>
Retro
<lang Retro>
: y|n ( -c ) "\nPress Y or N..." puts 0 [ drop getc dup [ 'Y <> ] [ 'N <> ] bi and ] while cr ;
</lang>
REXX
version 1
REXX (in general) requires the user to press ENTER when entering text.
This is because (IBM) REXX was designed and written in a time when all I/O to a user's terminal was in
block mode and required the user to press one of the following before any data was sent to the computer:
- the ENTER key
- A PF (program function key)
- A PA (program assist key)
- the ATTN (attention) key
- possibly some other special key(s)
Some REXX interpreters have a keyboard read subroutine so that the program can
read keyboard keys as they are pressed (see version 2 below).
<lang rexx>/*REXX program to test for a Y or N key when pressed. */
do queued(); pull; end /*flush the stack if anything queued. */
prompt='Press Y or N for some reason.' /*the PROMPT message.*/
do until pos(ans,'NY')\==0 /*keep prompting until user answers Y | N*/ say; say prompt /*show blank line, then show the prompt. */ pull ans . /*get the answer(s), also, uppercase it. */ end /*until*/ /*stick a fork in it, we're all done. */</lang>
This program accepts NY as valid input. It shouldn't!! 03.02.2013 Walter Pachl
version 2
This version of a REXX program works with PC/REXX. <lang rexx>/*REXX program to test for a Y or N key when pressed. */ prompt='Press Y or N for some reason.' /*the PROMPT message.*/
do until pos(ans,'NY')\==0 /*keep prompting until user answers Y | N*/ say; say prompt /*show blank line, then show the prompt. */ ans=inkey('wait'); upper ans /*get the answer(s), also, uppercase it. */ end /*until*/ /*stick a fork in it, we're all done. */</lang>
Ruby
<lang Ruby> def yesno
begin system("stty raw -echo") str = STDIN.getc ensure system("stty -raw echo") end if str == "Y" return true elsif str == "N" return false else raise "Invalid character." end
end </lang>
Run BASIC
<lang runbasic>[loop] cls ' Clear screen html "Click Y or N" ' no other options
button #y, "Y", [Y] ' they either click [Y] button #n, "N", [N] ' or they click [N]
html "
";msg$ ' print message showing what they entered
wait
[Y] msg$ = "You entered [Y]es": goto [loop]
[N] msg$ = "You entered [N]o" : goto [loop]
</lang>
Seed7
<lang seed7>$ include "seed7_05.s7i";
include "keybd.s7i";
const func boolean: yesOrNo (in string: prompt) is func
result var boolean: yes is FALSE; local var char: answer is ' '; begin while keypressed(KEYBOARD) do ignore(getc(KEYBOARD)); end while; write(prompt); repeat answer := lower(getc(KEYBOARD)); until answer in {'y', 'n'}; yes := answer = 'y'; end func;
const proc: main is func
begin writeln(yesOrNo("Press Y or N to continue ")); end func;</lang>
Tcl
Using the console (expects U*Xish stty)
<lang tcl>proc yesno Template:Message "Press Y or N to continue" {
fconfigure stdin -blocking 0 exec stty raw read stdin ; # flush puts -nonewline "${message}: " flush stdout while {![eof stdin]} { set c [string tolower [read stdin 1]] if {$c eq "y" || $c eq "n"} break } puts [string toupper $c] exec stty -raw fconfigure stdin -blocking 1 return [expr {$c eq "y"}]
}
set yn [yesno "Do you like programming (Y/N)"]</lang>
Without a console (answer in the global variable yn; this should work in any GUI for which there is a TCL):
<lang tcl> proc yesno {message} {
toplevel .msg pack [label .msg.l -text "$message\n (type Y/N)?"] set ::yn "" bind .msg <Key-y> {set ::yn "Y"} bind .msg <Key-n> {set ::yn "N"} vwait ::yn destroy .msg
}
yesno "Do you like programming?"
</lang>
XPL0
<lang XPL0>include c:\cxpl\codes; \intrinsic 'code' declarations loop [OpenI(1); \flush any pending keystroke
case ChIn(1) of \get keystroke ^Y,^y: Text(0, "yes"); ^N,^n: Text(0, "no"); $1B: quit \Esc key terminates program other ChOut(0, 7\bel\); CrLf(0); ]</lang>
Batch File
batch has a choice command.. <lang dos> CHOICE </lang>
- Programming Tasks
- Solutions by Programming Task
- Ada
- AutoHotkey
- BASIC
- Applesoft BASIC
- Locomotive Basic
- ZX Spectrum Basic
- BBC BASIC
- C
- C sharp
- Common Lisp
- Euphoria
- EGL
- Forth
- GW-BASIC
- Icon
- Unicon
- Inform 7
- Liberty BASIC
- OpenEdge/Progress
- Pascal
- CRT
- Perl
- PicoLisp
- PureBasic
- Python
- Retro
- REXX
- Ruby
- Run BASIC
- Seed7
- Tcl
- XPL0
- GUISS/Omit
- PARI/GP/Omit
- Batch File
- Keyboard Input