Необязательные именованные аргументы в Mathematica

Какой лучший/канонический способ определить функцию с необязательными именованными аргументами? Для конкретики создадим функцию foo с именованными аргументами a, b и c, которые по умолчанию равны 1, 2 и 3 соответственно. Для сравнения, вот версия foo с позиционными аргументами:

foo[a_:1, b_:2, c_:3] := bar[a,b,c]

Вот пример ввода и вывода для версии foo с именованными аргументами:

foo[]                  --> bar[1,2,3]
foo[b->7]              --> bar[1,7,3]
foo[a->6, b->7, c->8]  --> bar[6,7,8]

Конечно, также должно быть легко иметь позиционные аргументы перед именованными аргументами.


person dreeves    schedule 23.10.2009    source источник
comment
См. также: stackoverflow.com/questions/4682742/   -  person dreeves    schedule 15.01.2011


Ответы (3)


Я нашел стандартный способ сделать это в документации Mathematica: http://reference.wolfram.com/mathematica/tutorial/SettingUpFunctionsWithOptionalArguments.html

Options[foo] = {a->1, b->2, c->3};  (* defaults *)
foo[OptionsPattern[]] := bar[OptionValue@a, OptionValue@b, OptionValue@c]

Вводить «OptionValue» каждый раз немного громоздко. По какой-то причине вы не можете просто сделать глобальную аббревиатуру, например ov = OptionValue, но вы можете сделать это:

foo[OptionsPattern[]] := Module[{ov},
  ov[x___] := OptionValue[x];
  bar[ov@a, ov@b, ov@c]]

Или это:

With[{ov = OptionValue},
  foo[OptionsPattern[]] := bar[ov@a, ov@b, ov@c]
]

Или это:

$PreRead = ReplaceAll[#, "ov" -> "OptionValue"] &;

foo[OptionsPattern[]] := bar[ov@a, ov@b, ov@c]
person dreeves    schedule 23.10.2009
comment
О громоздком вводе OptionValue : в этом случае вы могли бы сказать In[32]:= OptionValue /@ bar[a, b, c] Out[32]= bar[OptionValue[a], OptionValue[b], OptionValue[c]] - person Sjoerd C. de Vries; 17.03.2011
comment
@dreeves Существует более краткая форма последнего блока кода, использующая With, а не Module. Могу ли я отредактировать ваш ответ, чтобы добавить это? - person Mr.Wizard; 17.03.2011
comment
@Sjoerd смешно, твоего комментария не было, когда я загрузил эту страницу. Думаю, вы тоже читаете эти старые посты. - person Mr.Wizard; 17.03.2011
comment
@Mr.Wizard Да, вызвано цепочкой вопросов Дривза. - person Sjoerd C. de Vries; 17.03.2011
comment
@Mr.Wizard Пожалуйста! (Я не думаю, что вам нужно спрашивать вообще; отменить легко, если исходному автору не нравится редактирование.) О, и не добавляйте, просто измените его, если он лучше. - person dreeves; 18.03.2011
comment
@dreeves Я знаю, что сайт разрешает это, но все же кажется вежливым спросить. Кстати, я не знаю, лучше ли это, просто другой стиль. - person Mr.Wizard; 18.03.2011
comment
@Mr.Wizard Вы уверены, что трюк PreRead безопасен? Что, если ov — просто часть другой строки или переопределена в блоке или что-то в этом роде? Кроме того, вы тестировали версию With? Есть что-то странное в OptionValue; confer my Почему-то нельзя.... - person dreeves; 24.03.2011
comment
Я тестировал их, но помните, что я использую Mma 7. Версия With работает, потому что замена выполняется до оценки. Версия $PreRead кажется безопасной, поскольку работает на уровне токенов. Попробуйте оценить: {7 "ov", 2 + ov, "stove", rover} - person Mr.Wizard; 24.03.2011

Да, OptionValue может быть немного сложнее, потому что он полагается на волшебство, так что

OptionValue[name] эквивалентно OptionValue[f,name], где f — это заголовок левой части правила преобразования, в котором появляется OptionValue[name].

Добавление явного Automatic обычно помогает, поэтому в вашем случае я бы сказал, что решение:

Options[foo] = {a -> 1, b -> 2, c -> 3};
foo[OptionsPattern[]] := 
  bar @@ (OptionValue[Automatic, #] &) /@ First /@ Options[foo] 

Кстати, раньше опции выполнялись путем сопоставления с opts:___?OptionQ, а затем вручную находили значения опций как {a,b,c}/.Flatten[{opts}]. Проверка шаблона OptionQ все еще используется (хотя и не задокументирована), но подход OptionValue имеет то преимущество, что вы получаете предупреждения о несуществующих параметрах (например, foo[d->3]). Это также относится и к вашему второму ответу, но не к тому, который вы приняли.

person Janus    schedule 19.02.2010

Я добавлю это возможное решение в смесь:

foo[opts___Rule] := Module[{f},
  f@a = 1; (* defaults... *)
  f@b = 2;
  f@c = 3;
  each[a_->v_, {opts}, f@a = v];

  Return[bar[f@a, f@b, f@c]]
]

Мне нравится его краткость, но я не думаю, что это стандартный способ. Любые ошибки, связанные с этим?

PS, он использует следующую удобную служебную функцию:

SetAttributes[each, HoldAll];                (* each[pattern, list, body]     *)
each[pat_, lst_, bod_] :=                    (*  converts pattern to body for *)
  Scan[Replace[#, pat:>bod]&, Evaluate@lst]  (*   each element of list.       *)
person dreeves    schedule 23.10.2009