23

古い習慣は一生懸命に死にます、そして私は私が現在開発している非常に大きなパッケージのopts___Ruleようにパターンマッチングと構造を使用していることに気づきます。thisoption /. {opts} /. Options[myfunction]Sal Manangoの「MathematicaCookbook」は、バージョン6以降のこれを行う方法がとであることを思い出させopts:OptionsPattern[]ますOptionValue[thisoption]。パッケージにはとにかくバージョン8が必要ですが、私はこの種のコードの書き方を何年にもわたって変更したことはありませんでした。

バージョン6以前の方法からすべてをリファクタリングする価値はありますか?パフォーマンスやその他のメリットはありますか?

よろしく

ヴェルベイア

編集:まとめ

この質問に対して多くの良い点がありましたので、ありがとうございました(もちろんプラス1つ)。要約すると、はい、使用するようにリファクタリングする必要がOptionsPatternありOptionValueます。(注:以前とOptionsPatternは異なりOptionPatternます!)理由はいくつかあります。

  1. 少し速いです(@Sasha)
  2. HoldForm引数が(@Leonid)になければならない関数をより適切に処理します
  3. OptionsPatternその関数に有効なオプションを渡していることを自動的にチェックしますFilterRules(別の関数(@Leonid)に渡す場合は引き続き必要です)
  4. RuleDelayed( )をはるかにうまく処理:>します(@rcollyer)
  5. Flatten(@Andrew)を使用せずにルールのネストされたリストを処理します
  6. OptionValue /@ list複数の呼び出しを行う代わりに、を使用して複数のローカル変数を割り当てる方が少し簡単ですsomeoptions /. {opts} /. Options[thisfunction](@rcollyerと私の間のコメントで出てきました)

編集:7月25日私は当初、/.構文を使用することは、実際に呼び出されている関数ではなく、別の関数からデフォルトのオプションを意図的に抽出する場合に意味があると考えていました。これは、ヘッドのリストを含むフォームを使用して処理されることがわかりますOptionsPattern[]。たとえば、次のようになります(ドキュメントOptionsPattern[{myLineGraph, DateListPlot, myDateTicks, GraphNotesGrid}]の「詳細情報」セクションを参照してください)。私は最近これを解決しただけです。

4

4 に答える 4

13

パターンマッチャーに依存するPatternTestと、後者はエバリュエーターの呼び出しを伴うため、使用するよりも実行が速くなるようです。とにかく、私のタイミングは、いくつかのスピードアップを達成できることを示していますが、リファクタリングを促すほど重要ではないと思います。

In[7]:= f[x__, opts : OptionsPattern[NIntegrate]] := {x, 
  OptionValue[WorkingPrecision]}

In[8]:= f2[x__, opts___?OptionQ] := {x, 
  WorkingPrecision /. {opts} /. Options[NIntegrate]}

In[9]:= AbsoluteTiming[Do[f[1, 2, PrecisionGoal -> 17], {10^6}];]

Out[9]= {5.0885088, Null}

In[10]:= AbsoluteTiming[Do[f2[1, 2, PrecisionGoal -> 17], {10^6}];]

Out[10]= {8.0908090, Null}

In[11]:= f[1, 2, PrecisionGoal -> 17]

Out[11]= {1, 2, MachinePrecision}

In[12]:= f2[1, 2, PrecisionGoal -> 17]

Out[12]= {1, 2, MachinePrecision}
于 2011-07-07T23:36:56.193 に答える
12

いくつかの回答は、オプションを使用する古い方法と新しい方法のさまざまな側面を強調していますが、いくつかの追加の観察を行いたいと思います。新しい構成は、グローバルオプションOptionValueのリストを調べて、渡されたオプションが関数に認識されていることを確認するため、OptionsPattern よりも安全性が高くなります。ただし、古いものは、標準のパターンマッチングのみに基づいており、グローバルプロパティのいずれにも直接関連していないため、理解しやすいようです。これらの構造のいずれかによって提供されるこの追加の安全性が必要かどうかはあなた次第ですが、特に大規模なプロジェクトでは、ほとんどの人がそれを役立つと思うと思います。OptionQOptionValueOptionQ

これらの型チェックが本当に役立つ理由の1つは、オプションがチェーンのような方法で関数によってパラメーターとして渡されたり、フィルター処理されたりすることが多いためです。そのため、このようなチェックがないと、パターンマッチングエラーの一部をキャッチするのが非常に困難になります。それらの起源の場所から「遠く」に害を引き起こしているでしょう。

コア言語に関しては、OptionValue-OptionsPattern構文はパターンマッチャーへの追加であり、おそらくそのすべての機能の中で最も「魔法のような」ものです。オプションをルールの特殊なケースと見なすことができる限り、意味的には必要ありませんでした。さらに、OptionValueパターンマッチングをOptions[symbol]グローバルプロパティに接続します。したがって、言語の純粋さを主張する場合、ルールはopts___?OptionQ理解しやすいように見えます。これを理解するために、標準のルール置換セマンティクス以外は何も必要ありません。

f[a_, b_, opts___?OptionQ] := Print[someOption/.Flatten[{opts}]/.Options[f]]

OptionQ(述語は、Mathematicaの古いバージョンのオプションを認識するように特別に設計されていることを思い出します)、これは:

f[a_, b_, opts:OptionsPattern[]] := Print[OptionValue[someOption]]

かなり魔法のように見えます。Traceを使用して、の短い形式がより長い形式に評価されることを確認すると、少し明確になりますOptionValueが、それが囲んでいる関数名を自動的に決定するという事実は依然として注目に値します。

