Оптимизация функций в R: переосмысление аргументов с S7
Функции с множеством аргументов могут быть трудными для использования и сопровождения. Чтобы решить эту проблему, можно использовать подход с объектами опций (options objects), применяя возможности системы S7 (новая реализация ООП в R о которой мы недавно
говорили). Рассмотрим пример, как можно переработать функцию readr::read_csv().
Проблема: слишком много параметров
Функция
readr::read_csv()
имеет более 20 аргументов, многие из которых редко используются, например,
locale
,
skip
,
na
. Перенос менее востребованных настроек в объект опций позволяет:
● Сократить количество аргументов в функции.
● Улучшить читаемость и поддерживаемость кода.
● Обеспечить строгую типизацию параметров.
Шаг 1: Создание свойств с помощью new_property()
Для начала определим свойства, которые будут использоваться в нашем объекте опций. Например, свойство для параметра `locale:
library(S7)
.locale <- new_property(
class_list,
default = readr::default_locale(),
validator = function(value) {
dnames <- value$date_names
invalid <- !rlang::is_character(dnames$mon, n = 12) ||
!rlang::is_character(dnames$mon_ab, n = 12) ||
!rlang::is_character(dnames$day, n = 7) ||
!rlang::is_character(dnames$day_ab, n = 7) ||
!rlang::is_character(dnames$am_pm, n = 2) || !rlang::is_scalar_character(value$date_format) || !rlang::is_scalar_character(value$time_format) || !rlang::is_scalar_character(value$decimal_mark) || !rlang::is_scalar_character(value$grouping_mark) || !rlang::is_scalar_character(value$tz) || !rlang::is_scalar_character(value$encoding)
if (invalid) {
"expected `locale` object"
}
}
)
Этот код задает значения по умолчанию и проверяет, что все элементы
locale
имеют правильный тип.
Подобным образом мы можем создать объекты опций для большинства других аргументов:
●
na
●
quote
●
comment
●
trim_ws
●
skip
●
n_max
●
guess_max
●
name_repair
●
num_threads
●
progress
●
show_col_types
●
skip_empty_rows
●
lazy
Шаг 2: Создание класса с помощью new_class()
Теперь создадим класс для объекта опций, который объединяет все свойства, включая
locale
,
na
,
trim_ws
и другие:
class_readr_opts <- new_class(
"readr_opts",
properties = list(
locale = .locale,
na = .na,
quote = .quote,
comment = .comment,
trim_ws = .trim_ws,
skip = .skip,
n_max = .n_max,
guess_max = .guess_max,
name_repair = .name_repair,
num_threads = .num_threads,
progress = .progress,
show_col_types = .show_col_types,
skip_empty_rows = .skip_empty_rows,
lazy = .lazy
)
)
opts <- class_readr_opts()
opts
Такой подход позволяет гибко задавать опции с использованием строгой типизации.
Шаг 3: Переопределение функции read_csv()
После создания объекта опций можно переопределить функцию read_csv() для работы с новым подходом:
read_csv <- function(
file,
col_names = TRUE,
col_types = NULL,
col_select = NULL,
id = NULL,
options = class_readr_opts()
) {
# function logic
}
Вместо передачи множества опциональных аргументов пользователь может передать объект
options
, упростив вызов и настройку функции.
————————————
Использование объектов опций с
S7
позволяет уменьшить сложность функций, сделать их более читаемыми и строго типизированными. Такой подход особенно полезен при разработке библиотек и сложных API.
Ссылки:
● В основе поста лежит статья
"S7 & Options objects"
#заметки_по_R