Как получить имя вызывающей функции внутри вызываемой процедуры?
Есть ли способ "не внутреннего" для получения имени вызывающего абонента, как это делает функция stop
?
Идея заключается в том, что у меня есть небольшая функция, которая проверяет входные данные и останавливает выполнение, если какое-либо условие не выполняется. Эта функция вызывается несколькими другими, которые используют один и тот же код проверки. Если вход недействителен, среда вызывающего объекта сбрасывается (поэтому я могу видеть аргументы, переданные функции), и выполнение останавливается.
Упрощенный пример:
check <- function(x)
{
if(x<0)
{
print(as.list(parent.frame()))
evalq(stop("invalid input."), parent.frame())
}
}
test <- function(x, y)
{
check(x)
}
Я думал, что оценка выражения quote(stop("blah"))
в среде вызывающего абонента заставит его показать имя вызывающего абонента. Однако в результате получается следующее:
test(-1, 2)
# $x
# [1] -1
#
# $y
# [1] 2
#
# Error in eval(substitute(expr), envir, enclos) : invalid input.
И это не изменится, если я использую parent.frame(n)
с n>1
в evalq
.
Итак, вот вопрос, на самом деле два вопроса: 1. Есть ли способ получить имя функции, создавшей среду (предполагая, что она была создана как таковая)? 2. Почему обходной путь выше не удается?
EDIT: Я сказал, что обходной путь выше не работает, потому что я хотел, чтобы сообщение об ошибке отображалось как
Error in test(x, y) : invalid input.
как будто оператор stop
был частью тела test
. Таким образом, вопрос 2 можно пересчитать как: 2 ': Почему оценка stop("invalid input.")
не захватила имя вызывающего абонента, учитывая, что она была оценена в среде вызывающего?
Ответы
Ответ 1
Спасибо @GavinSimpson и @RicardoSporta, но я понял это. Я отправлю ответ в случае, если кто-то ищет это в SO.
Имя функции, сгенерировавшей текущий вызов, может быть восстановлено
deparse(sys.calls()[[sys.nframe()-1]])
Возвращает строку, содержащую не только имя функции, но и весь объект вызова. Имя может быть восстановлено путем подмножества sys.calls()[[sys.nframe()-1]]
перед отпаркой.
Мне это нужно, потому что я написал функцию, которая проверяет аргументы и останавливает выполнение в случае ошибки. Но я хотел, чтобы эта функция (i) удалила среду и (ii) отобразила имя функции на одном уровне выше в стеке выполнения. (i) легко, но я застрял в (ii).
Что касается второго вопроса в моем сообщении, это то, что происходит: выражение stop("invalid input")
оценивается в среде функции test
, но это не то же самое, что выражение было частью test
, поскольку в этих двух сценариях исполняемый стек отличается. В последнем случае stop
имеет над ним только test
, но в первом он имеет eval
, check
, а затем test
вверх. Выполняемый стек, возвращенный sys.calls()
, не совпадает с окружающими средами. Это может вызвать путаницу.
Ответ 2
См. ?match.call
. Например:
foo <- function() {
match.call()[[1]]
}
foo()
as.character(foo())
который производит
> foo()
foo
>
> as.character(foo())
[1] "foo"
Упрощенная версия вашего кода
check <- function(x) {
match.call()[[1]]
}
test <- function(y) {
check(y)
}
дает
> test(2)
check
> as.character(test(2))
[1] "check"
Примечание match.call()
работает с использованием sys.call()
(на самом деле он вызывает sys.call(sys.parent())
) при вызове, как я сделал выше, без аргументов. Поэтому вы можете также обратиться к ?sys.call
.
Ответ 3
Для записи, как предположил Хэдли, вы можете использовать sys.call()
. Например:
funx = function(...) {
callingFun = as.list(sys.call(-1))[[1]]
calledFun = as.list(sys.call())[[1]]
message(paste(callingFun, " is calling ", calledFun, sep=""))
}
funy = function(...) {funx(...)}
> funy(a = 1, b = 2)
funy is calling funx
Ответ 4
На вопроС# 1 отвечает Гэвин (используйте match.call
).
Однако, основываясь на том, что вы описываете, вы также должны посмотреть traceback()
, выход которого вы можете передать другим функциям.
Что касается Вопрос №2:
Это не терпит неудачу, но работает точно так, как ожидалось. Ошибка, которую вы видите, не является ошибкой в истинном смысле, а скорее ошибкой вашей функции stop(.)
.
Если вы посмотрите на print(evalq)
, вы увидите, что он, в свою очередь, вызывает eval(substitute(expr), envir, enclos))
, где expr
- ваш stop("invalid input.")
Правильное обходное решение - использовать еще один уровень цитирования
evalq(quote(stop("invalid input.")))
# stop("invalid input.")
Ответ 5
Чтобы получить имя функции функции выше, вы можете просто использовать:
gsub(pattern="^([A-Za-z0-9]+)(\\({1})(.*)(\\){1})$",replacement="\\1",x=deparse(sys.call(-1)))