10

これは非常に単純で一般的な質問ですが、まだ議論されていません。何も見逃していないことを願っています。

関数のいくつかのレイヤーを持つ大きなプログラムの設計を開始しています。他のプログラミング言語には明確な戦略がありますが、「引数」を持つ関数の「パラメーター」を処理する方法について、R で標準的な解決策を見つけることができません。 "。「パラメーター」と「引数」は実際には関数の入力と同じですが、概念的な違いがあります。前者はより高いレベルに設定され、頻繁に変更されることはありませんが、後者は関数が処理する実際のデータです。

この簡単な例を考えてみましょう: 単純なスキーマ

対象のサブ関数 SF() は、「WORKER」によって異なる引数を使用して何度も照会されますが、「上」に設定されている同じパラメーターを使用します。もちろん、同じ質問が複数のレイヤーを持つより複雑なケースにも当てはまります。

これに対処する方法は 2 つあります。 1. すべてを継承しますが、関数呼び出しに無数の引数が含まれるか、これらすべての引数を囲む構造体になります。b. R は関数を呼び出すために引数のコピーを作成するため、あまり効率的ではない可能性があります。2. パラメータを変更するたびに関数を動的に評価し、それらを関数定義に「ハードワイヤ」します。しかし、特にクリーンな方法でそれを行う方法がわかりません。

これはどれも本当に好感が持てるとは思えないので、その点について意見はありますか?R のいくつかの環境機能を使用できるのではないでしょうか? :-)

ありがとう!

EDIT : 一部の人にとっては、コードはグラフよりも優れているため、メソッド「1.」を使用してすべての引数を渡すダミーの例を次に示します。多くのレイヤーとサブ関数がある場合、すべてのパラメーターを中間レイヤー (ここでは WORKER()) に渡すのは良くないようです。(コードとパフォーマンスの観点から)

F <- function(){
  param <- getParam()
  result <- WORKER(param)
  return(result)
}

getParam <- function(){
  return('O')
}

WORKER <- function(param) {
  X <- LETTERS[1:20]
  interm.result <- sapply(X,SF,param) # The use of sapply here negates maybe the performance issue?
  return(which(interm.result=='SO'))
}

SF <- function(x,param) {
  paste0(x,param)
}

EDIT 2 : 上記の例の単純さは、私の問題を見ている親切な人々の一部を誤解させるため、離散勾配降下を使用したより具体的な図を次に示します。繰り返しますが、すべてを同じ大きな関数で記述できるようにシンプルに保ちましたが、それは私が実際の問題でやりたいことではありません。

gradientDescent <- function(initialPoint= 0.5, type = 'sin', iter_max = 100){ 
  point <- initialPoint
  iter <- 1
  E <- 3
  deltaError <- 1
  eprev <- 0
  while (abs(deltaError) > 10^(-2) | iter < iter_max) {
    v_points <- point + -100:100 / 1000
    E <- sapply(v_points, computeError, type)
    point <- v_points[which.min(E)]
    ef <- min(E)
    deltaError <- ef - eprev
    eprev <- ef
    iter <- iter+1
  }
  print(point)
  return(point)
}

computeError <- function(point, type) {
  if (type == 'sin') {
    e <- sin(point)
  } else if (type == 'cos') {
    e <- cos(point)    
  }
}

サブ関数が評価されるたびに、サブ関数の「タイプ」パラメーターを渡すのは最適ではないことがわかりました。@hadley によって Closures にもたらされた参照と @Greg の説明は、私が必要とするソリューションへの良い道筋のようです。

4

3 に答える 3

3

レキシカルスコープを探しているのではないかと思います。R はレキシカル スコープを使用します。つまり、関数 WORKER と SF を F 内で定義すると、現在の値paramが渡されることなくアクセスできるようになります。

レキシカル スコープを利用できない場合 (SF は F の外で定義する必要があります)、別のオプションは、パラメーターを格納する新しい環境を作成することです。必要なすべての関数がこの環境にアクセスできる場合 (明示的に、または継承によって (この環境を関数のエンクロージング環境にする))、F はparamこの環境に割り当てることができ、他の関数は値にアクセスできます。

