If/Else Constructs Inside and Outside Functions

if/else constructs inside and outside functions

It’s a consequence of using an interactive shell (REPL) to run scripts:

After the first branch the shell has seen a complete statement so it assumes that you’re done typing. Unfortunately, R uses the same shell for interpreting scripts even if they are not typed in interactively – so even when you save the if statement to a file and source it (or pipe it into R) you will get the error on the else branch.

But the following will work just fine:

if (exp) a <- 1 else a <- 2

Here, the interpreter swallows the line and executes it.

In your function one would assume that the same applies – and it does! However, the function itself starts with an open brace in your case, so R has to read until it finds the matching closing brace. By contrast, take this function declaration:

f <- function (exp)
if (exp)
a <- 1
else
a <- 2

In R you can define functions without braces around the body. But the above code will fail for the same reason that the standalone if without braces fails. By contrast, if I had written the if on a single line this code would once again work.

Incidentally, your function uses an assignment to a variable that isn’t used. You can (should) do the following instead:

f <- function (exp) {
if (exp)
1
else
2
}

… and the same when using if inside the shell:

a <- if (exp) 1 else 2

because in R, if is an expression which returns a value.

R if/else in function

Solution

Use R's immanent vectorized ability. Select by [ and change the value by <- assignment.

This solution is very R-ish:

winsorize <- function(x) {
m <- mean(x)
s <- sd(x)
u <- m + 3 * s
l <- m - 3 * s
x[ x > u ] <- u # select elements > u and assign to them u in situ
x[ x < l ] <- l # select elements < l and assign to them l in situ
x # return the resulting vector
}

And also this solution is very R-ish with the already vectorized ifelse() function:

winsorize <- function(x) {
m <- mean(x)
s <- sd(x)
u <- m + 3 * s
l <- m - 3 * s
ifelse( x > u, u, ifelse( x < l, l, x))
}

Solution with sapply()

Another possibility is to use sapply(x, ...) to apply your if-else constructs on each element of x.

winsorize <- function(x){
m <- mean(x)
s <- sd(x)
upper <- m + 3 * s
lower <- m - 3 * s
# apply your if-else construct on each individual element (el) of x
# using `sapply()`
sapply(x, function(el) if(el > upper){
upper
} else if (el < lower) {
lower
} else {
el})
}

Or the same with ifelse():

