Jorgen Harmse
2022-Oct-24 15:45 UTC
[R] unexpected 'else' in " else" (Ebert,Timothy Aaron)
There were several interesting points about `ifelse`. The usual behaviour seems to be that all three inputs are evaluated, and the entries of `yes` corresponding to `TRUE` in `test` are combined with the entries of `no` corresponding to `FALSE` in `test`. Moreover, `yes` & `no` seem to be recycled as necessary in case `test` is longer. On top of that, there seems to be some sugar that suppresses evaluations in case `all(test)` and/or `all(!test)`, and the return type can be `logical` even if `yes` & `no` are not. I agreed with the other responses already, but my experiments further confirmed that `ifelse` is not interchangeable with `if(....) .... else ....`. The documentation confirms most of this, but 'same length and attributes (including dimensions and ?"class"?) as ?test?' looks wrong. The output seems to be `logical` or something related to the classes of `yes` & `no`. Regards, Jorgen Harmse.> ifelse(FALSE, {cat("Evaluating the vector for 'if'.\n"); 1:3}, {cat("Evaluating the vector for 'else'.\n"); 0:4})Evaluating the vector for 'else'. [1] 0> ifelse(rep(FALSE,5L), {cat("Evaluating the vector for 'if'.\n"); 1:3}, {cat("Evaluating the vector for 'else'.\n"); 0:4})Evaluating the vector for 'else'. [1] 0 1 2 3 4> ifelse(rep(TRUE,3L), {cat("Evaluating the vector for 'if'.\n"); 1:3}, {cat("Evaluating the vector for 'else'.\n"); 0:4})Evaluating the vector for 'if'. [1] 1 2 3> ifelse(c(TRUE,TRUE,FALSE), {cat("Evaluating the vector for 'if'.\n"); 1:3}, {cat("Evaluating the vector for 'else'.\n"); 0:4})Evaluating the vector for 'if'. Evaluating the vector for 'else'. [1] 1 2 2> ifelse(c(TRUE,TRUE,FALSE,TRUE), {cat("Evaluating the vector for 'if'.\n"); 1:3}, {cat("Evaluating the vector for 'else'.\n"); 0:4})Evaluating the vector for 'if'. Evaluating the vector for 'else'. [1] 1 2 2 1> args(ifelse)function (test, yes, no) NULL> ifelse(c(TRUE,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE), {cat("Evaluating the vector for 'if'.\n"); 1:3}, {cat("Evaluating the vector for 'else'.\n"); 0:4})Evaluating the vector for 'if'. Evaluating the vector for 'else'. [1] 1 2 2 1 2 0 1> ifelse(logical(0L), {cat("Evaluating the vector for 'if'.\n"); 1:3}, {cat("Evaluating the vector for 'else'.\n"); 0:4})logical(0)> ifelse(TRUE, integer(0L), numeric(0L))[1] NA> class(ifelse(TRUE, integer(0L), numeric(0L)))[1] "integer"> ifelse(integer(0L)) # test is an empty vector of integers and yes & no are missing.logical(0) [[alternative HTML version deleted]]
"...but 'same length and attributes (including dimensions and ?"class"?) as ?test?' looks wrong. The output seems to be `logical` or something related to the classes of `yes` & `no`." The documentation in fact says: "A vector of the same length and attributes (including dimensions and "class") as test and data values from the values of yes or no. **The mode of the answer will be coerced from logical to accommodate first any values taken from yes and then any values taken from no.** So the values are taken from 'yes' and 'no' (with coercion if they are of different classes), and the class of the result will presumably be inferred from the mode of those values. e.g.> z <- c(TRUE,TRUE,FALSE) > class(z)[1] "logical"> w <- ifelse(z,5,'a') > class(w)[1] "character" ## note coercion So it would appear that the ifelse() documentation needs to be clarified. For example, if the above asterisked phrase were "The S3 *class* of the answer will be inferred from the mode, where the mode of the answer will be coerced ..." that might resolve at least that bit of confusion However, that might also be incorrect -- what about S4 vs S3 vs Reference classes, for example (are such cases even possible?)? I leave resolution of these matters -- or at least their accurate and complete documentation -- to wiser heads. Cheers, Bert On Mon, Oct 24, 2022 at 8:45 AM Jorgen Harmse via R-help <r-help at r-project.org> wrote:> > There were several interesting points about `ifelse`. The usual behaviour seems to be that all three inputs are evaluated, and the entries of `yes` corresponding to `TRUE` in `test` are combined with the entries of `no` corresponding to `FALSE` in `test`. Moreover, `yes` & `no` seem to be recycled as necessary in case `test` is longer. On top of that, there seems to be some sugar that suppresses evaluations in case `all(test)` and/or `all(!test)`, and the return type can be `logical` even if `yes` & `no` are not. I agreed with the other responses already, but my experiments further confirmed that `ifelse` is not interchangeable with `if(....) .... else ....`. > > > > The documentation confirms most of this, but 'same length and attributes (including dimensions and ?"class"?) as ?test?' looks wrong. The output seems to be `logical` or something related to the classes of `yes` & `no`. > > > > Regards, > > Jorgen Harmse. > > > > > ifelse(FALSE, {cat("Evaluating the vector for 'if'.\n"); 1:3}, {cat("Evaluating the vector for 'else'.\n"); 0:4}) > > Evaluating the vector for 'else'. > > [1] 0 > > > ifelse(rep(FALSE,5L), {cat("Evaluating the vector for 'if'.\n"); 1:3}, {cat("Evaluating the vector for 'else'.\n"); 0:4}) > > Evaluating the vector for 'else'. > > [1] 0 1 2 3 4 > > > ifelse(rep(TRUE,3L), {cat("Evaluating the vector for 'if'.\n"); 1:3}, {cat("Evaluating the vector for 'else'.\n"); 0:4}) > > Evaluating the vector for 'if'. > > [1] 1 2 3 > > > ifelse(c(TRUE,TRUE,FALSE), {cat("Evaluating the vector for 'if'.\n"); 1:3}, {cat("Evaluating the vector for 'else'.\n"); 0:4}) > > Evaluating the vector for 'if'. > > Evaluating the vector for 'else'. > > [1] 1 2 2 > > > ifelse(c(TRUE,TRUE,FALSE,TRUE), {cat("Evaluating the vector for 'if'.\n"); 1:3}, {cat("Evaluating the vector for 'else'.\n"); 0:4}) > > Evaluating the vector for 'if'. > > Evaluating the vector for 'else'. > > [1] 1 2 2 1 > > > args(ifelse) > > function (test, yes, no) > > NULL > > > ifelse(c(TRUE,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE), {cat("Evaluating the vector for 'if'.\n"); 1:3}, {cat("Evaluating the vector for 'else'.\n"); 0:4}) > > Evaluating the vector for 'if'. > > Evaluating the vector for 'else'. > > [1] 1 2 2 1 2 0 1 > > > ifelse(logical(0L), {cat("Evaluating the vector for 'if'.\n"); 1:3}, {cat("Evaluating the vector for 'else'.\n"); 0:4}) > > logical(0) > > > ifelse(TRUE, integer(0L), numeric(0L)) > > [1] NA > > > class(ifelse(TRUE, integer(0L), numeric(0L))) > > [1] "integer" > > > ifelse(integer(0L)) # test is an empty vector of integers and yes & no are missing. > > logical(0) > > > > > > > > > [[alternative HTML version deleted]] > > ______________________________________________ > R-help at r-project.org mailing list -- To UNSUBSCRIBE and more, see > https://stat.ethz.ch/mailman/listinfo/r-help > PLEASE do read the posting guide http://www.R-project.org/posting-guide.html > and provide commented, minimal, self-contained, reproducible code.