开发者

Fast way to type check Symbol in a function with held arguments

One can test if an argument is a Symbol without a explicit value using:

func[s_Symbol] = ...

If the function has a Hold attribute however, that pattern will match all Symbols, not only those without an explicit value. I could use:

func[s_] /; Head[s] === Symbol = ...

but this comes with a greater performance penalty than I would like. Adding a rule for _Symbol has a fairly small impact on开发者_如何学Python performance, and HoldFirst does not appear to have any performance penalty, however Head[s] === Symbol has a significant overhead on a simple function. Tests with ValueQ and MatchQ[s, _Symbol] are even slower.

To clarify, I want to have two different definitions for func , with one for unassigned Symbols, and one for other arguments.

Is there a faster way?


Timings:

f[x_] = 0;

f /@ Range@1*^6; // Timing

f[s_Symbol] = 1;

f /@ Range@1*^6; // Timing
   {0.391, Null}
   {0.531, Null}
Remove[f]
SetAttributes[f, HoldFirst]

f[x_] = 0;

f /@ Range@1*^6; // Timing

f[s_] /; Head[s] === Symbol = 1;

f /@ Range@1*^6; // Timing
   {0.39, Null}
   {1.157, Null}


You can get performance comparable to the fastest exhibited running times by delegating held symbol arguments to a non-held helper function g:

Remove[f, g]
SetAttributes[f, HoldFirst]
f[_] = 0;
f[s_Symbol] := g[s]
g[_Symbol] = 1;
g[_] = 0;


Using pattern s_Symbol in the code with HoldFirst attribute will improve the performance:

In[121]:= Remove[f]
SetAttributes[f, HoldFirst]
f[s_Symbol] /; Head[s] === Symbol = 1;
f[_] = 0;

In[125]:= f /@ Range@1*^6; // Timing

Out[125]= {1.217, Null}

In[130]:= Remove[f2]
f2[s_Symbol] = 1;
f2[_] = 0;

In[133]:= f2 /@ Range@1*^6; // Timing

Out[133]= {1.123, Null}


You can get it faster with this:

ClearAll[f];
SetAttributes[f, HoldFirst]
f[x_] = 0;
f[s_Symbol] /; OwnValues[s] =!= {} = 1;

To compare, here is the one you used:

ClearAll[ff];
SetAttributes[ff, HoldFirst]
ff[x_] = 0;
ff[s_] /; Head[s] === Symbol = 1;

Now:

In[30]:= f /@ Range@1*^6; // Timing

Out[30]= {0.719, Null}

In[56]:= ff /@ Range@1*^6; // Timing

Out[56]= {1.25, Null}

This will be more effective when your arguments will mostly be non-symbols, and the reason that it is faster is that you can still use the _Symbol pattern to filter them out. For lists of symbols only, it may actually be slower:

symbTest = Table[ToExpression["sym" <> ToString[i]], {i, 100000}];
MapIndexed[If[OddQ[First@#2], #1 = First@#2] &, symbTest];

In[54]:= ReleaseHold[Map[f,Hold[symbTest]/.OwnValues[symbTest],{2}]]//Short//Timing
Out[54]= {0.234,{1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,<<99964>>,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0}}

In[58]:= ReleaseHold[Map[ff,Hold[symbTest]/.OwnValues[symbTest],{2}]]//Short//Timing
Out[58]= {0.141,{0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,<<99964>>,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1}}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