Хотя в нескольких ответах подчеркиваются различные аспекты старого и нового способов использования опций, я хотел бы сделать несколько дополнительных замечаний.Более новые конструкции OptionValue
- OptionsPattern
обеспечивают большую безопасность, чем OptionQ
, поскольку OptionValue
проверяет список глобальных опций, чтобы убедиться, что переданная опция известна функции.Однако более старый OptionQ
кажется более простым для понимания, поскольку он основан только на стандартном сопоставлении с образцом и не имеет прямого отношения ни к одному из глобальных свойств.Хотите вы или нет эту дополнительную безопасность, обеспечиваемую любой из этих конструкций, решать вам, но я предполагаю, что большинство людей считают ее полезной, особенно для более крупных проектов.
Одна из причин, почему эти проверки типов действительно полезны, заключается в том, что часто опции передаются в качестве параметров функциями в виде цепочки, фильтруются и т. Д., Поэтому без таких проверок некоторые ошибки сопоставления с образцом будуточень трудно поймать, так как они будут причинять вред "далеко" от места их происхождения.
С точки зрения основного языка конструкции 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
является частью языка шаблонов.Одним из них является улучшение скорости, обсуждаемое @Sasha.Однако вопросы скорости часто переоцениваются (это не умаляет его наблюдений), и я ожидаю, что это будет особенно актуально для функций с опциями, поскольку это, как правило, функции более высокого уровня, которые, вероятно, будут иметьтривиальное тело, на которое будет потрачена большая часть времени вычислений.
Еще одно довольно интересное отличие - это когда нужно передать опции в функцию, которая содержит свои аргументы.Рассмотрим следующий пример:
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
как часть оценки OptionQ
, поскольку OptionQ
не содержит аргументов.Чтобы избежать утечки оценки, необходимо использовать Function[opt,OptionQ[Unevaluated[opt]],HoldAll]
вместо OptionQ
.С OptionsPattern
у нас нет этой проблемы, так как факт совпадения может быть установлен чисто синтаксически:
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 ++ (если это уместно): автоматизация и (возможно) безопасность против простоты и чистоты.Мои два цента.