One trick I've used, which allows you to emulate the way most built-in functions work with bad arguments (by sending a message and then returning the whole form unevaluated) exploits a quirk of the way Condition works when used in a defintion. If foo
should only work with one argument:
foo[x_] := x + 1;
expr : foo[___] /; (Message[foo::argx, foo, Length@Unevaluated[expr], 1];
False) := Null; (* never reached *)
If you have more complex needs, it's easy to factor out the argument validation and message generation as an independent function. You can do more elaborate things by using side effects in Condition
beyond just generating messages, but in my opinion most of them fall into the "sleazy hack" category and should be avoided if possible.
Also, in the "metaprogramming" category, if you have a Mathematica package (.m
) file, you can use the "HeldExpressions" element to get all the expressions in the file wrapped in HoldComplete
. This makes tracking things down much easier than using text-based searches. Unfortunately, there's no easy way to do the same thing with a notebook, but you can get all the input expressions using something like the following:
inputExpressionsFromNotebookFile[nb_String] :=
Cases[Get[nb],
Cell[BoxData[boxes_], "Input", ___] :>
MakeExpression[StripBoxes[boxes], StandardForm],
Infinity]
Lastly, you can use the fact that Module
emulates lexical closures to create the equivalent of reference types. Here's a simple stack (which uses a variation the Condition
trick for error handling as a bonus):
ClearAll[MakeStack, StackInstance, EmptyQ, Pop, Push, Peek]
With[{emptyStack = Unique["empty"]},
Attributes[StackInstance] = HoldFirst;
MakeStack[] :=
Module[{backing = emptyStack},
StackInstance[backing]];
StackInstance::empty = "stack is empty";
EmptyQ[StackInstance[backing_]] := (backing === emptyStack);
HoldPattern[
Pop[instance : StackInstance[backing_]]] /;
! EmptyQ[instance] || (Message[StackInstance::empty]; False) :=
(backing = Last@backing; instance);
HoldPattern[Push[instance : StackInstance[backing_], new_]] :=
(backing = {new, backing}; instance);
HoldPattern[Peek[instance : StackInstance[backing_]]] /;
! EmptyQ[instance] || (Message[StackInstance::empty]; False) :=
First@backing]
Now you can print the elements of a list in reverse order in a needlessly convoluted way!
With[{stack = MakeStack[], list},
Do[Push[stack, elt], {elt, list}];
While[!EmptyQ[stack],
Print[Peek@stack];
Pop@stack]]