In this answer, what does the : Hold[$IterationLimit]
part of the following construct do?
cfRemainders[x_, iter_: Hold[$IterationLimit]]
Mathematica Stack Exchange is a question and answer site for users of Wolfram Mathematica. It only takes a minute to sign up.
Sign up to join this communityIn this answer, what does the : Hold[$IterationLimit]
part of the following construct do?
cfRemainders[x_, iter_: Hold[$IterationLimit]]
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.
f[x_, iter_: -1] := [. . ., iter /. -1 :> $IterationLimit]
Or, you could you Options.
$\endgroup$
May 5, 2013 at 16:25
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[]
.
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 Block
ed, 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...