How to parse a string in a calculator in R? - r

I wrote a calculator program in which the User enters two numbers and an arithmetic operation between the numbers, then presses Enter and gets the result, or rather did a string parsing, but it doesn't work. What can this be related to? I also wrote the code so that I could exit the function
add<- function(x,y){
return(x+y)
}
subtract <- function(x,y){
return(x-y)
}
multiply <- function(x, y) {
return(x * y)
}
div <- function(x,y){
if(y==0){
return("Error")
break
}
else if(y!=0){
return(x%/%y)
}
}
res<-NA
repeat{
print("9", quote=FALSE)
user_string <-
readline(prompt = ("Enter: "))
if(user_string== 9){
break # to stop the programme
}
components_of_user_string <-
unlist(strsplit(user_string, split = '')) #splitting the line
num1<-as.numeric(components_of_user_string[1]) #first number
num2<-as.numeric(components_of_user_string[3]) # second number
us_chosen_operation <-
components_of_user_string[2]
operations_vector <-
c('+', '-', '*', '/')
choice <- match(us_chosen_operation, operations_vector)
operator <- switch(choice, ' + ', ' - ', ' * ', ' / ')
result <-
switch(
choice,
add(num1, num2),
subtract(num1, num2),
multiply(num1, num2),
divide(num1, num2)
)
print(paste(num1, operator, num2, "=", result))
}

String parsing with eval(parse(...)) could be an option :
repeat {
input <- readline(prompt = ("Enter: "))
print(eval(parse(text = input)))
}
# Enter: (-4)+2
# [1] -2
# Enter: raise_to_degree(2,3)
# [1] 8

Related

My R function doesn't seem to be using the arguments I give it

I have created an R function that doesn't seem to be using the arguments that I give it. It runs but the result is not the one I should be getting given the parameters I pass to it. The code I have is as follows:
test_function <- function(text1, text2, number1) {
if (length(text1) == length(text2)) {
print("Equal")
} else {
print("Not equal")
}
operation <- length(text1) + number1
print(paste("The result for the operation is: "), operation)
}
x <- "Hello"
y <- "World!"
z <- 10
test_function(x, y, z)
Does anyone know why the result I'm getting is the following?
[1] "Equal"
[1] "The result for the operation is: "
Use nchar() instead of length().
In addition, paste("The result for the operation is:", operation).
test_function <- function(text1, text2, number1) {
if (nchar(text1) == nchar(text2)) {
print("Equal")
} else {
print("Not equal")
}
operation <- nchar(text1) + number1
print(paste("The result for the operation is:", operation))
}
x <- "Hello"
y <- "World!"
z <- 10
test_function(x, y, z)
#[1] "Not equal"
#[1] "The result for the operation is: 15"

How to mask subsequences of string with a pattern string

