Я часто использую пакет assertthat
для проверки постусловий в функциях. Когда я читал больше об идее «Проектирование по контракту», я наткнулся на идею сделать проверки выходных данных в сравнении с входными значениями.
Самый простой пример:
toggle <- function(x)!x
Можно сразу заявить, что x == !old_x
всегда должно быть правдой. (old_x
обозначает значение x
до оценки.)
(Конечно, этот пример упрощен, и постусловие не добавляет больше полезной информации для людей или компьютеров. Более полезный пример находится в нижней части вопроса ..)
Таким образом, я могу расширить свою функцию toggle
следующим образом, чтобы проверять это условие при каждом вызове:
toggle <- function(x){
old_x <- x
x <- !x
assertthat::assert_that(x == !old_x)
return(x)
}
Это работает, конечно, но мне было интересно, есть ли другой способ получить доступ к значению old_x
без явного сохранения его (или результата) под новым именем. И без разделения кода проверки постусловий на верх и низ функции. Что-то вроде того, как R оценивает вызовы функций.
Одна попытка, которую я могу придумать, - это использовать sys.call
и eval.parent
для доступа к старым значениям:
toggle <- function(x){
x <- !x
.x <- eval.parent(as.list(sys.call())[[2]])
assertthat::assert_that(x == !.x)
return(x)
}
Это работает, но мне все еще нужно назначить новую переменную .x
, а также подмножество с [[2]]
пока не является гибким. Однако запись этого типа assertthat::assert_that(x == !eval.parent(as.list(sys.call())[[2]])
не работает, а поиск с уровнями поиска sys.call(-1 ..)
не помог.
Другой (немного более полезный) пример, где постусловие добавляет некоторую информацию:
increment_if_smaller_than_2 <- function(x){
old_x <- x
x <- ifelse(x < 2, x <- x + 1, x)
assertthat::assert_that(all(x >= old_x))
return(x)
}
Есть подсказки?