50

関数のように、呼び出し元の名前を取得する「非内部」の方法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>1evalq

ここに質問があります。実際には2つの質問です。1。環境を作成した関数の名前を取得する方法はありますか(そのように作成されたと仮定して)?2.上記の回避策が失敗するのはなぜですか?

編集:エラーメッセージを次のように表示したかったので、上記の回避策は失敗すると言いました

Error in test(x, y) : invalid input.

まるでstop声明がtest身体の一部であるかのように。したがって、質問2は次のように言い換えることができます。2':stop("invalid input.")発信者の環境で評価されたと考えて、発信者の名前をキャプチャする評価が行われなかったのはなぜですか。

4

6 に答える 6

35

@GavinSimpsonと@RicardoSportaに感謝しますが、私はそれを理解しました。誰かがSOでこれを検索した場合に備えて、回答を投稿します。

現在の呼び出しを生成した関数の名前は、次の方法で取得できます。

deparse(sys.calls()[[sys.nframe()-1]])

これは、関数の名前だけでなく、呼び出しオブジェクト全体を含む文字列を返します。解析する前にサブセット化することで、名前だけを取得できますsys.calls()[[sys.nframe()-1]]

引数をチェックし、エラーが発生した場合に実行を停止する関数を作成したので、これが必要でした。しかし、私はこの関数が(i)環境をダンプし、(ii)実行スタックの1レベル上の関数の名前を表示するようにしたかったのです。(i)は簡単ですが、(ii)で立ち往生しました。

私の投稿の2番目の質問については、次のようになります。式stop("invalid input")は関数の環境で評価されますが、実行スタックが異なるため、式がの本体のtest一部である場合と同じではありません。この2つのシナリオ。後者の場合、はその上にのみありますが、最初の場合は、、そして上向きになります。によって返される実行スタックは、囲んでいる環境と同じものではありません。これが混乱を引き起こす可能性があります。teststoptestevalchecktestsys.calls()

于 2013-03-25T17:49:24.507 に答える
23

を参照してください?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"

Noteは、引数なしで上記のように呼び出されたときに(実際には)match.call()を使用して機能します。だからあなたも相談したいかもしれません。sys.call()sys.call(sys.parent())?sys.call

于 2013-03-24T05:38:43.977 に答える
10

記録のために、ハドリーが示唆したように、あなたはを使うことができます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
于 2014-09-01T12:02:18.760 に答える
3

質問#1はGavin(use match.call)が回答します。

ただし、説明している内容に基づいて、traceback()他の関数に渡すことができる出力も確認する必要があります。


質問2について:

失敗しているわけではありませんが、期待どおりに機能しています。表示されているエラーは、本当の意味でのエラーではなく、stop(.)関数からのエラーです。

あなたが見ると、あなたはそれがあなたのどこにいるのprint(evalq)かを順番に呼び出すことがわかりますeval(substitute(expr), envir, enclos))exprstop("invalid input.")

正しい回避策は、もう1つのレベルの引用符を使用することです。

  evalq(quote(stop("invalid input.")))
  # stop("invalid input.")
于 2013-03-24T05:47:01.527 に答える
3

rlangパッケージの代替方法:

f <- function() {
  name <- rlang::call_frame(n = 2)$fn_name
  rlang::abort(paste("The function", name, "was called and I throw an error."))
}
g <- function() f()
g()
#> Error: The function g was called and I throw an error.

reprexパッケージ(v0.2.1)によって2019-03-03に作成されました

于 2019-03-03T14:01:48.473 に答える
0

上記の関数の関数名を取得するには、次のコマンドを使用できます。

gsub(pattern="^([A-Za-z0-9]+)(\\({1})(.*)(\\){1})$",replacement="\\1",x=deparse(sys.call(-1)))
于 2014-04-08T02:46:19.853 に答える