5
$\begingroup$

Maybe this is a bad idea: I want to inject some conditions into Set associated to specfic symbols, like

clear@{f,g}
f/:Set[f[x_],y_]:=
    If[
        !IntegerQ@x,
        Print["non-integer"],
        (*pre-codes;*)
        f[x]=y;
        (*post-codes;*)
    ]

but this try fails:

Set[f[1],2]
(*
$IterationLimit::itlim: Iteration limit of 4096 exceeded.
Hold[f[1]=2]
*)

I also tried (but failed) adding some pre-codes and post-codes (like ValueQ) to escape from the infinite loop, except trivially changing Set to some other symbol like,

fSet[f[x_],y_]:=
    If[
        !IntegerQ@x,
        Print["non-integer"],
        f[x]=y;
    ]

Are there other ways to solve this problem, or should I abandon this idea?

$\endgroup$

3 Answers 3

9
$\begingroup$

There's a standard trick for dealing with this problem! It involves setting up a so-called intercept, and is a general way to avoid using the same definition you're creating within the definition:

setInterceptf = True;
Protect[setInterceptf];

f /: Set[f[x_], y_] /; setInterceptf :=
  Block[{setInterceptf = False},
    If[!IntegerQ@x, Print["non-integer"], f[x] = y]
   ]

This way, the definition applies by default (since setInterceptf is globally True)—but inside the definition, the external definition won't get used, since setInterceptf will be False.

You'll have to have an extra global symbol here, but we Protected it just in case, so it shouldn't be much of an issue.

$\endgroup$
1
  • 1
    $\begingroup$ This is clearly a very flexible approach! $\endgroup$
    – user293787
    Jul 29 at 4:40
3
$\begingroup$

Instead of f[x]=y one could try

   AppendTo[DownValues[f],HoldPattern[f[x]]:>y];

But as OP correctly points out, manual management of DownValues has disadvantages.

$\endgroup$
2
  • $\begingroup$ Thx! But it seems that using this method we also need to further modify the list DownValues. For example, after f[1]=a;f[1]=b two downvalues will be kept, and we need to care about the order. $\endgroup$
    – lilyric
    Jul 29 at 4:48
  • $\begingroup$ I agree. Manual management of DownValues is possible but probably not a good idea in general. The solution using an intercept is certainly the way to go. $\endgroup$
    – user293787
    Jul 29 at 4:55
2
$\begingroup$

There is no need for any hocus locus here, just make the condition part of the pattern:

f /: Set[f[x_], y_] /; !IntegerQ[x] := (
    Print @ "not an integer";
    $Failed (* or nothing here and Print returns Null *)
)

It works as expected:

In[28]:= f[2] = 3

Out[28]= 3

In[29]:= f[2.2] = 5

During evaluation of In[29]:= not an integer

Out[29]= $Failed
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.