7

I'm trying to define a function f, such that

f[a_]:=a;

f[f[a__]]:=f[a];

Then, I tried to evaluate f[x,y]. Since f[x,y] satisfies none of these two patterns, it is expected that the result will be just f[x,y]. Nevertheless, in fact, the result is

Hold[f(a,b)]

and I get a error message:

$IterationLimit::itlim: Iteration limit of 4096 exceeded. >>

It seems quite confusing. How could this happen?

J. M.'s missing motivation
  • 124,525
  • 11
  • 401
  • 574
Wen Chern
  • 716
  • 4
  • 8
  • Greetings! To make the most of Mma.SE please take the [tour] now. Help us to help you, write an excellent question. Edit if improvable, show due diligence, give brief context, include minimum working examples of code and data in formatted form. As you receive give back, vote and answer questions, keep the site useful, be kind, correct mistakes and share what you have learned. – rhermans Nov 02 '15 at 14:31
  • 4
    You're looking for the Flat attribute. – rcollyer Nov 02 '15 at 14:33
  • 1
    Also, take a look at ?f. Another way to achieve the desired result would be f[Hold@f[a__]]:=f[a];. – Graumagier Nov 02 '15 at 14:40
  • @rcollyer,@Graumagier.Thanks for all of your help. I changed my code by replacing the second line by SetAttributes[f,Flat]. However, the problem remained. – Wen Chern Nov 02 '15 at 15:20
  • I'm not really sure Flat does what you want to do. Did you try f[Hold@f[a__]]:=f[a];? Also take care to clear all definitions/restart the kernel. – Graumagier Nov 02 '15 at 15:24
  • @ Graumagier, I tried this method, the problem is solved. However, I couldn't get the desired result--Flatting the function. – Wen Chern Nov 02 '15 at 15:32

4 Answers4

6

Note the following

a=3;
g[a]=2;
g//Definition

g[3]=2

We see that the definition g[3]=2 was stored, rather than g[a]=2. The argument of g, which is a, is evaluated before the definition is made.

The same happens in your code. f[a__] evaluates to a__ before the definition is made.

f//Definition
f[a_]:=a
f[a__]:=f[a]

I like the following solution

f4[a_] := a;
f4[HoldPattern@f4[a__]] := f4[a];

Another solution relies on using Unevaluated in a strange and AFAIK undocumented way, like this

f2[a_] := a;
f2[Unevaluated@f2[a__]] := f2[a];
f2 // Definition
f2[f2[a__]]:=f2[a] 
f2[a_]:=a

I kind of like this too, because it allows you to make the definition you want to make, without HoldPattern.

Jacob Akkerboom
  • 12,215
  • 45
  • 79
  • I have quited the kernel before runing these two lines of codes. How could the arguments be evaluated? – Wen Chern Nov 02 '15 at 15:27
  • @WenChern it is just what Mathematica does. I suppose the case of the example of g was the main case that was focused on in making this decision. – Jacob Akkerboom Nov 02 '15 at 15:33
  • The problem arises if you evaluate f[a_]=a prior to f[f[a__]]=f[a], because first f[a_] gets "replaced" with a, and then f[f[a__]]=f[a] evaluates to f[a__]=f[a]. – Graumagier Nov 02 '15 at 15:33
  • @Graumagier yes, this is essentially Chris Degnens solution. My own solution is something that may be nice to see for more experienced users. I suppose it also explicitly reminds you of the problem, which can be nice. – Jacob Akkerboom Nov 02 '15 at 15:36
  • Sure, your solution is definitely more robust. I just tried to explain the problem to Wen Chern, but your comment was put up first ;) – Graumagier Nov 02 '15 at 15:39
  • @Graumagier,@Jacob Akkerboom. Thank for your help. The problem is well solved. I think I get your points now. – Wen Chern Nov 02 '15 at 15:47
4

You can get the expected output by defining the functions in reverse order.

f[f[a__]] := f[a]
f[a_] := a

f[x, y]

f[x, y]

Chris Degnen
  • 30,927
  • 2
  • 54
  • 108
  • Thank you very much. Though I don't know how this method works, it does solve the problem. – Wen Chern Nov 02 '15 at 15:37
  • @WenChern it changes the order in which the patterns are tested. Since f[f[a]] matches both f[a_] and f[f[a__]], you need to change the order they're stored in (cf. DownValues, and the answers). This is most easily effected by changing the order they're declared in. Note, however, there is a notion of specificity, where more specific patterns are tested before more general ones. In this case, though, they're equally general. – rcollyer Nov 02 '15 at 16:14
  • 1
    @rcollyer unless I am mistaken, this is not what is going on. The pattern f[f[a__]] is more specific than f[a_], so in this sense the order should not matter. In the example f7[a_] := a; f7[{a__}] := f7[a]; the DownValues are automatically sorted for this reason. The reason for the trouble is that in the other order of evaluation, there is an unexpected evaluation of a pattern. Please see my answer. – Jacob Akkerboom Nov 02 '15 at 16:40
  • @JacobAkkerboom you're correct. I misread DownValues@f, completely missing the transformation. – rcollyer Nov 02 '15 at 16:48
  • 2
    @rcollyer to be honest this is something I could easily have missed while writing code myself. It is nice to be reminded of this danger IMO. – Jacob Akkerboom Nov 02 '15 at 17:10
  • @JacobAkkerboom yes it does. – rcollyer Nov 02 '15 at 17:11
3

Jacob gives a good exposition on different methods that work. But, to avoid any possibility of ambiguity, I would go with something very different

f[a_] := a
f[q_f] := q

which is correctly ordered

DownValues@f
(* {HoldPattern[f[q_f]] :> q, HoldPattern[f[a_]] :> a} *)
rcollyer
  • 33,976
  • 7
  • 92
  • 191
0

It looks like your double uderscore (BlankSequence) in the second definition matches things like x,y. For example:

f[a__] := 10*a;
f[3, 2, 4]
240
Mitchell Kaplan
  • 3,696
  • 22
  • 34
  • Sure it does, but by Wen Chern's intuition neither f[f[a__]] nor f[a_] should match f[x,y] (and they don't actually). Only in the above combination they do. – Graumagier Nov 02 '15 at 14:50
  • @Graumagier I guess I don't really understand what the desired result is. – Mitchell Kaplan Nov 02 '15 at 14:57
  • As I understand it the expression f[x,y] should be returned unevaluated because it is not of the form f[f[a__]] (a function inside a function). – Graumagier Nov 02 '15 at 14:58