Добавление функций

Итак, если у вас есть две функции f, g: X → Y, а если существует некоторая двоичная операция +, определенная на Y, то f + g имеет каноническое определение как функцию x → f ( x) + g (x).

Какой лучший способ реализовать это в Mathematica?

f[x_] := x^2
g[x_] := 2*x
h = f + g;
h[1]

дает

(f + g)[1]

в качестве выходного

конечно,

H = Function[z, f[z] + g[z]];
H[1]

Урожайность '3'.

Ответ 1

Рассмотрим:

In[1]:= Through[(f + g)[1]]

Out[1]= f[1] + g[1]

Чтобы уточнить, вы можете определить h следующим образом:

h = Through[ (f + g)[#] ] &;

Если у вас ограниченное количество функций и операндов, то UpSet в соответствии с рекомендациями yoda, безусловно, синтаксически чище. Однако Through является более общим. Без каких-либо новых определений с участием Times или h можно легко сделать:

i = Through[ (h * f * g)[#] ] &
i[7]
43218

Ответ 2

Другой способ сделать то, что вы пытаетесь сделать, это использовать UpSetDelayed.

f[x_] := x^2;
g[x_] := 2*x;

f + g ^:= f[#] + g[#] &; (*define upvalues for the operation f+g*)

h[x_] = f + g;

h[z]

Out[1]= 2 z + z^2

Также см. этот очень приятный ответ от rcollyer (а также от Леонида и Вербеи) для более UpValues и когда использовать их

Ответ 3

Я напишу полный код для Gram-Schmidt и пример добавления функции и т.д., так как у меня был этот код, написанный около 4 лет назад. Однако не проверял широко. Теперь я не изменил ни одной строки, поэтому отказ от ответственности (в то время я был намного хуже на mma). Тем не менее, здесь представлена ​​реализация процедуры Gram-Schmidt, которая представляет собой слегка обобщенную версию кода, который я обсуждал здесь здесь:

oneStepOrtogonalizeGen[vec_, {}, _, _, _] := vec;

oneStepOrtogonalizeGen[vec_, vecmat_List, dotF_, plusF_, timesF_] := 
    Fold[plusF[#1, timesF[-dotF[vec, #2]/dotF[#2, #2], #2]] &, vec,  vecmat];

GSOrthogonalizeGen[startvecs_List, dotF_, plusF_, timesF_] := 
    Fold[Append[#1,oneStepOrtogonalizeGen[#2, #1, dotF, plusF, timesF]] &, {},  startvecs];

normalizeGen[vec_, dotF_, timesF_] := timesF[1/Sqrt[dotF[vec, vec]], vec];

GSOrthoNormalizeGen[startvecs_List, dotF_, plusF_, timesF_] := 
  Map[normalizeGen[#, dotF, timesF] &, GSOrthogonalizeGen[startvecs, dotF, plusF, timesF]];

Вышеуказанные функции параметризуются тремя функциями, реализующими сложение, умножение на число и произведение точек в данном векторном пространстве. Примером для иллюстрации будет найти полиномы Hermite ортонормирующими одночленами. Это возможные реализации для трех функций, которые нам нужны:

hermiteDot[f_Function, g_Function] := 
   Module[{x}, Integrate[f[x]*g[x]*Exp[-x^2], {x, -Infinity, Infinity}]];

SetAttributes[functionPlus, {Flat, Orderless, OneIdentity}];
functionPlus[f__Function] :=   With[{expr = Plus @@ Through[{f}[#]]}, expr &];

SetAttributes[functionTimes, {Flat, Orderless, OneIdentity}];
functionTimes[a___, f_Function] /; FreeQ[{a}, # | Function] := 
      With[{expr = Times[a, f[#]]}, expr &];

Эти функции могут быть немного наивными, но они проиллюстрируют идею (и да, я также использовал Through). Вот несколько примеров, иллюстрирующих их использование:

In[114]:= hermiteDot[#^2 &, #^4 &]
Out[114]= (15 Sqrt[\[Pi]])/8

In[107]:= functionPlus[# &, #^2 &, Sin[#] &]
Out[107]= Sin[#1] + #1 + #1^2 &

In[111]:= functionTimes[z, #^2 &, x, 5]
Out[111]= 5 x z #1^2 &

Теперь основное испытание:

In[115]:= 
results = 
  GSOrthoNormalizeGen[{1 &, # &, #^2 &, #^3 &, #^4 &}, hermiteDot, 
      functionPlus, functionTimes]

Out[115]= {1/\[Pi]^(1/4) &, (Sqrt[2] #1)/\[Pi]^(1/4) &, (
  Sqrt[2] (-(1/2) + #1^2))/\[Pi]^(1/4) &, (2 (-((3 #1)/2) + #1^3))/(
  Sqrt[3] \[Pi]^(1/4)) &, (Sqrt[2/3] (-(3/4) + #1^4 - 
  3 (-(1/2) + #1^2)))/\[Pi]^(1/4) &}

Это действительно правильно нормированные полиномы Эрмита, как легко проверить. Нормализация встроенного HermiteH отличается. Наши результаты нормализуются, как, например, нормализуют волновые функции гармонического осциллятора. Тривиально получить список полиномов в виде выражений, зависящих от переменной, например x:

In[116]:= Through[results[x]]
Out[116]= {1/\[Pi]^(1/4),(Sqrt[2] x)/\[Pi]^(1/4),(Sqrt[2] (-(1/2)+x^2))/\[Pi]^(1/4),
(2 (-((3 x)/2)+x^3))/(Sqrt[3] \[Pi]^(1/4)),(Sqrt[2/3] (-(3/4)+x^4-3 (-(1/2)+x^2)))/\[Pi]^(1/4)}

Ответ 4

Я бы предложил определить для этой цели оператор, отличный от встроенного Plus. Существует ряд операторов, предоставляемых Mathematica, которые зарезервированы для пользовательских определений в таких случаях. Одним из таких операторов является CirclePlus, который не имеет заранее определенного значения, но имеет приятное компактное представление (по крайней мере, компактное в ноутбуке - не столь компактное на веб-странице StackOverflow). Вы можете определить CirclePlus для выполнения добавления функции, таким образом:

(x_ \[CirclePlus] y_)[args___] := x[args] + y[args]

С помощью этого определения вы можете теперь выполнить добавление функции:

h = f \[CirclePlus] g;
h[x]
(* Out[3]= f[x]+g[x] *)

Если кому-то нравится жить на краю, тот же метод можно использовать со встроенным оператором Plus, если он не защищен первым:

Unprotect[Plus];
(x_ + y_)[args___] := x[args] + y[args]
Protect[Plus];

h = f + g;
h[x]
(* Out[7]= f[x]+g[x] *)

В целом я бы посоветовал не изменять поведение встроенных функций, особенно фундаментальных, как Plus. Причина в том, что нет никакой гарантии, что добавленные пользователем определения в Plus будут соблюдаться другими встроенными или функциями ядра. В некоторых случаях запросы на Plus оптимизируются, и эти оптимизации могут не учитывать учетные данные пользователя. Тем не менее, это соображение может не повлиять на какое-либо конкретное приложение, поэтому этот вариант по-прежнему является допустимым, если не рискованным, выбором дизайна.