1

Thank you very much in advance for helping me out - I am new to R programming, and have got stuck with trying to use user-inputs accepted through one function within another. The second function is a simple pay calculator where the three variables are number of hours, per hour pay rate and the number of times the rate is multiplied once the number of working hours exceeds 180. I have written a first function called enterval through which I am asking the user to enter the above variables. In a second function called salary, I am trying to use enterval to accept the inputs before running the payout calculations. I am getting stuck because the second function, salary, is breaking when I come to an "if" condition, specifying if h > 180. I am sharing my code below. Thanks again for your kind assistance. I searched among previous answers but could not find a specific instance that fully answered my query - apologies if I missed out an appropriate previous response. The error I am getting on running this code is "Error in h > 180 : comparison (6) is possible only for atomic and list types"

enterval <- function()  {
h <- (readline("Please enter number of hours: "))
h <- as.integer(h)
r <- (readline("Please enter applicable rate: "))
r <- as.integer(r)
m <- (readline("Please confirm your multiplier: "))
m <- as.integer(m)
}
salary <- function ()  {
    enterval()
    if (h > 180)  {
        totalpay <- (180*r) + ((h-180)*r*m)
        }
    else  {
        totalpay <- (h*r)
        }
    totalpay
}
digitman
  • 13
  • 4
  • Thank you very much all of you for your great help. The answers have really opened up the concept of function within functions in R. I have tried the various solutions and have got the results I needed, along with a couple of advanced options I had simply no knowledge of. Warmest regards for such kind help. – digitman Oct 30 '15 at 11:10

2 Answers2

2

I think that what you need is the function to be like this:

enterval <- function()  {
  h <- (readline("Please enter number of hours: "))
  h <- as.numeric(h)
  r <- (readline("Please enter applicable rate: "))
  r <- as.numeric(r)
  m <- (readline("Please confirm your multiplier: "))
  m <- as.numeric(m)
  list(h=h, r=r, m=m)
}

salary <- function ()  {
  inputs <- enterval()
  if (inputs$h > 180)  {
    totalpay <- (180*inputs$r) + ((inputs$h-180)*inputs$r*inputs$m)
  }
  else  {
    totalpay <- (inputs$h*inputs$r)
  }
  totalpay
}

Output:

> salary()
Please enter number of hours: 5
Please enter applicable rate: 0.5
Please confirm your multiplier: 2
[1] 2.5

In your question, enterval just returned the value stored in m but even that was not saved anywhere (because you did not assign that to a variable inside salary so it could not be used by salary. In R functions return only the last object (or what the function return returns if used). In the function above I return a list with elements h, r and m.

Then I save that list to inputs which can be used by salary. Elements in inputs can be accessed using the $ operator.

Also, as a small addition, when you say rate I believe it is a number between 0-1 so I changed as.integer to as.numeric because as.integer will round down to the integer. Feel free to change that back to as.integer if indeed you needed an integer.

EDIT

Better and probably more advanced way of writing salary:

As per @RichardScriven 's comment a good way to avoid typing all the input$* variables is to use list2env like this:

salary <- function ()  {
  inputs <- enterval()
  list2env(inputs, environment())
  if (h > 180)  {
    totalpay <- (180*r) + (h-180)*r*m)
  }
  else  {
    totalpay <- (h*r)
  }
  totalpay
}

list2env will essentially create variables out of the list elements inside salary's environment, which are immediately accesible without needing to use input$*.

LyzandeR
  • 37,047
  • 12
  • 77
  • 87
  • 2
    You could add `list2env(inputs, environment())` under `inputs <- enterval()` in `salary()` and not have to write all those `inputs$` subsets, as `h`, `r`, and `m` will be function environment variables. – Rich Scriven Oct 29 '15 at 23:04
  • 1
    @RichardScriven Thanks this is a valid comment and I was contemplating about using it or not, then I thought it might be too much for someone who is starting now in R. I will add it though. – LyzandeR Oct 29 '15 at 23:06
  • 1
    Actually if you wanted to be really snazzy you could do `list2env(enterval(), environment())` and remove the `inputs` assignment. But that might be a bit too snazzadelic for a newcomer ;) – Rich Scriven Oct 29 '15 at 23:23
  • @RichardScriven Wicked! – LyzandeR Oct 29 '15 at 23:27
  • Thank you very much all of you for your great help. The answers have really opened up the concept of function within functions in R. I have tried the various solutions and have got the results I needed, along with a couple of advanced options I had simply no knowledge of. Warmest regards for such kind help. – digitman Oct 30 '15 at 11:09
  • You are very welcome @digitman . Really glad I could be of help :). You ll see what a nice language R is once you get the hang of it :) – LyzandeR Oct 30 '15 at 11:33
  • @RichardSriven and LyzandeR thanks a lot for explaining list2env; I tried it and learnt the basic use of this very useful function. Not yet fully conversant with the conceptual strength I need to understand how I could remove the inputs assignment, but I am working on it to try and bolster my understanding of environments and hopefully I will pick it up. Huge huge help! – digitman Oct 30 '15 at 11:45
1

Variables assigned in R functions (similar to many other programming languages) have limited scope, this means that the m you assign in your function will only be available within that function. If you want your variable to be available outside of the function you have a two major options:

  1. Return the variable, this is the preferred option, its much cleaner and is good programming practice for numerous reasons described in many stack overflow post. An important thing to remember is a function can only return one variable.

  2. You can do a global assignment, this will make the variable in your function have a global scope and be accessible within all functions. The code for this is m <<- 1 as opposed to m <- 1. This isn't recommended for a variety of reasons. See Global variables in R or Global and local variables in R for more on this subject.

Since you can only return one variable you might put all three objects into a data frame or a list and return that. Though I would question whether you want the value entry done in a function. Additionally if you're user input is primary to your goal R might not be the right language. That being said the code below accomplishes what you're looking for

enterval <- function()  {
  h <- (readline("Please enter number of hours: "))
  h <- as.integer(h)
  r <- (readline("Please enter applicable rate: "))
  r <- as.integer(r)
  m <- (readline("Please confirm your multiplier: "))
  m <- as.integer(m)
  salaryVariables <- data.frame("hours" = h, "rate" = r, "multiplier" = m)
  return(salaryVariables)
}
salary <- function(salaryInfo)  {
  r <- salaryInfo$rate
  h <- salaryInfo$hours
  m <- salaryInfo$multiplier
  if (h > 180)  {
    totalpay <- (180*r) + ((h-180)*r*m)
  }
  else  {
    totalpay <- (h*r)
  }
  return(totalpay)
}

mySalary <- enterval()
salary(mySalary)
Community
  • 1
  • 1
admccurdy
  • 694
  • 3
  • 11
  • `salary(mySalary)` will not work with the way `salary` is defined. – LyzandeR Oct 29 '15 at 23:19
  • Thanks for pointing that out I was playing with global assignments before I altered the functions which clearly got me into trouble. – admccurdy Oct 30 '15 at 00:13
  • Thank you very much all of you for your great help. The answers have really opened up the concept of function within functions in R. I have tried the various solutions and have got the results I needed, along with a couple of advanced options I had simply no knowledge of. Warmest regards for such kind help. – digitman Oct 30 '15 at 11:11