There are always several ways to do the same thing in Mathematica. For example, when adapting WReach’s solution for my recent problem I used Condition:
ClearAll[ff];
SetAttributes[ff, HoldAllComplete];
ff[expr_] /; (Unset[done]; True) :=
Internal`WithLocalSettings[Null, done = f[expr],
AbortProtect[If[! ValueQ[done], Print["Interrupt!"]]; Unset[done]]]
However, we can do the same thing with Block:
ClearAll[ff];
SetAttributes[ff, HoldAllComplete];
ff[expr_] :=
Block[{done},
Internal`WithLocalSettings[Null, done = f[expr],
AbortProtect[If[! ValueQ[done], Print["Interrupt!"]]]]]
Or with Module:
ClearAll[ff];
SetAttributes[ff, HoldAllComplete];
ff[expr_] :=
Module[{done},
Internal`WithLocalSettings[Null, done = f[expr],
AbortProtect[If[! ValueQ[done], Print["Interrupt!"]]]]]
Probably there are several other ways to do the same. Which way is the most efficient from the point of view of memory and CPU use (f may return very large arrays of data – but may return very small)?
Both
ModuleandBlockare quite efficient, so the overhead induced by them is only noticable when the body of a function whose variables you localize does very little. There are two major reasons for the overhead: scoping construct overhead (scoping constructs must analyze the code they enclose to resolve possible name conflicts and bind variables – this takes place for bothModuleandBlock), and the overhead of creation and destruction of new symbols in a symbol table (only forModule). For this reason,Blockis somewhat faster. To see how much faster, you can do a simple experiment:We defined here 4 functions, with the simplest body possible – just return the argument, possibly assigned to a local variable. We can expect the effect to be most pronounced here, since the body does very little.
From these timings, we see that
Blockis about twice faster thanModule, but that the version that uses persistent variable created byModulein the last function only once, is about twice more efficient thanBlock, and almost as fast as a simple function invokation (because persistent variable is only created once, and there is no scoping overhead when applying the function).For real functions, and most of the time, the overhead of either
ModuleorBlockshould not matter, so I’d use whatever is safer (usually,Module). If it does matter, one option is to use persistent local variables created by Module only once. If even this overhead is significant, I’d reconsider the design – since then obviously your function does too little.There are cases whenBlockis more beneficial, for example when you want to be sure that all the memory used by local variables will be automatically released (this is particularly relevant for local variables withDownValues, since they are not always garbage – collected when created byModule). Another reason to useBlockis when you expect a possibility of interrupts such as exceptions or aborts, and want the local variables to automatically be reset (whichBlockdoes). By usingBlock, however, you risk name collisions, since it binds variables dynamically rather than lexically.So, to summarize: in most cases, my suggestion is this: if you feel that your function has serious memory or run-time inefficiency, look elsewhere – it is very rare for scoping constructs to be the major bottleneck. Exceptions would include not garbage-collected
Modulevariables with accumulated data, very light-weight functions used very frequently, and functions which operate on very efficient low-level structures such as packed arrays and sparse arrays, where symbolic scoping overhead may be comparable to the time it takes a function to process its data, since the body is very efficient and uses fast functions that by-pass the main evaluator.EDIT
By combining
BlockandModulein the fashion suggested here:you can have the best of both worlds: a function as fast as
Block– scoped one and as safe as the one that usesModule.