Make readline wait for input in R - r

I'm trying to make my code ask me for a "TRUE" or "FALSE" value before proceeding.
It currently works fine if I run it one line at a time, however when I run all the code at once in RStudio it simply proceeds without waiting for user input and records a value of "" for my parameter.
raw <- readline("TRUE or FALSE -- this is a validation run: ")
if (raw == "F" | raw == "FALSE" | raw == "False"){
validation <- F
} else{
validation <- T
}
rm(raw)
Ideally, I would like an answer that works regardless of how I run it -- RScript, source within RStudio, or running it (i.e. selecting the code and pressing run or ctrl-enter).

If you want to do this in interactive mode then you already have answers but not for use with Rscript. For that instance you need to send messages to the console with cat:
If this test file is named 'prompt.r' and is in the directory where you are working in a system console session:
cat("a string please: ");
a <- readLines("stdin",n=1);
cat("You entered")
str(a);
cat( "\n" )
Then you can run it from the command line as
$ Rscript prompt.r
If you want an all-purpose script then this would run your script under interactive conditions and my script for non-interactive ones:
if (interactive() ){raw <-
readline("TRUE or FALSE -- this is a validation run: ")
if (raw == "F" | raw == "FALSE" | raw == "False"){
validation <- F
} else{
validation <- T
}
rm(raw) } else{
# non-interactive
cat("a string please: ");
a <- readLines("stdin",n=1);
cat("You entered")
str(a);
cat( "\n" )}

