tags:

views:

271

answers:

5

Does mathematica have something like "select any" that gets any element of a list that satisfies a criterion?

+1  A: 

There's "Select", that gets all the elements that satisfy a condition. So

In[43]:= Select[ {1, 2, 3, 4, 5}, OddQ ]
Out[43]= {1, 3, 5}

Or do you mean that you want to randomly select a single matching element? I don't know of anything built-in, but you can define it pretty quickly:

Any[lst_, q_] :=
   Select[ lst, q] // (Part[#, 1 + Random[Integer, Length[#] - 1]]) &

Which you could use the same way::

 In[51]:= Any[ {1, 2, 3, 4, 5}, OddQ ]
 Out[51]= 3
Eric
+2  A: 

Well, the downside of Eric's answer is that it does execute OddQ on all elements of the list. My call is relatively costly, and it feels wrong to compute it too often. Also, the element of randomness is clearly unneeded, the first one is fine with me.

So, how about

SelectAny[list_List, criterion_] := 
 Catch[Scan[  If[criterion[#], Throw[#, "result"]] &, list]; 
  Throw["No such element"], "result"]

And then

SelectAny[{1, 2, 3, 4, 5}, OddQ]

returns 1.

I still wish something were built into Mathematica. Using home-brew functions kind of enlarges your program without bringing much direct benefit.

nes1983
Mathematica already seems to contain an incomprehensibly large number of built-in functions; at some stage, I think, you have to accept that not everything is provided out of the box.
Will Robertson
+1  A: 

You can do it relatively easily with Scan and Return

fstp[p_, l_List] := Scan[ p@# && Return@# &, l ]

So

In[2]:= OddQ ~fstp~ Range[1,5]
Out[2]= 1
In[3]:= EvenQ ~fstp~ Range[1,5]
Out[3]= 2

I really wish Mathematica could have some options to make expressions evaluated lazily. In a lazy language such as Haskell, you can just define it as normal

fstp p = head . filter p
jxy
You can usually get a decent approximation of lazy evaluation using the HoldFirst/HoldRest/HoldAll attributes on symbols, in addition to the familiar trick of delaying things by wrapping them up as thunks.
Pillsy
+3  A: 

If you just want to return after the first matching element, use the optional third argument to Select, which is the maximum number of results to return. So you can just do

Any[list_List, crit_, default_:"no match"] := 
    With[{maybeMatch = Select[list, crit, 1]},
        If[maybeMatch =!= {},
            First[maybeMatch],
            default]

Mathematica lacks a great way to signal failure to find an answer, since it lacks multiple return values, or the equivalent of Haskell's Maybe type. My solution is to have a user-specifiable default value, so you can make sure you pass in something that's easily distinguishable from a valid answer.

Pillsy
+2  A: 

The Select function provides this built-in, via its third argument which indicates the maximum number of items to select:

In[1]:= Select[{1, 2, 3, 4, 5}, OddQ, 1]
Out[1]= {1}

When none match:

In[2]:= Select[{2, 4}, OddQ, 1]
Out[2]= {}

Edit: Oops, missed that nes1983 already stated this.

Andrew Moylan
I don't see mention of Select's third argument in nes1983's answer. Seems to me this should be the accepted answer.
dreeves
Oh, you must've meant Pillsy.
dreeves