Подтвердить что ты не робот

Держать любые аргументы

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

Или делать что-то вроде HoldLast как аналог HoldFirst?

4b9b3361

Ответ 1

Насколько я знаю, вы не можете сделать это напрямую в том смысле, что атрибут 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).

Ответ 2

Другой способ сделать это будет, например:

SetAttributes[f,HoldFirst];
f[{heldArg1_,heldArg2_},arg3_,arg4_,arg5_]:= Hold[arg3 heldArg1];

И это позволило бы иметь любое сочетание удерживаемых и нераспространенных аргументов. HoldRest можно использовать аналогичным образом.