Are you running the code by highlighting the lines and clicking run? If so, that could be your problem because R is line-by-line entering your code in the terminal.
Instead, write your script (or comment out the parts you're not testing) and click the source button. Then R waits for user response instead of inputting the line after readline() into readline().
I had the same problem as you, which prompted me to look for an answer. But then I tried this different method of executing the code and it worked.

The reason why readline is "not waiting for input" is, according to the manual (?readline):
In non-interactive use the result is as if the response was RETURN and
the value is "".
You have stumbled on one of the "surprising" features of R.
<rant>
Why readline() works in "interactive mode" only is a complete mystery for me, since it's a perfectly acceptable use case to ask for user input from within a script. Python, for instance, gives you the input(prompt) function which you may invoke whenever you need it.
</rant>
A relatively convenient way of getting around this mess is to define a function ("Every programming problem can be solved by yet another level of indirection"...):
user.input <- function(prompt) {
if (interactive()) {
return(readline(prompt))
} else {
cat(prompt)
return(readLines("stdin", n=1))
}
}
A positive side-effect is that you can add all sorts of nice input validation in user.input(). But I doubt that that was the intention behind the bizarre behaviour of readline().

Although it requires interactive mode, I found the askYesNo() function within the utils package to provide a nice, simple user interface for TRUE/FALSE (or more accurately Yes/No) questions. For example, the following code would produce the following dialog.
Yes returns TRUE
No returns FALSE
Cancel returns NA

Using svDialogs may let you do this.
It's not exactly using readline, but does achieve a similar outcome to what you're looking for.
The code won't run until the dialog box has been filled:
library(svDialogs)
raw <- dlgInput("TRUE or FALSE -- this is a validation run: ",Sys.info()["raw"])$res
if (raw == "F" | raw == "FALSE" | raw == "False"){
validation <- F
} else{
validation <- T
}
rm(raw)

You have to create a function set an "if" conditional sentence with "is.na()" then execute "readline()" again if the value entered is null.
see below:
readinteger <- function()
{
n <- readline(prompt="Enter an integer: ")
n <- as.integer(n)
if (is.na(n)){
n <- readinteger()
}
return(n)
}
print(readinteger())
rm(list=ls())

Related

Is it possible to use the R function readline without requiring the user to press enter?

I am writing a small function that requires user input, which I have coded for using the readline function, but want to eliminate the need for the user to press [enter] after responding to the prompt in the console.
I have searched extensively on SO without finding a solution and the readline documention provides no potential solutions.
timer <- function() {
require(tictoc) #load required package
experiment_no <- readline("Experiment number: ")
while(T){ #open infinite while loop
tic() #start timer
input_state=readline("State input: ") #allow for entry of state
if(input_state %in% 1:5){ #check if it's acceptable
elapsed=toc() #if it is then end timer and record data
write.table(cbind(experiment_no,input_state,elapsed$toc-elapsed$tic),'results.txt',col.names=F,row.names=F,quote=F,append=T)
}else if(input_state=='t'){ #if input is 't'
break #break out of while loop
}else if(input_state <1 | input_state > 5 & input_state!='t'){#if input is not and accepted state AND is not 't'
print('thats not an allowed state- please try another')
}
}
}
I would like the user to be able to input the experiment number into the console without pushing enter.
In contrast to r2evans' response, this solution only works in RStudio. It relies on the rstudioapi package's ability to capture the input currently in the console.
dynamic_readline <- function() {
while (rstudioapi::isAvailable()) {
input <- rstudioapi::getConsoleEditorContext()$contents
if (input != "") {
rstudioapi::sendToConsole("", execute = FALSE)
return(input)
}
Sys.sleep(0.1)
}
readline()
}
The while loop checks the content of the console every 0.1 seconds and returns the input if anything is entered. If something is detected, it sends "" to the console to clear it. If rstudioapi isn't available (i.e. the editor isn't Rstudio), the function falls back to readline().
The sleep time can be adjusted based on the response time you need, although in the R console you're not going to be getting 60 Hz gameplay.
You can also adjust the if statement to test for specific valid inputs rather than any non-empty input by using %in% instead of !=. (Something like if (input %in% c("w", "a", "s", "d", "quit"))). This would make it feasible to use this method with known inputs longer than one character.
Edit: up front, this also doesn't work with RStudio. Since I'm confident RStudio is really intercepting and controlling quite a lot of R, while it is possible that C_menu has some other method of control (e.g., options), I think it is more likely that RStudio is intercepting the keypresses (thereby forcing <enter>-use). With that, I suggest that this leans towards a bug/feature-request with RStudio.
If you look at the source for utils::menu, you'll see at the end that it does:
repeat {
ind <- .Call(C_menu, as.character(choices))
if (ind <= nc)
return(ind)
cat(gettext("Enter an item from the menu, or 0 to exit\n"))
}
This suggests that the C_menu function (not exported) can be (ab)used to do what you want. I don't know the internal workings of the function, so some empirical takeaways/caveats:
usually (but not always[1]), the first pass in the repeat loop requires an enter for any entry;
after that first <enter>, it appears to be <enter>-less for single-digit integers, anything else supports some arbitrary length and requires an <enter>
repeat {
ret <- .Call(utils:::C_menu, letters)
if (ret == 0L) break
cat(" you entered ", ret, "\n")
}
# Selection: 345 # I typed "345<enter>"
# you entered 345
# Selection: 3 # just "3", no enter
# you entered 3
# Selection: 4
# you entered 4
# Selection: 5
# you entered 5
# Selection: # just enter
# you entered 27
# Selection: C # "C<enter>"
# you entered 27
# Selection: c # "c<enter>"
# you entered 3
# Selection: abc # "abc<enter>"
# you entered 27
# Selection: 0 # just "0", no enter, it quits
I'm inferring that 27 means I entered something not in the original choices (letters).
Not sure if you can adapt your process to this, as it seems to be restricted to single-digit numbers (once you get past the first entry). I have not found the source for it yet to see if there's a companion function with other arguments.
Notes:
I can infrequently get the code to handle the enter-less entry on the first go, not sure if it's a matter of my code-editing (emacs/ess) or something else. If so, it is likely a bug somewhere.

How to exit from the script in R

I have a function like this
Squ <- function(x, expected.result){
result <- x*x
if(result != expected.result){
stop("We have some error /n/n")
return(NULL)
}
return(result)
}
I am running below three lines.
Squ(2, 4)
Squ(3, 7)
Squ(4, 16)
Ideally i expected this script will stop in second line i.e.Squ(3, 7) as if condition is true in the function but it didn't stop & runs the script completely. How to write the script to get stop if conditions met true.
Thanks,
Mani
Normally, running R code interactively means you'll keep running new lines of code even when there is an error. If you want the code to stop as soon as there's any kind of error, you should wrap the calls to Squ in a function. You'll have to edit the function if you want to save the values of each test, but it looks like you already know what each value should be and you're trying to find the first error. Using a function will give you the first error message and then halt execution.
test_Squ <- function() {
Squ(2, 4)
Squ(3, 7)
Squ(4, 16)
print("All code ran without throwing an error")
}
test_Squ()
# Error in Squ(3, 7) : We have some error /n/n
Let me know if you have a more specific use case in mind and I'll edit my answer.

Rscript and user prompts