winsorize <- function(x){
m <- mean(x)
s <- sd(x)
upper <- m + 3 * s
lower <- m - 3 * s
sapply(x, function(el)
ifelse(el > upper, upper, ifelse(el < lower, lower, el))
}

Solution with Vectorize()

Or make a function out of your if-else construct, vectorize this function using Vectorize() before you apply it on x:

winsorize <- function(x){
m <- mean(x)
s <- sd(x)
upper <- m + 3 * s
lower <- m - 3 * s
# define function for one element
winsorize.one.element <- function(el) {
if(el > upper){ upper } else if (el < lower) { lower } else { el}
}
# Vectorize this function
winsorize.elements <- Vectorize(winsorize.one.element)
# Apply the vectorized function to the vector and return the result
winsorize.elements(x)
}

This winsorize.one.element function can be written neater by ifelse,
but although ifelse is vectorized

Good way of calling Functions/class in big if/else constructs

The more interesting question imo is what exactly you want to achieve by this?

Java is an object-oriented language. Therefore i would solve this by one subclass per type:

abstract class Type{
abstract void method();
}

class Type1 extends Type{
void method(){
//do sth. specific for this type
}
}

If the methods are actually all in the same class you could still call them out of these classes by simply passing yourself (i see that this could get ugly).

class RandomClass(){
void method1(){
//do sth for type1
}
void method2(){
//do sth for type2
}
}

abstract class Type{
RandomClass randomClass;
Type(RandomClass randomClass){
this.randomClass = randomClass;
}
abstract void method();
}

class Type1 extends Type{
void method(){
randomClass.method1();
}
}
class Type2 extends Type{
void method(){
randomClass.method2();
}
}

Otherwise you could use reflection, like suggested by Sohaib (example taken from his suggested link):

Yyyy.class.getMethod("methodName").invoke(someArgs)

But using Reflection for somehting like this seems very unhandy as it is inperformant and a nice trap for later maintenance (just imagine someone starts renaming the methods).

So to answer the question itself (at least how i understand it):

Dynamically calling methods e.g. by determining their name dynamically at runtime, is something you only do in scripting languages.
The object-oriented approach might come with overhead, but at the end is the better style for this kind of language.

If both solutions do not work for you, a switch statement or if-else cascade is your best alternative.

Why can't if and else statements be on separate lines in R

This has nothing to do with enforcing a style, but to do with how the R parser works. In essence, when your if clause is syntactically complete with a newline at the end of its closing bracket, the console thinks you are done with that instruction, and waits for its next instruction. But an else clause has to be part of the same instruction. If the next instruction starts with an else, the R parser doesn't know what the else is referring to.

Let's demonstrate this for clarity. If you paste the following code into the console:

x <- 5

if(x<10){
print("x<10")
}

Then you will see this:

> x <- 5
>
> if(x<10){
+ print("x<10")
+ }
[1] "x<10"
>

Notice that if a line of code is syntactically complete, R will run the code, then print the console marker > at the start of the next line. If a newline symbol is present in the pasted code, but the code is syntactically incomplete, the console places a + at the start of each line, indicating that the R parser needs more input before it has syntactically complete code to run.

But if you run your example code, look what happens:

> if(x<10){
+ print("x<10")
+ }
[1] "x<10"
> else{
Error: unexpected 'else' in "else"
> print("x>9")
[1] "x>9"
> }
Error: unexpected '}' in "}"

Your if statement with its associated brackets was a syntactically complete element, so R ran the code. It printed out [1] "x<10" as expected, then waited for its next instruction. However, its next instruction was else {, and the R parser knows that no syntactically correct R code can start with an else. R can't go back in time and undo what it did in the if clause just because you subsequently write an else clause.

So, this has nothing to do with enforcing a style. You simply need a way of saying to R "I'm not done yet" after an if clause. There are various ways to do this. For example, your code would be fine inside a function, or a loop, or brackets. These are just ways of saying to the R parser : "don't run the code yet, I'm not done."

x <- 5

{

if(x<10){
print("x<10")
}
else{
print("x>9")
}

}
#> [1] "x<10"

Created on 2022-02-11 by the reprex package (v2.0.1)

Unexpected behaviour adding two if(){}else{} constructs

The else clause doesn't end till R can identify the end of the expression. In R the {} aren't part of the syntax for if/else statements. The {} can be used anywhere you want to possibly put multiple statements. You can also do

if(TRUE) 1 else 0 + if(TRUE) 1 else 0 

The {} aren't really meaningful. And since

 0 + if(TRUE) 1 else 0 

is a valid expression, R just assumes you wanted all of that for your else clause. Normally R will end the else clause when it encounters a newline after a completed expression. This means that

if(TRUE){1}else{0} + 
if(TRUE){1}else{0}

will also return the same value because the + at the end of the first line indicates that there's more to come because a valid expression can't end in +.

Note you can see how the expression is turned into the abstract syntax tree with the help of the lobstr package if you are really curious.

#lobstr::ast(if(TRUE){1}else{0} + if(TRUE){1}else{0})
o-`if`
+-TRUE
+-o-`{`
| \-1
\-o-`+`
+-o-`{`
| \-0
\-o-`if`
+-TRUE
+-o-`{`
| \-1
\-o-`{`
\-0

Here we see that everything is nested in the first if. The + is not the main operator.

As you've done, you can use () or {} to end the expression blocks explicitly

{if(TRUE){1}else{0}} + {if(TRUE){1}else{0}}

Consider also the case of

x <- 5
if(FALSE) x+1 else x+2
# [1] 7
if(FALSE) x+1 else {x}+{2}
# [1] 7

Note how the x+2 is taken all together for the else expression. It doesn't end at the first symbol x.

Interpreting error message in if condition in R

In the R definition:

When the if statement is not in a block the else, if present, must
appear on the same line as the end of statement2. Otherwise the new
line at the end of statement2 completes the if and yields a
syntactically complete statement that is evaluated. A simple solution
is to use a compound statement wrapped in braces, putting the else on
the same line as the closing brace that marks the end of the
statement.

(R Language Definition)

So, the 'else' must me in the same line as '}'. Either

a=2 

if (length(a)>1){
b<<-rowSums(c[,-(1:2)][,c(T,F)]) } else {b<<-sum(c[seq(3,length(x),2)]) }

or

a=2 

if (length(a)>1){
b<<-rowSums(c[,-(1:2)][,c(T,F)])
} else {b<<-sum(c[seq(3,length(x),2)]) }

but is still a bad code/identation pattern. I'd try something like

a=2 

if (length(a)>1) {
b<<-rowSums(c[,-(1:2)][,c(T,F)])
} else {
b<<-sum(c[seq(3,length(x),2)])
}

to avoid further mistakes, or the suggested 'ifelse' function.

Use if else to declare a `let` or `const` to use after the if/else?

This is a good example of where a simple ternary assignment could suffice:

const classes = withBorder ?
`${styles.circularBorder} ${styles.dimensions} ${styles.circularPadding} row flex-items-xs-middle flex-items-xs-center` :
`${styles.dimensions} ${styles.circularPadding} row flex-items-xs-middle flex-items-xs-center`

As specified in other comments/answers let and const are block scoped, so that's why they don't work in your example.

For DRYer code, you can use the ternary only for the part that depends on it:

 const classes = (withBorder ? `${styles.circularBorder} ` : "") +
`${styles.dimensions} ${styles.circularPadding} row flex-items-xs-middle flex-items-xs-center`

R if-else not working

It is that you are lacking curly braces. Try

if(is.na(result)[1]) {
print("NA")
} else {
coef(result)[[1]]
}

This matters less when your source an entire file at once but for line-by-line parsing (eg when you enter at the prompt) you have to tell R that more code is coming.



Related Topics



Leave a reply



Submit