Следующее является немного расточительным, но оно работает: Идея состоит в том, чтобы просто отслеживать случаи, когда атрибут Listable
в Plus
поместил одинаковый pt
во все элементы списка (т.е. необработанную точку ) - а затем вытащите его обратно. Сначала определим функцию для добавления объектов pt:
SetAttributes[ptPlus, {Orderless}]
ptPlus[pt[pa : {_, _}], pt[pb : {_, _}], r___] :=
ptPlus[pt[pa + pb], r];
ptPlus[p_pt] := p;
Затем мы удостоверяемся, что любой Plus
, который включает pt
, сопоставлен с ptPlus
(связывает правило с pt).
Plus[h___, a_pt, t___] ^:= ptPlus[h, a, t];
Вышеуказанные правила означают, что: {x0,y0}+pt[{x1,y1}]
будет расширен с {x0+pt[{x1,y1}],y0+pt[{x1,y1}]}
до {ptPlus[x0,pt[{x1,y1}]],ptPlus[y0,pt[{x1,y1}]]}
. Теперь мы просто создаем правило для преобразования этого в pt[{x0,y0}]+pt[{x1,y1}]
(обратите внимание на отложенное условие, которое проверяет, что pt
s равны):
{ptPlus[x__], ptPlus[y__]} ^:= Module[{
ptCases = Cases[{{x}, {y}}, _pt, {2}]},
ptCases[[1]] + pt[Plus @@@ DeleteCases[{{x}, {y}}, _pt, {2}]]
/; Equal @@ ptCases]
Более непрозрачный, но немного более осторожный вариант, который легче обобщить на более высокие измерения:
ptPlus /: p : {_ptPlus, _ptPlus} := Module[{ptCases, rest,
lp = ReleaseHold@Apply[List, Hold[p], {2}]},
ptCases = Cases[lp, _pt, {2}];
rest = Plus @@@ DeleteCases[lp, _pt, {2}];
ptCases[[1]] + pt[rest] /; And[Equal @@ ptCases, VectorQ@rest]]
Весь этот подход, конечно, приведет к ужасно тонким ошибкам, когда {a+pt[{0,0}],a+pt[{0,b}]} /. {a -> pt[{0,0}]}
оценивается как pt[{0,0}]
, когда c==0
и {pt[{0,0}],pt[{0,c}]}
в противном случае ...
HTH - сказал парень про себя ...