OptionsPatternパターンランゲージの一部であることの結果はさらにいくつかあります。1つは、@Sashaによって議論された速度の改善です。ただし、速度の問題はしばしば強調されすぎており(これは彼の観察を損なうものではありません)、オプションのある関数は特に非レベルの関数である可能性が高いため、これは特に当てはまると思います。計算時間のほとんどが費やされる些細な本体。

もう1つの興味深い違いは、引数を保持する関数にオプションを渡す必要がある場合です。次の例を考えてみましょう。

ClearAll[f, ff, fff, a, b, c, d];
Options[f] = Options[ff] = {a -> 0, c -> 0};
SetAttributes[{f, ff}, HoldAll];
f[x_, y_, opts___?OptionQ] :=
   {{"Parameters:", {HoldForm[x], HoldForm[y]}}, {" options: ", {opts}}};
ff[x_, y_, opts : OptionsPattern[]] :=
   {{"Parameters:", {HoldForm[x], HoldForm[y]}}, {" options: ", {opts}}};

これで結構です:

In[199]:= f[Print["*"],Print["**"],a->b,c->d]
Out[199]= {{Parameters:,{Print[*],Print[**]}},{ options: ,{a->b,c->d}}}

しかし、ここOptionQでは、パターンマッチングプロセスの一部として、ベースの関数が評価をリークします。

In[200]:= f[Print["*"],Print["**"],Print["***"],a->b,c->d]
During evaluation of In[200]:= ***
Out[200]= f[Print[*],Print[**],Print[***],a->b,c->d]

これは完全に些細なことではありません。何が起こるかというと、パターンマッチャーは、一致または不一致の事実を確立するために、引数を保持していないため、Printの評価の一部として3番目のを評価する必要があります。評価リークを回避するには、の代わりにを使用する必要があります。一致の事実は純粋に構文的に確立できるため、この問題は発生しません。OptionQOptionQFunction[opt,OptionQ[Unevaluated[opt]],HoldAll]OptionQOptionsPattern

In[201]:= ff[Print["*"],Print["**"],a->b,c->d]
Out[201]= {{Parameters:,{Print[*],Print[**]}},{ options: ,{a->b,c->d}}}

In[202]:= ff[Print["*"],Print["**"],Print["***"],a->b,c->d]
Out[202]= ff[Print[*],Print[**],Print[***],a->b,c->d]

つまり、要約すると、ある方法を別の方法から選択することは、主に好みの問題だと思います。それぞれが生産的に使用でき、また、それぞれが悪用される可能性があります。安全性が高いため、新しい方法を使用する傾向がありますが、古い方法の方が意味的に理解しやすい一方で、驚くようなコーナーケースがいくつか存在することを排除しません。これは、C-C ++の比較に似ています(これが適切な場合):自動化と(おそらく)安全性と単純さと純粋さ。私の2セント。

于 2011-07-08T13:35:59.023 に答える
11

あまり知られていない(しかし頻繁に役立つ)事実は、オプションがネストされたリストに表示されることを許可されているということです。

In[1]:= MatchQ[{{a -> b}, c -> d}, OptionsPattern[]]

Out[1]= True

FilterRulesなどのオプション処理関数はこれについて知っています:

In[2]:= FilterRules[{{PlotRange -> 3}, PlotStyle -> Blue, 
  MaxIterations -> 5}, Options[Plot]]

Out[2]= {PlotRange -> 3, PlotStyle -> RGBColor[0, 0, 1]}

OptionValueはそれを考慮に入れます:

In[3]:= OptionValue[{{a -> b}, c -> d}, a]

Out[3]= b

ただし、ReplaceAll(/。)は、もちろんこれを考慮していません。

In[4]:= a /. {{a -> b}, c -> d}

During evaluation of In[4]:= ReplaceAll::rmix: Elements of {{a->b},c->d} are a mixture of lists and nonlists. >>

Out[4]= a /. {{a -> b}, c -> d}

したがって、OptionsPatternを使用する場合は、おそらくOptionValueも使用して、ユーザーが渡すオプションのセットを確実に使用できるようにする必要があります。

一方、ReplaceAll(/。)を使用する場合はopts___Rule、同じ理由で固執する必要があります。

opts___Ruleまた、特定の(明らかにあいまいな)場合には、少し寛容すぎることに注意してください。

有効なオプションではありません:

In[5]:= MatchQ[Unevaluated[Rule[a]], OptionsPattern[]]

Out[5]= False

しかし、___Ruleそれを通過させます:

In[6]:= MatchQ[Unevaluated[Rule[a]], ___Rule]

Out[6]= True

更新:rcollyerが指摘したように、もう1つの深刻な問題は、 RuleDelayed(:>)___Ruleで指定されたオプションが欠落していることです。これを回避することはできますが(rcollyerの回答を参照)、OptionValueを使用するもう1つの理由があります。

于 2011-07-08T01:18:25.673 に答える
9

コード自体には微妙ですが修正可能な欠陥があります。パターンopts___Ruleはフォームのオプションと一致しないa :> bため、使用する必要がある場合は、コードを更新する必要があります。差し迫った修正は、に置き換えることです。これには、より多くの入力が必要opts___Ruleです。しかし、私たちの間で怠惰な人にとっては、の短い形式よりも多くの入力が必要です。ただし、コードの読み取りがよりクリーンになると思います。opts:(___Rule | ___RuleDelayed)OptionsPattern[]OptionValue[...]ReplaceAll

OptionsPattern[]との使用法を見つけ、OptionValue読みやすく、何が行われているのかを即座に理解できるようになりました。古い形式のopts___ ...ReplaceAllは、最初のパススルーで理解するのがはるかに困難でした。それに加えて、明確なタイミングの利点があります。コードを更新します。

于 2011-07-08T02:58:56.080 に答える