Можно ли определить функцию, которая содержит аргументы в заданных позициях?
Или делать что-то вроде HoldLast как аналог HoldFirst?
Можно ли определить функцию, которая содержит аргументы в заданных позициях?
Или делать что-то вроде HoldLast как аналог HoldFirst?
Насколько я знаю, вы не можете сделать это напрямую в том смысле, что атрибут HoldN
отсутствует.
Тем не менее, ниже есть работа, которая должна выполнять то, что вы просили.
Один простой способ - определить вспомогательную функцию, которая будет выполнять основную работу, и вашу "основную" функцию (ту, которая на самом деле будет вызвана) как HoldAll
, например:
In[437]:=
SetAttributes[f, HoldAll];
f[a_, b_, c_] :=
faux[a, Unevaluated[b], c];
faux[a_, b_, c_] := Hold[a, b, c]
In[440]:= f[1^2, 2^2, 3^2]
Out[440]= Hold[1, 2^2, 9]
Вам не нужно выставлять faux
на верхний уровень, вместо этого можно привязать его к Module[{faux}, your definitions]
.
Эта процедура может быть автоматизирована. Вот упрощенный синтаксический анализ для сигнатур функций, для извлечения имен патчей (примечание - это действительно упрощенно):
splitHeldSequence[Hold[seq___], f_: Hold] := List @@ Map[f, Hold[seq]];
getFunArguments[Verbatim[HoldPattern][Verbatim[Condition][f_[args___], test_]]] :=
getFunArguments[HoldPattern[f[args]]];
getFunArguments[Verbatim[HoldPattern][f_[args___]]] :=
FunArguments[FName[f], FArgs @@ splitHeldSequence[Hold[args]]];
(*This is a simplistic "parser".It may miss some less trivial cases*)
getArgumentNames[args__FArgs] :=
args //. {
Verbatim[Pattern][tag_, ___] :> tag,
Verbatim[Condition][z_, _] :> z,
Verbatim[PatternTest][z_, _] :> z
};
Используя это, мы можем написать следующий пользовательский оператор определения:
ClearAll[defHoldN];
SetAttributes[defHoldN, HoldFirst];
defHoldN[SetDelayed[f_[args___], rhs_], n_Integer] :=
Module[{faux},
SetAttributes[f, HoldAll];
With[{heldArgs =
MapAt[
Unevaluated,
Join @@ getArgumentNames[getFunArguments[HoldPattern[f[args]]][[2]]],
n]
},
SetDelayed @@ Hold[f[args], faux @@ heldArgs];
faux[args] := rhs]]
Это проанализирует ваше исходное определение, извлеките имена паттернов, оберните интересующий аргумент в Unevaluated
, представите локальный faux
и сделайте двухэтапное определение - в основном шаги, которые мы сделали вручную. Нам нужно SetDelayed @@ ..
обмануть механизм переименования переменных With
, чтобы он не переименовал наши переменные шаблона на l.h.s. Пример:
In[462]:=
ClearAll[ff];
defHoldN[ff[x_,y_,z_]:=Hold[x,y,z],2]
In[464]:= ?ff
Global`ff
Attributes[ff]={HoldAll}
ff[x_,y_,z_]:[email protected]@Hold[x,Unevaluated[y],z]
In[465]:= ff[1^2,2^2,3^2]
Out[465]= Hold[1,2^2,9]
Обратите внимание, что это тривиально обобщать на список позиций, в которых вам нужно удерживать аргументы. В общем, вам нужен лучший анализатор паттернов, но простой пример выше может быть хорошим началом. Обратите также внимание на то, что на этой конструкции будет немного времени выполнения, а также, что Module
-генерированные вспомогательные функции faux
не будут собирать мусор, если вы Clear
или Remove
основной - вам может понадобиться ввести специальный деструктор для ваших функций, сгенерированных с помощью defHoldN
. Для альтернативного решения этой проблемы см. Мой пост в этот поток (тот, где я ввел функцию makeHoldN
).
Другой способ сделать это будет, например:
SetAttributes[f,HoldFirst];
f[{heldArg1_,heldArg2_},arg3_,arg4_,arg5_]:= Hold[arg3 heldArg1];
И это позволило бы иметь любое сочетание удерживаемых и нераспространенных аргументов. HoldRest можно использовать аналогичным образом.