2

For example, I have defined a bunch of binary functions Sum1, Sum2, Prod1, Prod2, ... I want to make these functions associative, that is, the expression Sum1[a,b,c] will be evaluated as Sum1[Sum1[a,b],c]. I use the method here to achieve it:

Sum1[a_,b_,c__]:=Sum1[Sum1[a,b],c]

Now I want to do this to all of these binary functions without repeating it line by line (because there are too many of them). Is there a way to do it? I tried to do the following

Do[f[a_,b_,c__]:=f[f[a,b],c],{f,{Sum1,Sum2,Prod1,Prod2}}]

But after this, when I evaluate Sum1[a,b,c], it will return f[f[a,b],c]! In other words, the f after the := is not substituted by the instances from the list {Sum1,Sum2,Prod1,Prod2}.

Yifeng Huang
  • 123
  • 4
  • 2
    Do[With[{f=f}, f[a_,b_,c__]:=f[f[a,b],c]],{f,{Sum1,Sum2,Prod1,Prod2}}] – I.M. Jun 16 '22 at 04:25
  • @I.M. That works, but generally I think that if you feel like you need to use With[{f = f}, ...] you really should be using a Function and some mapping construct instead. – Sjoerd Smit Jun 16 '22 at 08:53

1 Answers1

4

Try this:

install[f_Symbol]:=(f[a_,b_,c__]:=f[f[a,b],c];);
Scan[install,{Sum1,Sum2,Prod1,Prod2}];

You may also want to have a look at Flat and OneIdentity, see here, but using them can be tricky.

user293787
  • 11,833
  • 10
  • 28
  • I have looked at Flat, but I am always confused about one thing - If f is flat, all it does is that f[f[a,b],c] will be converted to f[a,b,c]; I guess it is intended to be used for pattern matching and working with associative products of formal variables. However, if f is a concrete function (rather than a formal associative product), then Flat doesn't instruct f[a,b,c] to be evaluated as f[f[a,b],c]. – Yifeng Huang Jun 16 '22 at 13:34
  • What precisely do you mean by a concrete function? For instance SetAttributes[f,{Flat,OneIdentity}]; f[a_,b_]:=a+b; f[3,4,5] does give 12. – user293787 Jun 16 '22 at 13:47
  • Oh I see, the issue seems deeper - This example above is fine, but mine is not, probably due to the fact that it is an operator on pure functions. Let's say the objects are infinite sequences a[n] (implemented as a pure function a) and we want to define the termwise sum f[a,b]:=a[#1]+b[#1]&; SetAttributes[f,{Flat,OneIdentity}]. Then f[a,b][3]' givesa[3]+b[3]butf[a,b,c][3]just givesf[a,b,c][3]instead of the intendeda[3]+b[3]+c[3]`. – Yifeng Huang Jun 16 '22 at 14:06
  • So f[a_,b_]:=(a[#]+b[#]&); SetAttributes[f,{Flat,OneIdentity}]; f[a,b,c][3] gives a[3]+b[3]+c[3]. You may have missed some underscores in the definition of f? – user293787 Jun 16 '22 at 14:16
  • Oops I forgot to input the underscore here but I did in my original test...I find that f[a_,b_]:=(a[#]+b[#]&) works but f[a_,b_]:=a[#]+b[#]& doesn't - I am very confused now because this parenthesis is not supposed to make a difference, right? – Yifeng Huang Jun 16 '22 at 14:40
  • 1
    After some retest, I find that the real problem seems to be that I need to set attributes before defining the function. The parenthesis doesn't matter. I need to run f[a_,b_]:=(a[#]+b[#]&); SetAttributes[f,{Flat,OneIdentity}] twice before it to work. Alternatively, if I do SetAttributes[f,{Flat,OneIdentity}]; f[a_,b_]:=(a[#]+b[#]&), then once is enough. – Yifeng Huang Jun 16 '22 at 14:49