Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>This is a version I've been using for quite a while (wrote it for a second edition of my book originally, but I ended up using it a lot). If arguments represent some unevaluated code, then the test function must have <code>HoldAll</code> or <code>HoldFirst</code> attributes if we want a single piece of code representing one specific clause to be passed to it in its unevaluated form (which may or may not be desirable).</p> <pre><code>ClearAll[fastOr]; Attributes[fastOr] = {HoldRest}; fastOr[test_, {args___}] := fastOr[test, args]; fastOr[test_, args___] := TrueQ[Scan[ Function[arg, If[test[arg], Return[True]], HoldAll], Hold[args]]]; </code></pre> <p>Edit: I just noticed that the solution by Daniel Reeves at the bottom of the page linked in the question, is very similar to this one. The main difference is that I care about both short-circuiting and keeping arguments unevaluated (see below), while Daniel focuses on the short-circuiting part only. </p> <p>It does have a short-circuiting behavior. We need <code>HoldRest</code> attribute since we want to preserve the arguments in their unevaluated form. We also need the <code>HoldAll</code> (or <code>HoldFirst</code>) attribute in a pure function to preserve each of the arguments unevaluated until it is passed to <code>test</code>. Whether or not it gets evaluated before it is used in the body of <code>test</code> depends now on the attributes of <code>test</code>. As an example:</p> <pre><code>Clear[fullSquareQ]; fullSquareQ[x_Integer] := IntegerQ[Sqrt[x]]; In[13]:= Or @@ Map[fullSquareQ, Range[50000]] // Timing Out[13]= {0.594, True} In[14]:= fastOr[fullSquareQ, Evaluate[Range[10000]]] // Timing Out[14]= {0., True} </code></pre> <p>Here is an example where we pass as arguments some pieces of code inducing side effects (printing). The code of the last argument has no chance to execute, since the result has already been determined at the previous clause:</p> <pre><code>In[15]:= fastOr[# &amp;, Print["*"]; False, Print["**"]; False, Print["***"]; True, Print["****"]; False] During evaluation of In[15]:= * During evaluation of In[15]:= ** During evaluation of In[15]:= *** Out[15]= True </code></pre> <p>Note that, since <code>fastOr</code> accepts general pieces of unevaluated code as clauses for <code>Or</code>, you have to wrap your list of values in <code>Evaluate</code> if you don't care that they are going to be evaluated at the start ( as is the case with the <code>Range</code> example above). </p> <p>Finally, I will illustrate the programmatic construction of held code for <code>fastOr</code>, to show how it can be used (consider it a tiny crash course on working with held expressions if you wish). The following function is very useful when working with held expressions:</p> <pre><code>joinHeld[a___Hold] := Hold @@ Replace[Hold[a], Hold[x___] :&gt; Sequence[x], {1}]; </code></pre> <p>Example:</p> <pre><code>In[26]:= joinHeld[Hold[Print[1]], Hold[Print[2], Print[3]], Hold[], Hold[Print[4]]] Out[26]= Hold[Print[1], Print[2], Print[3], Print[4]] </code></pre> <p>Here is how we use it to construct programmatically the held arguments that were used in the example with Print-s above:</p> <pre><code>In[27]:= held = joinHeld @@ MapThread[Hold[Print[#]; #2] &amp;, {NestList[# &lt;&gt; "*" &amp;, "*", 3], {False, False, True, False}}] Out[27]= Hold[Print["*"]; False, Print["**"]; False, Print["***"]; True, Print["****"]; False] </code></pre> <p>To pass it to <code>fastOr</code>, we will use another useful idiom: append (or prepend) to <code>Hold[args]</code> until we get all function arguments, and then use <code>Apply</code> (note that, in general, if we don't want the piece we are appending / prepending to evaluate, we must wrap it in <code>Unevaluated</code>, so the general idiom looks like <code>Append[Hold[parts___],Unevaluated[newpart]]</code>):</p> <pre><code>In[28]:= fastOr @@ Prepend[held, # &amp;] During evaluation of In[28]:= * During evaluation of In[28]:= ** During evaluation of In[28]:= *** Out[28]= True </code></pre> <p>Regarding the original implementation you refer to, you can look at my comment to it I made some while ago. The problem is that TakeWhile and LengthWhile have bugs for packed arrays in v. 8.0.0, they are fixed in the sources of 8.0.1 - so, starting from 8.0.1, you can use either mine or Michael's version.</p> <p>HTH</p> <p>Edit:</p> <p>I just noticed that in the post you referred to, you wanted a different syntax. While it would not be very difficult to adopt the approach taken in <code>fastOr</code> to this case, here is a different implementation, which arguably is in closer correspondence with the existing language constructs for this particular syntax. I suggest to use <code>Table</code> and exceptions, since iterators in <code>Table</code> accept the same syntax as you want. Here it is:</p> <pre><code>ClearAll[AnyTrue, AllTrue]; SetAttributes[{AnyTrue, AllTrue}, HoldAll]; Module[{exany, exall}, AnyTrue[iter : {var_Symbol, lis_List}, expr_] := TrueQ[Catch[Table[If[TrueQ[expr], Throw[True, exany]], iter], exany]]; AllTrue[iter : {var_Symbol, lis_List}, expr_] := Catch[Table[If[! TrueQ[expr], Throw[False, exall]], iter], exall] =!= False; ]; </code></pre> <p>A few words of explanation: I use Module on the top level since the custom exception tags we need to define just once, can as well do that at definition-time. The way to break out of Table is through exceptions. Not very elegant and induces a small performance hit, but we buy automatic dynamic localization of your iterator variables done by <code>Table</code>, and simplicity. To do this in a safe way, we must tag an exception with a unique tag, so we don't catch some other exception by mistake. I find using Module for creating persistent exception tags to be a very useful trick in general. Now, some examples:</p> <pre><code>In[40]:= i = 1 Out[40]= 1 In[41]:= AnyTrue[{i, {1, 2, 3, 4, 5}}, i &gt; 3] Out[41]= True In[42]:= AnyTrue[{i, {1, 2, 3, 4, 5}}, i &gt; 6] Out[42]= False In[43]:= AllTrue[{i, {1, 2, 3, 4, 5}}, i &gt; 3] Out[43]= False In[44]:= AllTrue[{i, {1, 2, 3, 4, 5}}, i &lt; 6] Out[44]= True In[45]:= AllTrue[{a, {1, 3, 5}}, AnyTrue[{b, {2, 4, 5}}, EvenQ[a + b]]] Out[45]= True In[46]:= AnyTrue[{a, {1, 3, 5}}, AllTrue[{b, {2, 4, 5}}, EvenQ[a + b]]] Out[46]= False </code></pre> <p>I started with an assignment to <code>i</code> to show that possible global values of iterator variables don't matter - this is taken care of by <code>Table</code>. Finally, note that (as I commented elsewhere), your original signatures for <code>AllTrue</code> and <code>AnyTrue</code> are a bit too restrictive, in the sense that the following does not work:</p> <pre><code>In[47]:= lst = Range[5]; AllTrue[{i, lst}, i &gt; 3] Out[48]= AllTrue[{i, lst}, i &gt; 3] </code></pre> <p>(since the fact that <code>lst</code> represents a list is not known at the pattern-matching time, due to <code>HoldAll</code> attribute). There is no good reason to keep this behavior, so you can just remove the <code>_List</code> checks: <code>AnyTrue[iter : {var_Symbol, lis_}, expr_]</code> and similarly for <code>AllTrue</code>, and this class of use cases will be covered.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload