Необязательные именованные аргументы без их переноса в "OptionValue" - PullRequest
4 голосов
/ 13 января 2011

Предположим, у меня есть функция с необязательными именованными аргументами, но я настаиваю на ссылке на аргументы по их неукрашенным именам.

Рассмотрим эту функцию, которая добавляет два именованных аргумента, a и b:

Options[f] = {a->0, b->0};  (* The default values. *)
f[OptionsPattern[]] := 
  OptionValue[a] + OptionValue[b]

Как я могу написать версию этой функции, в которой последняя строка заменяется просто a+b?(Представьте, что a+b - это целый код).

Ответы на следующий вопрос показывают, как сокращать OptionValue (легче сказать, чем сделать), но не как вообще от него избавиться: Необязательные именованные аргументы в Mathematica

Философское дополнение: Похоже, что у Mathematica будет эта магия с OptionsPattern и OptionValue, она может также пройти весь путь и иметьязыковая конструкция для правильного выполнения именованных аргументов, где вы можете просто ссылаться на них по их именам.Как и любой другой язык с именованными аргументами.(А пока мне любопытно, какие обходные пути возможны ...)

Ответы [ 3 ]

6 голосов
/ 13 января 2011

Почему бы просто не использовать что-то вроде:

Options[f] = {a->0, b->0};
f[args___] := (a+b) /. Flatten[{args, Options[f]}]

Для более сложного кода я бы, вероятно, использовал что-то вроде:

Options[f] = {a->0, b->0};
f[OptionsPattern[]] := Block[{a,b}, {a,b} = OptionValue[{a,b}]; a+b]

и использовал бы один вызов OptionValue, чтобы получить всезначения сразу.(Основная причина в том, что это сокращает количество сообщений, если присутствуют неизвестные параметры.)

Обновление для программного генерирования переменных из списка параметров:

Options[f] = {a -> 0, b -> 0};
f[OptionsPattern[]] := 
  With[{names = Options[f][[All, 1]]}, 
    Block[names, names = OptionValue[names]; a + b]]
4 голосов
/ 13 января 2011

Вот окончательная версия моего ответа, содержащая вклады от ответа Бретта Чемпиона.

ClearAll[def];
SetAttributes[def, HoldAll];
def[lhs : f_[args___] :> rhs_] /; !FreeQ[Unevaluated[lhs], OptionsPattern] :=
   With[{optionNames = Options[f][[All, 1]]},
     lhs := Block[optionNames, optionNames = OptionValue[optionNames]; rhs]];
def[lhs : f_[args___] :> rhs_] := lhs := rhs;

Причина, по которой определение приводится в качестве отложенного правила в аргументе, заключается в том, что таким образом мы можем извлечь выгоду из подсветки синтаксиса.Блочный трюк используется потому, что он подходит к проблеме: он не мешает возможным вложенным лексическим конструкциям внутри вашей функции, и поэтому нет опасности непреднамеренного захвата переменных.Мы проверяем наличие OptionsPattern, так как этот код не будет корректным для определений без него, и мы хотим, чтобы def также работал в этом случае.Пример использования:

Clear[f, a, b, c, d];
Options[f] = {a -> c, b -> d};
(*The default values.*)
def[f[n_, OptionsPattern[]] :> (a + b)^n]

Теперь вы можете посмотреть определение:

Global`f
f[n$_,OptionsPattern[]]:=Block[{a,b},{a,b}=OptionValue[{a,b}];(a+b)^n$]

f[n_,m_]:=m+n

Options[f]={a->c,b->d}

Мы можем проверить это сейчас:

In[10]:= f[2]
Out[10]= (c+d)^2

In[11]:= f[2,a->e,b->q]
Out[11]= (e+q)^2

Изменения сделаны на"время компиляции" и довольно прозрачно.В то время как это решение экономит некоторую типизацию по Бретту, оно определяет набор имен опций во время компиляции, в то время как у Бретта - во время исполнения.Следовательно, он немного более хрупок, чем у Бретта: если вы добавите новую функцию в функцию после того, как она была определена с помощью def, вы должны очистить ее и повторно запустить def.Однако на практике принято начинать с ClearAll и помещать все определения в один фрагмент (ячейку), поэтому это не представляется реальной проблемой.Кроме того, он не может работать с именами строковых опций, но ваша оригинальная концепция также предполагает, что они являются символами.Кроме того, они не должны иметь глобальных значений, по крайней мере, во время выполнения def.

0 голосов
/ 13 января 2011

Вот своего рода ужасное решение:

Options[f] = {a->0, b->0};
f[OptionsPattern[]] := Module[{vars, tmp, ret},
  vars = Options[f][[All,1]];
  tmp = cat[vars];
  each[{var_, val_}, Transpose[{vars, OptionValue[Automatic,#]& /@ vars}],
    var = val];
  ret = 
    a + b;  (* finally! *)
  eval["ClearAll[", StringTake[tmp, {2,-2}], "]"];
  ret]

Используются следующие удобные функции:

cat = StringJoin@@(ToString/@{##})&;        (* Like sprintf/strout in C/C++.  *)
eval = ToExpression[cat[##]]&;              (* Like eval in every other lang. *)
SetAttributes[each, HoldAll];               (* each[pattern, list, body]      *)
each[pat_, lst_, bod_] := ReleaseHold[      (*  converts pattern to body for  *)
  Hold[Cases[Evaluate@lst, pat:>bod];]];    (*   each element of list.        *)

Обратите внимание, что это не работает, если a или b имеет глобальное значение при вызове функции. Но это всегда было так для именованных аргументов в Mathematica.

...