I have a main string that looks like this:
my_main <- "ABCDEFGHIJ"
What I want to do is to sequentially mask at every position with another pattern string:
my_pattern <- "x*x" # the length could be varied from 1 up to length of my_main
Every character that overlap with * will be kept, other will be replaced with x.
The final result is a vector of strings that contain these:
xBxDEFGHIJ
AxCxEFGHIJ
ABxDxFGHIJ
ABCxExGHIJ
ABCDxFxHIJ
ABCDExGxIJ
ABCDEFxHxJ
ABCDEFGxIx
Next if the pattern is
my_pattern <- "xx**x"
The result would be:
xxCDxFGHIJ
AxxDExGHIJ
ABxxEFxHIJ
ABCxxFGxIJ
ABCDxxGHxJ
ABCDExxHIx
How can I achieve that?
This might be a little over-complicated, but it's a start:
I'm going to reuse Reduce_frame from https://stackoverflow.com/a/70945868/3358272.
Reduce_frame <- function(data, expr, init) {
expr <- substitute(expr)
out <- rep(init[1][NA], nrow(data))
for (rn in seq_len(nrow(data))) {
out[rn] <- init <- eval(expr, envir = data[rn,])
}
out
}
From here, let's split the pattern into a frame (for ease of access, if nothing else):
repl <- subset(
data.frame(p = strsplit(my_pattern, "")[[1]], i = seq_len(nchar(my_pattern))),
p != "*")
repl
# p i
# 1 x 1
# 3 x 3
From here, we can do it once with:
tail(Reduce_frame(repl, `substring<-`(init, i, i, p), init = my_main), 1)
# [1] "xBxDEFGHIJ"
Which means we can iterate fairly easily:
sapply(c(0, seq_len(nchar(my_main) - nchar(my_pattern))), function(offset) {
tail(Reduce_frame(transform(repl, i = i + offset),
`substring<-`(init, i, i, p), init = my_main), 1)
})
# [1] "xBxDEFGHIJ" "AxCxEFGHIJ" "ABxDxFGHIJ" "ABCxExGHIJ" "ABCDxFxHIJ" "ABCDExGxIJ" "ABCDEFxHxJ" "ABCDEFGxIx"
To use your second pattern,
my_pattern <- "xx**x"
repl <- transform(...) # from above
## the rest of this code is unchanged from above
sapply(c(0, seq_len(nchar(my_main) - nchar(my_pattern))), function(offset) {
tail(Reduce_frame(transform(repl, i = i + offset),
`substring<-`(init, i, i, p), init = my_main), 1)
})
# [1] "xxCDxFGHIJ" "AxxDExGHIJ" "ABxxEFxHIJ" "ABCxxFGxIJ" "ABCDxxGHxJ" "ABCDExxHIx"
So this can be easily functionized:
Reduce_frame <- ... # defined above
func <- function(S, pattern) {
stopifnot(nchar(S) >= nchar(pattern))
repl <- subset(
data.frame(p = strsplit(pattern, "")[[1]], i = seq_len(nchar(pattern))),
p != "*")
sapply(c(0, seq_len(nchar(S) - nchar(pattern))), function(offset) {
tail(Reduce_frame(transform(repl, i = i + offset),
`substring<-`(init, i, i, p), init = S), 1)
})
}
func("ABCDEFGHIJ", "x*x")
# [1] "xBxDEFGHIJ" "AxCxEFGHIJ" "ABxDxFGHIJ" "ABCxExGHIJ" "ABCDxFxHIJ" "ABCDExGxIJ" "ABCDEFxHxJ" "ABCDEFGxIx"
func("ABCDEFGHIJ", "xx**x")
# [1] "xxCDxFGHIJ" "AxxDExGHIJ" "ABxxEFxHIJ" "ABCxxFGxIJ" "ABCDxxGHxJ" "ABCDExxHIx"
Here's one way using strsplit, grepl, and paste.
f <- \(mm, mp) {
m <- el(strsplit(mm, ''))
p <- el(strsplit(mp, ''))
i <- which(!grepl(p, pattern='\\*'))
vapply(c(0L, seq_len(length(m) - max(i))), \(j) {
m[i + j] <- p[i]
paste(m, collapse='')
}, vector('character', 1L))
}
f('ABCDEFGHIJ', 'x*x')
# [1] "xBxDEFGHIJ" "AxCxEFGHIJ" "ABxDxFGHIJ" "ABCxExGHIJ" "ABCDxFxHIJ"
# [6] "ABCDExGxIJ" "ABCDEFxHxJ" "ABCDEFGxIx"
f('ABCDEFGHIJ', 'x**x')
# [1] "xBCxEFGHIJ" "AxCDxFGHIJ" "ABxDExGHIJ" "ABCxEFxHIJ" "ABCDxFGxIJ"
# [6] "ABCDExGHxJ" "ABCDEFxHIx"
f('ABCDEFGHIJ', 'xx**x')
# [1] "xxCDxFGHIJ" "AxxDExGHIJ" "ABxxEFxHIJ" "ABCxxFGxIJ" "ABCDxxGHxJ"
# [6] "ABCDExxHIx"
f('ABCDEFGHIJ', 'kk**krr')
# [1] "kkCDkrrHIJ" "AkkDEkrrIJ" "ABkkEFkrrJ" "ABCkkFGkrr"
f('ABCDEFGHIJ', 'kk**kr*r')
# [1] "kkCDkrGrIJ" "AkkDEkrHrJ" "ABkkEFkrIr"
Here is an approach along the same lines as r2evans' answer but relying on some stringr functions which should be more efficient than the base equivalents:
library(stringr)
f <- function(main, r_pattern) {
shift <- nchar(main) - nchar(r_pattern) + 1
idx <- as.data.frame(str_locate_all(r_pattern, "[^*]+")[[1]])
x_pattern <- str_split(r_pattern, "\\*+")[[1]]
Reduce(
function(x, y)
`str_sub<-`(
x,
seq(idx$start[y], length.out = shift),
seq(idx$end[y], length.out = shift),
omit_na = FALSE,
x_pattern[y]
),
seq(nrow(idx)),
init = main
)
}
f("ABCDEFGHIJ", "x*x")
[1] "xBxDEFGHIJ" "AxCxEFGHIJ" "ABxDxFGHIJ" "ABCxExGHIJ" "ABCDxFxHIJ" "ABCDExGxIJ" "ABCDEFxHxJ" "ABCDEFGxIx"
f("ABCDEFGHIJ", "xx**x")
[1] "xxCDxFGHIJ" "AxxDExGHIJ" "ABxxEFxHIJ" "ABCxxFGxIJ" "ABCDxxGHxJ" "ABCDExxHIx"
# Edit after OP comment:
f(my_main, "KK**KRR")
[1] "KKCDKRRHIJ" "AKKDEKRRIJ" "ABKKEFKRRJ" "ABCKKFGKRR"
Here is Ruby code that produces the desired result. I am presenting it in the event that a reader wishes to convert it to R, possibly with modification, of course.
You should be able to read the code even if you don't know Ruby, as long as you understand that:
'abc'.size returns 3;
0..8 is a range of integers between 0 and 8, inclusive;
'abc' << 'd' returns 'abcd';
7.modulo(3) returns 1;
'abcd'[2] returns 'c', 2 being an index; and
s == 'x' ? 'x' : my_main[j] reads, "if the string s (which will be 'x' or '*') equals 'x' return 'x', else return the character of my_main at index j.
The Ruby code (somewhat simplified from what would normally be written) is as follows.
def doit(my_main, my_pattern)
msz = my_main.size
psz = my_pattern.size
(0..msz-psz).map do |i|
s = ''
(0..msz-1).each do |j|
s << (my_pattern[(j-i).modulo(msz)] == 'x' ? 'x' : my_main[j])
end
s
end
end