Have a fully working R-script. When executing it from Rscript it doesn't stop to accept user input however.
The user input is some readline statements. The cat statements prompting for input works as intended. Have I missed something?
I execute 'Rscript scriptfile.R' from terminal on macOS.
You can create a function typeline() to read an input line and then use the result for your next commands. It will wait for your input text either you run your code in Rstudio or in terminal:
typeline <- function(msg="Enter text: ") {
if (interactive() ) {
txt <- readline(msg)
} else {
cat(msg);
txt <- readLines("stdin",n=1);
}
return(txt)
}
txt=typeline("your message: ")
print(txt)
Managed to get it to work by changing readline to readLines as mentioned in the post suggested by meenaparam. The downside with this method is that ithe script only works in batch mode, running it in Rstudio makes it hang. Would be good to know a general way to capture keyboard input i.e that works both in interactive and batch mode.
Use this
cat("What's your name? ")
name <- readLines(file("stdin"),1)
cat("What's your age? ")
age <- readLines(file("stdin"),1)
print(name)
print(age)

Running R script_Readline and Scan does not pause for user input

I have looked at other posts that appeared similar to this question but they have not helped me. This may be just my ignorance of R. Thus I decided to sign up and make my first post on stack-overflow.
I am running an R-script and would like the user to decide either to use one of the two following loops. The code to decide user input looks similar to the one below:
#Define the function
method.choice<-function() {
Method.to.use<-readline("Please enter 'New' for new method and'Old' for old method: ")
while(Method.to.use!="New" && Method.to.use!="Old"){ #Make sure selection is one of two inputs
cat("You have not entered a valid input, try again", "\n")
Method.to.use<-readline("Please enter 'New' for new method and 'Old' for old method: ")
cat("You have selected", Method.to.use, "\n")
}
return(Method.to.use)
}
#Run the function
method.choice()
Then below this I have the two possible choices:
if(Method.to.use=="New") {
for(i in 1:nrow(linelist)){...}
}
if(Method.to.use=="Old"){
for(i in 1:nrow(linelist)){...}
}
My issue is, and what I have read from other posts, is that whether I use "readline", "scan" or "ask", R does not wait for my input. Instead R will use the following lines as the input.
The only way I found that R would pause for input is if the code is all on the same line or if it is run line by line (instead of selecting all the code at once). See example from gtools using "ask":
silly <- function()
{
age <- ask("How old are you? ")
age <- as.numeric(age)
cat("In 10 years you will be", age+10, "years old!\n")
}
This runs with a pause:
silly(); paste("this is quite silly")
This does not wait for input:
silly()
paste("this is quite silly")
Any guidance would be appreciated to ensure I can still run my entire script and have it pause at readline without continuing. I am using R-studio and I have checked that interactive==TRUE.
The only other work-around I found is wrapping my entire script into one main function, which is not ideal for me. This may require me to use <<- to write to my environment.
Thank you in advance.

User input when executing R code in batch mode

I am searching for a way to get user input inside a loop while executing in batch mode.
readLines() and scan() work well for me in interactive mode only, in batch mode they start to read in lines of code as user input, unless all the code is surrounded by {}, which is inconvenient. I need a simple solution to get just 1 integer value in a way that I can just type in value and press ENTER, so
the input field (if the solution involves GUI) must automatically get focus and
ENTER must trigger end of input/submission.
I can't find a way to do it that will satisfy both conditions, e.g. ginput() from gWidgets activates the input field, but ENTER doesn't trigger form submission.
Here is how I solved my own problem:
require(gWidgets)
options(guiToolkit="RGtk2")
INPUT <- function(message) {
CHOICE <- NA
w <- gbasicdialog(title=message, handler = function(h,...) CHOICE <<- svalue(input))
input <- gedit("", initial.msg="", cont=w, width=10)
addHandlerChanged(input, handler=function (h, ...) {
CHOICE <<- svalue(input)
dispose(w)
})
visible(w, set=TRUE)
return(CHOICE)
}
repeat{
x=INPUT("Input an integer")
if(!is.na(as.integer(x))) break
}
print(x)
Update:
I can't test this right now, but take a look at ?menu and have it pop up a gui window.
I'm not certain if that will work, but it is different in that it takes a mouse-click response.
original answer:
As per the documentation to ?readline:
This can only be used in an interactive session.
..
In non-interactive use the result is as if the response was RETURN and the value is "".
If you are simply waiting for one piece of information, and you do not know this piece of information before beginning the execution of the script (presumably, there is a decision to be made which is dependent on the results earlier in the script), then one alternative is to simply break your script up into three parts:
everything before the decision point.
an interactive script which prompts for input
everything after the decision point.
And simply chain the three together by having the first end by calling the second in an interactive session. Then have the second end by calling the third.

Resources