于 2013-08-31T17:53:51.250 に答える
2

他の人に代わって話すリスクがありますが、あなたの質問に関心があり、回答が不足している理由は、あなたがこれを過度に複雑にしているように見えるからだと思います.

確かにあなたの例に示されているタスクを考えると、私は次のようなことをします:

SF <- function(x, par) {
    paste0(x, par)
}

F <- function(param) {
    which(sapply(LETTERS[1:20], FUN = SF, par = param) == "SO")
}

F(param="O")
#  S 
# 19 

または、Greg Snow が参照したレキシカル スコープを使用します。

F <- function(param) {
    SF <- function(x) {
         paste0(x, param)
    }
    which(sapply(LETTERS[1:20], FUN = SF) == "SO")
}
F(param="O")

または、実際には、paste0()ベクトル化されているという事実を利用して:

F <- function(param) {
    which(paste0(LETTERS[1:20], param) == "SO")
}
F("O")
# [1] 19

私の答えが単純すぎるかもしれないことは理解しています。あなたは明らかにもっと複雑なことを考えていますが、それが何であるかをもっとよく示す必要があると思います。より多くのヘルプを得るには、@baptiste の 2 番目のコメントの提案に従うことをお勧めします。抽象度の低い例を示し、引数なしで呼び出す理由を説明します (また、関数が必要な理由を示すこともできますF()) 。getParam()getParam()

于 2013-09-01T01:13:11.163 に答える
0

この質問は灰色ですが、この種の問題が解決された他のいくつかの方法に触れて、回答スロットに回答を提供すると役立つと思いました. パターンを見て報告することは、それを支持することと同じではないことに注意してください!

閉鎖

コメントと修正された回答で述べたように、クロージャーは確かにここで良い答えです。つまり、関数内に関数、つまりジェネレーター関数を定義し、ジェネレーター関数から生成された関数に情報を運ぶことができます。

generator_function <- function(param) {
  function() {
    param   
  }
}

generated_function <- generator_function(0)
generated_function()

computeError質問のコンテキストでは、これはの内部で定義することをお勧めしgradientDecent、その後、その環境にcomputeError持ち込むことができます。type

クロージャーを理解したら、それらが非常に強力であることがわかると思います。ただし、最初に考えるのは少し難しいです。さらに、それらに慣れておらず、生成された関数がジェネレーター関数の入力から切り離されてしまうと、デバッグが少し難しくなる可能性があります。これは、 の値が何でありtype、どこから来たのかについて混乱が生じる可能性があるためです。最初の問題を解決するには、 を心からお勧めしpryr::unencloseます。2 つ目は、必要が生じた場合に、私よりも賢明な意見を受け入れます。

オプションを設定する

?options多くの場合、未加工のパラメーターは、直接または getter/setter 関数 (例 ) を介してオプション (cf ) として設定されますknitr。ただし、関数がオプションとして設定されているのも時々見てきました。個人的には、このパターンはパッケージ間でかなり一貫性がなく、オプションは通常、特定の関数のドキュメントに埋め込まれているため嫌いですが、実際の呼び出しは、より高いレベルの関数を作成する必要がある場合に、より高いレベルの関数になる可能性があります。必要なものは、低次関数のドキュメントに埋もれている可能性があります。

...

一部の作成者は、ドットを自由に使用してパラメータスパゲッティを回避しています。ずっとドットです。このアプローチは非常に強力です。10 回のうち 9 回しか機能しません。欠点は、少なくとも私にとっては、ドットのデバッグとドキュメント化が難しいことです。たとえば、デバッグ側では、機能入力のいずれも厳密ではないため、タイプミスされたパラメーター名を見つけるのは難しいようです。

他の

もちろん他のパターンも!それらのトン。人々は環境を回ったり、リストを作成したりします。あなたにとって「正しい」答えは、おそらくあなたの個人的なスタイル、機能するもの、そして数か月後に戻って見たときに何が明らかになるかを組み合わせたものです。 .

于 2016-07-02T12:47:54.737 に答える