Подтвердить что ты не робот

Как получить имя вызывающей функции внутри вызываемой процедуры?

Есть ли способ "не внутреннего" для получения имени вызывающего абонента, как это делает функция 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.") не захватила имя вызывающего абонента, учитывая, что она была оценена в среде вызывающего?

4b9b3361

Ответ 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)))