У R є набір операторів, такий самий, як у багатьох інших мовах:
1 == 2 # FALSE
1 != 2 # TRUE
1 < 2 # TRUE
1 > 2 # FALSE
1 <= 2 # TRUE
1 >= 2 # FALSE
Основна форма конструкції if-then-else така:
if (x > 10) {
y <- "big"
} else if (x > 2) {
y <- "medium"
} else {
y <- "tiny"
}
У цьому випадку обовʼязкові і дужки ( ) навколо булевого значення (англ. Boolean), і фігурні дужки { } навколо тіла.
Просту умовну конструкцію if можна скоротити:
if (x == 0) return("success")
Забігаючи наперед до концепції vector-filtering: умова, узята в дужки (), має обчислюватися до одного значення TRUE/FALSE.
Якщо включити вектори в порівняння, зазвичай отримаємо вектор булевих значень, тож його потрібно загорнути в агрегувальну функцію, наприклад all() (чи всі значення TRUE?) або any() (чи хоча б одне значення TRUE?).
numbers <- c(4,5,7,9,10,11)
numbers %% 3 == 0 # gives vector of booleans
#> [1] FALSE FALSE FALSE TRUE FALSE FALSE
# use any() to get a single TRUE/FALSE
if (any(numbers %% 3 == 0)) print("1 or more numbers are divisible by 3")
#> [1] "1 or more numbers are divisible by 3"
ifelse
Альтернативна форма if-else може стати в пригоді, за умови, що результат буває лише правдою або неправдою.
y <- ifelse(x > 8, "big", "small")
# Don't mix result types:
z <- ifelse(x > 100, 100, "small") # numeric or string result? Expect problems!
ifelse() приймає рівно три параметри:
TRUE
FALSE.Вхідні дані можуть бути векторами, і тоді на виході отримаємо вектор.
У цьому випадку переконаймося, що обидві гілки дають однаковий тип даних: числовий, символьний, логічний тощо.
Про це йтиметься в концепції vector-functions.
Обидва результати ("big", "small" у прикладі вище) мають бути одиничними значеннями.
На жаль, якщо передати сюди довший вектор, R, найімовірніше, зробить щось заплутане й несподіване і не видасть жодного попереджувального повідомлення.
if_else
Супровідники мови R дотримуються політики якомога більше уникати змін, що ламають сумісність. Це допомагає підтримувати величезну кількість сторонніх пакетів, доступних у CRAN, але обмежує можливість оновлювати базовий R сучасними концепціями програмування.
Останніми роками вийшло кілька пакетів, які розширюють, а іноді й замінюють функціональність R, спираючись на десятиліття досвіду вивчення сильних і слабких сторін початкового дизайну R.
Ці пакети утворюють колекцію tidyverse.
Їх додали до системи запуску тестів Exercism у 2023 році, і ми можемо використовувати їх у вправах.
Наш трек R зʼявився на кілька років раніше, ніж додали tidyverse, тож можна помітити, що багато рішень спільноти тримаються базового R.
dplyr
dplyr здебільшого призначений для роботи з датафреймами (про це йтиметься далі в курсі).
Додати його можна або за допомогою library(dplyr) на початку коду, або library(tidyverse), щоб підключити всю колекцію.
Можна побачити попереджувальні повідомлення про замасковані імена, але це рідко стає проблемою.
У межах цієї концепції варто згадати функцію if_else.
Вона здебільшого схожа на ifelse з базового R.
library(dplyr) # warnings not shown
x <- 10
if_else(x > 8, "big", "small")
#> [1] "big"
Головна відмінність у тому, що if_else може приймати необовʼязковий четвертий аргумент для пропущених значень, які в R позначаються як NA.
xs <- c(3, NA, 10, NA, 4) # a 5-element vector
xs
#> [1] 3 NA 10 NA 4
if_else(xs > 8, "big", "small", "missing")
#> [1] "small" "missing" "big" "missing" "small"
# contrast:
ifelse(xs > 8, "big", "small")
#> [1] "small" NA "big" NA "small"
Також можна помітити, що if_else дає значно кращі повідомлення про помилки, ніж старіша ifelse.
Після завантаження бібліотеки dplyr нам також стане доступна функція between().
R не дозволяє обʼєднувати порівняння ланцюжком, тож 1 <= x <= 5 видасть повідомлення про помилку.
Із завантаженим dplyr вираз between(x, 1, 5) рівнозначний x >= 1 & x <= 5 і, можливо, читається краще.
І нижня, і верхня межі включні.