R showing error - "Building a Basic Calculator"

add <- function(x, y) {
return(x + y)
}
subtract <- function(x, y) {
return(x - y)
}
multiply <- function(x, y) {
return(x * y)
}
divide <- function(x, y) {
return(x / y)
}
factors <- function(x) {
if (choice == 5 | choice == 6) {
choice <- readline(prompt = "Enter the number: ")
}
else {
num1 = as.integer(readline(prompt = "Enter first number: "))
num2 = as.integer(readline(prompt = "Enter second number: "))
}
}
# take input from the user
print("Select operation.")
print("1.Add")
print("2.Subtract")
print("3.Multiply")
print("4.Divide")
print("5. Factors")
print("6. Prime")
choice = as.integer(readline(prompt="Enter choice[1/2/3/4/5/6]: "))
num1 = as.integer(readline(prompt="Enter first number: "))
num2 = as.integer(readline(prompt="Enter second number: "))
operator <- switch(choice,"+","-","*","/")
result <- switch(choice, add(num1, num2), subtract(num1, num2), multiply(num1, num2), divide(num1, num2), factor(i)
print(paste(num1, operator, num2, "=", result))
ERROR IN R CONSOLE -
> choice = as.integer(readline(prompt="Enter choice[1/2/3/45/6]: "))
Enter choice[1/2/3/45/6]: num1 = as.integer(readline(prompt="Enter first number: "))
Warning message:
NAs introduced by coercion
> num2 = as.integer(readline(prompt="Enter second number: "))
Enter second number: operator <- switch(choice,"+","-","*","/")
Warning message:
NAs introduced by coercion
> result <- switch(choice, add(num1, num2), subtract(num1, num2), multiply(num1, num2), divide(num1, num2))
> print(paste(num1, operator, num2, "=", result))
Error in paste(num1, operator, num2, "=", result) :
object 'num1' not found
This is the error thats generated by R.It appears that R is not able to run through the integers as per my commands. I cannot understand whats wrong with it. I have made few changed but even then the errors appear to be the same. When i run the code until the inputs taken from the user it runs quite fine but the inputs are not recognizing num1 and num2 it says -
"Warning message:
NAs introduced by coercion"
I do not understand since I'm a beginner. Please help me out.
Thank you

matched brackets showed as unmatched

convertToClockTime <- function(file, lag = Latency) {
colnames(adamcorrectfile)[which(colnames(adamcorrectfile) == "X.1")] <- "Calculated.Run.Time"
adamcorrectfile$Calculated.Run.Time <- round(adamcorrectfile$Calculated.Run.Time, digits = 0)
adamcorrectfile$LPRS.Time <- as.POSIXct(adamcorrectfile$LPRS.Time, format = "%H:%M")
adamcorrectfile <- adamcorrectfile[order(adamcorrectfile$LPRS.Time),]
output <- colnames(adamcorrectfile)
for (j in unique(adamcorrectfile$Folder)) {
adamcorrectfile.Folder <- adamcorrectfile[which(adamcorrectfile$Folder == "print 1"),]
adamcorrectfile.Folder$start.time <- adamcorrectfile.Folder$LPRS.Time + lag
adamcorrectfile.Folder$end.time <- adamcorrectfile.Folder$start.time + adamcorrectfile.Folder$`Calculated.Run.Time`
for (i in 2:nrow(adamcorrectfile)) {
adamcorrectfile.Folder[i,"start.time"] <- max(adamcorrectfile.Folder[i,"LPRS.Time"] + 15*60, adamcorrectfile[i-1, "end.time"]
adamcorrectfile.Folder[i, "end.time"] <- adamcorrectfile.Folder[i,"start.time"] + adamcorrectfile.Folder[i,"Calculated.Run.Time"]
}
output <- rbind(output, adamcorrectfile.Folder)
return(output)
}
}
On line 1 it says unmatched opening bracket '{' even though it is matched. Any help. Are there some tricky things with R indentation, it just keeps popping errors due to indentations it seems
It looks like the problem is that you haven't closed the parentheses on the max function in the middle of the for loop.
Here is the working code:
convertToClockTime <- function(file, lag = Latency) {
colnames(adamcorrectfile)[which(colnames(adamcorrectfile) == "X.1")] <- "Calculated.Run.Time"
adamcorrectfile$Calculated.Run.Time <- round(adamcorrectfile$Calculated.Run.Time, digits = 0)
adamcorrectfile$LPRS.Time <- as.POSIXct(adamcorrectfile$LPRS.Time, format = "%H:%M")
adamcorrectfile <- adamcorrectfile[order(adamcorrectfile$LPRS.Time),]
output <- colnames(adamcorrectfile)
for (j in unique(adamcorrectfile$Folder)) {
adamcorrectfile.Folder <- adamcorrectfile[which(adamcorrectfile$Folder == "print 1"),]
adamcorrectfile.Folder$start.time <- adamcorrectfile.Folder$LPRS.Time + lag
adamcorrectfile.Folder$end.time <- adamcorrectfile.Folder$start.time + adamcorrectfile.Folder$`Calculated.Run.Time`
for (i in 2:nrow(adamcorrectfile)) {
adamcorrectfile.Folder[i,"start.time"] <- max(adamcorrectfile.Folder[i,"LPRS.Time"] + 15*60, adamcorrectfile[i-1, "end.time"])
adamcorrectfile.Folder[i, "end.time"] <- adamcorrectfile.Folder[i,"start.time"] + adamcorrectfile.Folder[i,"Calculated.Run.Time"]
}
output <- rbind(output, adamcorrectfile.Folder)
return(output)
}
}

R .Last.call feature - similar to .Last.value

Similarly to .Last.value is there any way to access last call? Below expected results of potential .Last.call.
sum(1, 2)
# [1] 3
str(.Last.call)
# language sum(1, 2)
The bests if it would not require to parse file from file system.
The last.call package is no longer on cran, but you can still get the code:
# -----------------------------------------------------------------------
# FUNCTION: last.call
# Retrieves a CALL from the history and returns an unevaluated
# call.
#
# There are two uses for such abilities.
# - To be able to recall the previous commands, like pressing the up key
# on the terminal.
# - The ability to get the line that called the function.
#
# TODO:
# - does not handle commands seperated by ';'
#
# -----------------------------------------------------------------------
last.call <-
function(n=1) {
f1 <- tempfile()
try( savehistory(f1), silent=TRUE )
try( rawhist <- readLines(f1), silent=TRUE )
unlink(f1)
if( exists('rawhist') ) {
# LOOK BACK max(n)+ LINES UNTIL YOU HAVE n COMMANDS
cmds <- expression()
n.lines <- max(abs(n))
while( length(cmds) < max(abs(n)) ) {
lines <- tail( rawhist, n=n.lines )
try( cmds <- parse( text=lines ), silent=TRUE )
n.lines <- n.lines + 1
if( n.lines > length(rawhist) ) break
}
ret <- rev(cmds)[n]
if( length(ret) == 1 ) return(ret[[1]]) else return(ret)
}
return(NULL)
}
Now, to use it:
sum(1, 2)
# [1] 3
last.call(2)
# sum(1, 2)
I've modified this code to output text strings of the preceding commands / calls in a manner that preserves how there were formatted across lines in the original call, sot that I can use cat() to output the calls (for a function that emails me when the preceding function is done running). Here's the code:
lastCall <- function(num.call = 1) {
history.file <- tempfile()
try(savehistory(history.file), silent = TRUE )
try(raw.history <- readLines(history.file), silent = TRUE )
unlink(history.file)
if (exists('raw.history') ) {
# LOOK BACK max(n)+ LINES UNTIL YOU HAVE n COMMANDS
commands <- expression()
num.line <- max(abs(num.call) + 1)
while (length(commands) < max(abs(num.call) + 1)) {
lines <- tail(raw.history, n = num.line)
try(commands <- parse(text = lines), silent = TRUE)
num.line <- num.line + 1
if (num.line > length(raw.history)) break
}
ret <- rev(commands)[num.call + 1]
if (length(ret) == 1) {
a <- ret[1]
} else {
a <- ret
}
# a <- rev(commands)[num.call + 1]
out <- lapply(a, deparse) %>%
sapply(paste, sep = "\n", collapse = "\n")
}
out
}
Enjoy!

Resources