14
$\begingroup$

In this answer, what does the : Hold[$IterationLimit] part of the following construct do?

cfRemainders[x_, iter_: Hold[$IterationLimit]]
$\endgroup$

3 Answers 3

7
$\begingroup$

The original complete definition is

cfRemainders[x_, iter_: Hold[$IterationLimit]] := 
 NestWhileList[FractionalPart[1/#] &, FractionalPart[x], # != 0 &, 1, ReleaseHold[iter]]

The iter_ : Hold[$IterationLimit] makes iter an Optional argument with the default value Hold[$IterationLimit] if the argument is omitted.

Secondly, by using Hold, $IterationLimit is not evaluated until cfRemainders is actually called and ReleaseHold[iter] is executed. So if a user resets $IterationLimit, subsequent calls to cfRemainders will respect it.


One question might be why did I do it this way. (My feeling is that I'm overlooking something and someone will suggest a better way to limit the number of times an iteration occurs.)

Without some limit on NestWhileList, calling cfRemainders on an irrational number would run forever (or until Mma ran out of memory). $IterationLimit seemed like a convenient system variable to use ($RecursionLimit is another and one can argue which seems more appropriate). But without Hold in the definition

cfRemainders[x_, iter_: $IterationLimit] := ...

the current value of $IterationLimit is substituted and the definition is equivalent to (assuming $IterationLimit == 4096)

cfRemainders[x_, iter_: 4096] := ...

(You can check with ? cfRemainders.) In this case, if a user changes $IterationLimit, cfRemainders is unaffected.

Alternative definition could be to set my own arbitrary limit or make the argument iter mandatory:

cfRemainders[x_, iter_: 1000] := ...
cfRemainders[x_, iter_] := ...

Another good question is why worry about this, when I neglected to put a restriction on x to be numeric, which would be helpful, too:

cfRemainders[x_?NumericQ, iter_: Hold[$IterationLimit]] := ...

In fact, I think I'll go edit my other answer.

$\endgroup$
1
  • $\begingroup$ I think your method is quite reasonable, but if you seek another you could also do something like: f[x_, iter_: -1] := [. . ., iter /. -1 :> $IterationLimit] Or, you could you Options. $\endgroup$
    – Mr.Wizard
    May 5, 2013 at 16:25
4
$\begingroup$

What this does is set a default value for iter, meant for use if cfRemainders is called with only one argument. The default value for iter in this case is $IterationLimit, and the Hold[] enclosing it means cfRemainders will use $IterationLimit symbol for the new rule. If there was no enclosing Hold[], $IterationLimit would have been replaced with the Integer value assciated with it, in the new rule defined for cfRemainders. This makes cfRemainders more robust\general because, upon calling it, it'll always iterate the appropriate number of times, even if $IterationLimit has been changed. Check the differences with DownValues[cfRemainders], with/without Hold[].

$\endgroup$
4
$\begingroup$

Actually this is more like a comment to Michael E.'s answer than an own answer, but it became too long for a comment. I think it is worth mentioning that $IterationLimit (and also $RecursionLimit and probably some others as well) is somewhat special and thus needs special treatment: For a "normal" variable it would be quite simple to achieve what Michael wants with something like:

 $iterationlimit = 4096;
     Block[{$iterationlimit},cfRemainder[x_, iter_: $iterationlimit] := Print[iter];]

Now if you look at DownValues[cfRemainder] or just use the followings lines of code you find that it will behave as wanted even without the extra Hold and ReleaseHold:

DownValues[cfRemainders]

(*
==> {HoldPattern[cfRemainders[x_, iter_ : $iterationlimit]] :> 
  Print[iter]}
*)

cfRemainders[10]
Block[{$IterationLimit = 20}, cfRemainders[10]]

For some deeper reason out of my knowledge that doesn't work for $IterationLimit as it seems to not behave as a normal variable within Block: it will always have that OwnValue even if it is Blocked, presumably this is to build an extra barrier against infinite iterations (which would lead to a crash):

Block[{$IterationLimit},
      cfRemainders[x_, iter_: $IterationLimit] := Print[ReleaseHold[iter]];
  ];
DownValues[cfRemainders]

(*
==> {HoldPattern[cfRemainders[x_, iter_ : 4096]] :> 
  Print[ReleaseHold[iter]]}
*)

There are possibilities to overcome that situation, the trick Micheal used with using an extra Hold and ReleaseHold is one, here are two ways which avoid the extra ReleaseHold but need some more complicated definitions:

Variant 1:

ClearAll[cfRemainders];
Module[{marker},
  cfRemainders[x_, iter_: marker] := Print[iter];
  DownValues[cfRemainders] = 
    ReplaceAll[DownValues[cfRemainders], marker :> $IterationLimit];

];

Variant 2:

ClearAll[cfRemainders];
Block[{$IterationLimit},
       OwnValues[$IterationLimit] = {};
   cfRemainders[x_, iter_: $IterationLimit] := Print[iter];
]

By looking at the DownValues[cfRemainders] you can check that both these variants will put an unevaluated $IterationLimit into the definition and will work as expected. While I think that the second variant is somewhat more elegant I have the feeling that it might undermine the extra barriers against inifinite recursions and might be dangerous in certain circumstances. So I'd probably go with version 1 which I think should be safe. I'm also quite sure that there are even better ways to put that unevaluated $IterationLimit into the optional part of such a function definition that I couldn't make up...

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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