Re-prompt readline() if the input is invalid - r

Lets say I want to ask the user for an input, a number over 10. If not, print a message and re-prompt/ask again. How can this be achieved in R?
I understand that this could be solved with IF or WHILE statement, but I canĀ“t wrap my head around this.
Example
math <- function(number_1) {
number_1 <- readline("Enter your number: ")
if the number is below i want to reprompt readline(...)
result <- number_1 / 2
return(result)
}

Here's a way:
math <- function() {
result <- NA
while (is.na(result) || result < 10) {
text <- readline("Enter your number: ")
result <- as.numeric(text)
}
result
}
You don't need to give any input to your function; it will get the input when it prompts the user. The is.na(result) code checks for an NA: initially the result is NA, so it will run the loop at least once, and if
the user enters something that isn't a number, you'll get another one.
Since readline() returns a character value, you need as.numeric to convert it to a number.

Related

How to get content or length of current console output?

I'm looking for the functions get_output_content or at least get_output_length below, that would tell me how many characters were printed in the console.
test <- function(){
cat("ab")
cat("\b")
cat("cd")
c <- get_output_content() # "acd" (I'd be happy with "ab\bcd" as well)
l <- get_output_length() # 3
return(list(c,l))
}
test()
In this example obviously I could easily count the characters in the input, but If I'm using other functions I may not. Can you help me build one or both of these functions ?
EDIT to clarify:
in my real situation, I cannot work upstream and count before, like in the proposed solutions, I need to count the displayed output at a given time without monitoring what's before.
here's a reproducible example looking more like what I want to achieve
library(pbapply)
my_files <- paste0(1000:1,".pdf")
work_on_pdf <- function(pdf_file){
Sys.sleep(0.001)
}
report <- pbsapply(my_files,work_on_pdf) # the simple version, but I want to add the pdf name next to the bar to have more info about my progress
# so I tried this but it's not satisfying because it "eats" some of the current output of pbapply
report <- pbsapply(my_files,function(x){
buffer_length <- 25
work_on_pdf(x)
catmsg <- paste0(c( # my additional message, which is in 3 parts:
rep("\b",buffer_length), # (1) eat 25 characters
x, # (2) print filename
rep(" ",buffer_length-nchar(x))), # (3) print spaces to cover up what may have been printed before
collapse="")
cat(catmsg)
})
if I was able to count what's in the console I could easily tweak my function to get something satisfying.
NEW EDIT : FYI solution to example but not to general question:
I could solve my precise issue with this, though it doesn't solve the general question, which is measuring the current output of the console when you don't have any other info.
library(pbapply)
my_files <- paste0(1000:1,".pdf")
work_on_pdf <- function(pdf_file){
Sys.sleep(0.01)
}
pbsapply2 <- function(X,FUN,FUN2){
# FUN2 will give the additional message
pbsapply(X,function(x){
msg <- FUN2(x)
cat(msg)
output <- FUN(x)
eraser <- paste0(c(
rep("\b",nchar(msg)), # go back to position before additional message
rep(" ",nchar(msg)), # cover with blank spaces
rep("\b",nchar(msg))), # go back again to initial position
collapse="")
cat(eraser)
return(output)
})
}
report <- pbsapply2(my_files,work_on_pdf,function(x) paste("filename:",x))
Something like this (?):
test <- function(){
c <- paste0(capture.output(cat("ab")),
capture.output(cat("\b")),
capture.output(cat("cd")))
n <- nchar(c)
l <- length(c)
return(list(c,n,l))
}
test()

Vectorizing A Custom Function

