web-dev-qa-db-ja.com

欠落している引数をRの関数から関数に渡す

関数でオプションの引数を使用し、missing()でそれらをチェックするのが一般的な方法であることを学びました(たとえば、 SO 22024082 で説明されているように)

この例では、round0はオプションの引数です(round0は論理として定義できます)。

foo = function(a, round0) {
    a = a * pi
    if(!missing(round0)) round(a)
    else a
}

しかし、この関数を別の関数から呼び出すと、どうすれば「欠落」を渡すことができますか?

bar = function(b) {
    if(b > 10) round1=T
    foo(b, round1)
}

B <10の場合、bar()のround1は定義されていませんが、とにかくfooに渡されます。 foo()を変更した場合:

foo = function(a, round0) {
    a = a * pi
    print(missing(round0))
    print(round0)
    if(!missing(round0)) round(a)
    else a
}

bar(9)を実行すると、出力は次のようになります。

bar(9)
[1] FALSE
Error in print(round0) : object 'round1' not found
Called from: print(round0)

つまり、round0は欠落していませんが、アクセスすることもできませんか?

Bar()で異なる関数呼び出しを使用したくないのですが、foo()にいくつかのオプションの引数がある場合、欠落している/欠落していないすべてのオプションの引数の組み合わせに対して関数呼び出しを作成する必要があります。

「欠落」を渡すことは可能ですか、またはこの問題に適用される他の解決策は何ですか?

15
MarkusN

あなたの例では、round0は欠落しておらず、(欠落しているのではなく)未定義のround1に設定されています。

これを行う一般的な最良の方法は、デフォルト値を使用することです。この場合はFALSEです。

foo = function(a, round0 = FALSE) {
    a = a * pi
    if (!round0) round(a)
    else a
}

bar = function(b) {
    round1 <- FALSE
    if (b > 10) round1=TRUE
    foo(b, round1)
}

または、デフォルト値をパラメータリストで簡単に表現できない場合:

foo = function(a, round0 = NULL) {
    a = a * pi
    if(!is.null(round0)) round(a)
    else a
}

bar = function(b) {
    round1 <- NULL
    if (b > 10) round1=TRUE
    foo(b, round1)
}

どちらの場合も、呼び出し元の関数でパラメーターを手動でデフォルト値に設定する必要があることに注意してください。

fooステートメント内で必要に応じて、引数の有無にかかわらずif関数を呼び出すこともできます。

bar = function(b) {
    if (b > 10) foo(b, TRUE) else foo(b)
}

しかし、(私が知る限り)引数として渡さない以外に変数missingを作成することはできません。

8
Nick Kennedy

私は最近同様の問題に遭遇し、一般的な方法でそれを解決したいと思いました。以下の関数g()の定義に示されているように実行できると思います。

_f <- function(a = 5, b = 3, c = 2, d = 7) {
  if (missing(a)) {print("a is missing.")}
  if (missing(b)) {print("b is missing.")}
  if (missing(c)) {print("c is missing.")}
  if (missing(d)) {print("d is missing.")}

  cat(a, b, c, d)
}

g <- function(a = 1, b = 1, c = 1, d = 1) {
  args <- as.list(match.call())
  args[[1]] <- NULL # remove first list element, it's the function call
  do.call(f, args, envir = parent.frame())
}
_

さまざまな引数のセットを使用してg()を呼び出すと次のようになります。

_> g()
[1] "a is missing."
[1] "b is missing."
[1] "c is missing."
[1] "d is missing."
5 3 2 7

> g(a = 3)
[1] "b is missing."
[1] "c is missing."
[1] "d is missing."
3 3 2 7

> g(b = 10, c = 10)
[1] "a is missing."
[1] "d is missing."
5 10 10 7
_

すべての引数を次の関数に渡したくない場合、または一部を追加したい場合は、argsリストに追加またはリストから削除できます。例として、これを一般的な方法で実行する次の関数g()を参照してください。

_g <- function(a = 1, b = 1, c = 1, x = 1, y = 1, z = 1) {
  f_args <- c("a", "b", "c") # arguments we want to hand off to function f

  # obtain the list of arguments provided
  args <- as.list(match.call())
  # remove first list element, it's the function call
  args[[1]] <- NULL
  # remove the arguments that are not listed in f_args
  args <- args[na.omit(match(f_args, names(args)))]

  # now add argument d, we always want it to be 0:
  args <- c(args, list(d = 0))
  do.call(f, args, envir = parent.frame())
}
_

さまざまな引数のセットを使用してg()を呼び出すと次のようになります。

_> g()
[1] "a is missing."
[1] "b is missing."
[1] "c is missing."
5 3 2 0

> g(a = 3)
[1] "b is missing."
[1] "c is missing."
3 3 2 0

> g(b = 10, c = 10)
[1] "a is missing."
5 10 10 0
_

do.call()の詳細については、 この回答 を参照してください。

3
Claus Wilke

引数なしでsubstitute()を使用すると、欠落しているオブジェクトを作成できます。

あなたの場合、round1else句で欠落しているオブジェクトにすることができます。

foo = function(a, round0) {
  a = a * pi
  if(!missing(round0)) round(a)
  else a
}

bar = function(b) {
  if(b > 10) round1=T else round1 <- substitute()
  foo(b, round1)
}

bar(9)
#> [1] 28.27433

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

1