I was recently introduced to the LetL
macro thanks to Leonid's answer to one of my prior questions. I was, needless to say, impressed by the simplicity of its recursive definition. However, I noticed that it may not necessarily be optimized. As it is defined, if my LetL
statement contains a definition which does not need to be nested, then it will call With
unnecessarily:
testLetL := LetL[{x = 1, y = 2, z = 2 x y}, {x, y, z}]
?testLetL
(* testLetL:=With[{x=1},With[{y=2},With[{z=2 x y},{x,y,z}]]] *)
So I compared it to Module
:
testModule := Module[{x = 1, y = 2, z}, z = 2 x y; {x, y, z}]
(Do[#, {i, 5000000}] // AbsoluteTiming) & /@ {testLetL, testModule}
(* {{0.9390537, Null}, {0.9270530, Null}} *)
As you can see, there doesn't seem to be much speed gained in using LetL
- essentially nested With
s - instead of Module
. I thought perhaps that it was the extra With
being called that was slowing things down. So I tried another test:
testLetL2 := LetL[{x = 1, y = 2 x }, {x, y}]
testModule2 := Module[{x = 1, y}, y = 2 x ; {x, y}]
(Do[#, {i, 5000000}] // AbsoluteTiming) & /@ {testLetL2, testModule2}
(* {{0.9270531, Null}, {0.9120521, Null}} *)
This again showed that they were pretty much the same, if not Module
being a bit faster.
My question is, then:
Is LetL
simply used for convenience or are my tests missing something?
Answer
The main point of LetL
is just replacement of nested With
, not necessarily the speed gain. Now, why would one want to use nested With
in place of Module
:
- Immutable code (same advantages as
With
- no side effects in the body) - Use variables defined earlier in definitions of variables defined later.
In fact, if you want the second property, you will either have to have nested Module
-s as well, or make side effects in the body.
That said, LetL
should be pretty fast. You should not normally see large timing difference between LetL
and equivalent nested With
. Moreover, for functions defined via SetDelayed
, LetL
expands into equivalent nested With
at definition-time, so there is no run-time performance penalty at all.
And yes, your tests missed the point, since pure function is #-&
notation evaluates its argument, so you were actually testing already evaluated expressions. Try this:
Do[testLetL2,{i,50000}]//AbsoluteTiming
Do[testModule2,{i,50000}]//AbsoluteTiming
(*
{0.190430,Null}
{0.276367,Null}
*)
Comments
Post a Comment