I wrote this function to return the string before a certain character, which goes like so:
strBefore <- function(find, x, last = FALSE, occurence) {
# Checking.
if (class(x)[1] != "character") { stop("The strBefore function only supports objects of character class.") }
# Getting the place of the find, and handling both caes of last.
fullPlace <- gregexpr(find, x)[[1]] # Gets the location of the occurences of find in x.
# Handling the case where last is TRUE.
if (last == TRUE) { place <- max(fullPlace) # Grabbing the latest character index if last is TRUE.
} else { place <- min(fullPlace) } # Otherwise, getting the first space.
# Handles the occurrenceargument if given.
if (!missing(occurrence)) { place <- fullPlace[occurrence] }
# Subsetting the string.
xlen <- nchar(x) # Getting the total number of characters in the string.
x <- substr(x, 1, place - 1) # Minus 1 because we don't want to include the first hit for find.
return(x)
}
Where find is the character you want the string before, x is the character, last asks if you to get before the last occurrence of find, and occurrence designates which occurrence of find to get before (overrides last if given).
If I use it on a single character object, it works fine like so:
> test <- "Hello World"
> test2 <- strBefore(" ", test)
> test2
[1] "Hello"
However, if I use it on a character vector, it cuts each item in the vector at the same place as the first item:
> test <- c("Hello World", "Hi There", "Why Hello")
> test2 <- strBefore(" ", test)
> test2
[1] "Hello" "Hi Th" "Why H"
Now, this link here does provide me with a method for doing what I want:
Using gsub to extract character string before white space in R
However, I do like having the functionality of the "occurrence" argument, which returns the string before the 2nd, 3rd, etc... occurrence of the find argument.
Just as a note, I can vectorize my function with sapply like so:
> test <- c("Hello World", "Hi There", "Why Hello")
> test2 <- sapply(test, function(x) strBefore(" ", x))
> test2
Hello World Hi There Why Hello
"Hello" "Hi" "Why"
Which somewhat solves my problem...but is there a way to do this more cleanly without having to use an apply function? I'm not looking for a solution to what strBefore does, but more a solution to how to vectorize custom functions. Thanks for your time.

How to check if subscript will be out of bounds?

If i want to check the existence of a variable I use
exists("variable")
In a script I am working on I sometimes encounter the problem of a "subscript out of bounds" after running, and then my script stops. In an if statement I would like to be able to check if a subscript will be out of bounds or not. If the outcome is "yes", then execute an alternative peace of the script, and if "not", then just continue the script as it was intended.
In my imagination in case of a list it would look something like:
if {subscriptOutofBounds(listvariable[[number]]) == TRUE) {
## execute this part of the code
}
else {
## execute this part
}
Does something like that exist in R?
You can compare the length of your list with other number. As an illustration, say I have a list with 3 index and want to check by comparing them with a vector of number 1 to 100.
lol <- list(c(1:10),
c(100:200),
c(3:50))
lol
check_out <- function(x) {
maxi <- max(x)
if (maxi > length(lol)) {
#Excecute this part of code
print("Yes")
}
else {
#Excecute this part of code
print("No")
}
}
num <- 1:100
check_out(num)
The biggest number of vector num is 100 and your list only has 3 index (or length =3), so it will be out of bound from your list, then it will return Yes

R - How to use readline() with additional code after?

Simple problem, but I can't find an answer that works anywhere. When I use readline() (for example, as demonstrated here - http://www.rexamples.com/4/Reading%20user%20input) it works perfectly:
readinteger <- function()
{
n <- readline(prompt="Enter an integer: ")
return(as.integer(n))
}
a <- print(readinteger())
However, if I add any code after this, readline() is skipped and the code just continues:
readinteger <- function()
{
n <- readline(prompt="Enter an integer: ")
return(as.integer(n))
}
a <- print(readinteger())
b <- 7
Any solutions (and/or easier ways to get user input)?
The problem here is that as soon as a <- print(readinteger()) is entered, it is evaluated, and b <- 7 is interpreted as the input to readline. A solution is to wrap your code in a function or a block:
{
a <- print(readinteger())
b <- 7
}
By putting everything into a block, the whole block is read as code and only after, as it is evaluated, you will be prompted for an integer.
put multiple outputs if processed by your function or the print function into "ONE object"
you name it: myOput <- list(abandoned.b,that.c)
print(myoutput)

User input in R

I am trying to run an R script, using Rscript in Windows command prompt and ask for user's input.
So far, i have found answers on how to implement something similar in R's interactive shell. readline() or scan() doesn't seem to work for the command prompt.
Examples:
I have a polynomial y=cX where X can take more than one values X1,X2,X3 and so on. C variable is know, so what i need in order to calculate the value of y is to ask the user for the Xi values and store them somewhere inside my script.
The script bellow does nothing.
UIinput <- function(){
#Ask for user input
x <- readline(prompt = "Enter X1 value: ")
#Return
return(x)
}
The second example below just prompts the message and then ends.
FUN <- function(x) {
if (missing(x)) {
message("Uhh you forgot to eneter x...\nPlease enter it now.")
x <- readLines(n = 1)
}
x
}
FUN()
Console output:
Uhh you forgot to eneter x...
Please enter it now.
character(0)
Any suggestions?
Thanks in advance
cat("input x: ")
x <- readLines(con="stdin", 1)
cat(x, "\n")

Resources