17

I've been using R for 4 months now, and I really hope there is a way to use main function as in other languages (C++, Python...)

The main reason I want to do this is that all the variables I use in a R script are global variables that can potentially pollute the namespace of the functions I defined in the same script:

f <- function(x) {
  x + a
}
a <- 50
f(5)

For me, this is just a personal preference. I'm a sloppy programmer and I want to prevent myself from making silly mistakes.

I can surely define main <- function() {}, but I want to know if there is anything similar to this:

if __name__ == "__main__": 
    main()

(In this Python script, if the function name is main, then run main() to call the main function.)

srborlongan
  • 4,460
  • 4
  • 26
  • 33
BlueFeet
  • 2,407
  • 4
  • 21
  • 24
  • 3
    For those that don't know python, what does `if __name__ == "__main__": main()` do? – Rich Scriven Oct 28 '15 at 19:59
  • It sounds like the reliance on global variables is the problem. Might I suggest reconsidering this? Alternatives are setting global options, when appropriate, and passing values in function arguments. – Jack Wasey Oct 28 '15 at 20:30
  • @Alex: when I'm collaborating with others, I don't expect them to take the time to read every line of my code carefully and take precautions in their code. -- in other words, I can't count on everybody else to be less sloppy and I don't blame them because they shouldn't have to do this. I don't consider R to be a very object oriented programming language... For now, I can make sure I only use local variable and passed parameters in my function. I hope I could do better than this – BlueFeet Oct 28 '15 at 21:35

6 Answers6

12

So, it's not quite the same as __name__ == "__main__", but you might find the interactive function interesting here. Which returns TRUE if you are in an interactive mode.

So you can do something like this:

main <- function() {
    ....
}

if(!interactive()) {
    main()
}

This is a bit different though because it will always run if it's required from a script.

Leif Andersen
  • 21,580
  • 20
  • 67
  • 100
5

but is there anything similar to

if __name__ == "__main__": 
    main()

in python?

There is indeed!

But you need to use the package ‘box’ instead of source/packages. Then, inside your module, you can write

if (is.null(box::name())) …

… which is equivalent to Python’s if __name__ == '__main__'.

Or you can even use the klmr/sys module. Then you can write the following:

box::use(klmr/sys)

f = function(x) {
    x + a
}

sys$run({
    a = 50
    sys$print(f(5))
})

If you execute this script on the command line (via Rscript or R CMD BATCH), it will execute the main function specified by sys$run. Conversely, if you import this script as a module into another script, the main function won’t be executed, but f will still be defined and exported.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • @NoName More “not thought of” than “not intended”. This is a clear deficiency in the built-in R mechanisms. ‘box’ merely fixes these deficiencies. It’s worth noting that Hadley Wickham (of ‘tidyverse’ fame, and one of the R Foundation members) commented on the original release of the ‘box’ module system, saying that if we could design the R package mechanism all over again, this is (something like) what it should look like. – Konrad Rudolph Jun 19 '21 at 14:37
4

Answering your exact question (I'm not addressing whether it makes sense in the context of R. I'm only starting to dig deeper into R and finding it ill suited to write non-interactive programs so I'm blaming it on me). You can create a main function and pass to it the input arguments. However, note that for this work you don't call it via R. It seems that you need to use Rscript instead.

main <-function (argv)
{
  if (length (argv) < 3)
    {
      cat ('usage error: requires at least 3 arguments\n',
           file=stderr ());
      return (1);
    }

  cat (sprintf ('This program was called with %d arguments\n',
                length (argv)));

  return (0);
}

if (identical (environment (), globalenv ()))
  quit (status=main (commandArgs (trailingOnly = TRUE)));
carandraug
  • 12,938
  • 1
  • 26
  • 38
  • Elegant hack. However, it should be noted that the use of semicolons as statement delimiters is strongly discouraged in R, as is the spacing. – Konrad Rudolph Jun 06 '17 at 21:50
  • @KonradRudolph why is it discouraged? Does it behave weird? Or it just a matter of style? – carandraug Jun 07 '17 at 09:39
  • Since it fulfils no purpose, it’s syntactic [visual clutter](http://www.infovis-wiki.net/index.php?title=Visual_Clutter), i.e. it decreases the signal-to-noise ratio of code and therefore makes it slightly harder to read and more error-prone. It’s discouraged for similar reasons that [chartjunk](https://en.wikipedia.org/wiki/Chartjunk) is. – Konrad Rudolph Jun 07 '17 at 10:23
  • @KonradRudolph I think that the spacing makes it less cluttered. For what is worth, I follow the GNU standards for style. That's just a matter of style. I get the point for semicolons, but their number is so small and they feel so common that they don't feel out of place. – carandraug Jun 07 '17 at 10:58
  • 1
    The spacing in your code isn’t clutter, but it *is* highly non-idiomatic. The virtually unanimous convention is to have two spaces around operators, no spaces between the function call and the opening parenthesis, and the opening brace on the same line as the previous statement, and the closing brace intended At the same level as the parent block. (For operator spacing, the GNU coding standards says the same). The GNU standards are also pretty much unused outside of GNU projects, and are generally seen as seriously whacky. – Konrad Rudolph Jun 07 '17 at 11:10
  • @KonradRudolph [R is a GNU project](https://www.r-project.org/about.html). But anyway, all of that doesn't matter because it boils down to a matter of style. The question was about calling a specific function when called as a script, which I hope my answer does address. – carandraug Jun 07 '17 at 11:34
  • 3
    R may be a GNU project but it doesn't follow GNU code style. I'm calling out the formatting because Stack Overflow answers teach programming and it's generally a bad idea to teach nonstandard or questionable techniques unless there's a good reason. – Konrad Rudolph Jun 07 '17 at 11:35
  • The second `cat` call and `return(0)` are only called if `length(argv) >= 3`, hence I think your function might benefit from an `else`-statement to make it clearer what happens. I would not call that *visual clutter*; rather the contrary but it is, of course, just a stylistic preference. – Therkel Apr 09 '18 at 06:07
2

Not sure what the point of your function is, but perhaps a solution is to use a default argument

f <- function(x, a = 45) {
  x + a
}
a <- 50
f(5)

Then if you need to change the a term, change it within the function rather than rely on the context in which it is called.

Hugh
  • 15,521
  • 12
  • 57
  • 100
1

The simplest solution that I have found that will work even when running source("..") from the interactive terminal (unlike Leif Andersen's solution) is:

if (sys.nframe() == 0) {
    # ... do main stuff
}

sys.nframe() is equal to 0 when run from the interactive terminal or using Rscript.exe, in which case the main code will run. Otherwise when the code is sourced, sys.nframe() is equal to 4 (in my case, not sure how it works exactly) which will prevent the main code from running.

source

Bakr
  • 117
  • 8
-1

You can try the below... Place the below block in your R script as part of the global environment and it will invoke the main() method when the script is executed.

if(getOption("run.main", default=TRUE)) {
   main()
}

Hope this fits in with your stated requirement

if __name__ == "__main__": 
    main()
bincob
  • 859
  • 1
  • 9
  • 13