6

I'm trying to teach myself to use pure functions and slots as much as possible. Here is my problem.

I have a list:

RandomSeed[314];
l = RandomInteger[{1, 10}, 20]
(*{3, 6, 1, 4, 4, 6, 1, 6, 3, 6, 8, 10, 8, 7, 5, 4, 8, 4, 4, 8}*)

Now I want to select all elements that equal 1 by using Select:

Select[l, # == 1 &]
(*{1, 1}*)

Then I want to get two lists, one with 8s and second with 1s:

test[p_] := p == # &;
Select[l, test[#]] & /@ {8, 1}
(*{{8, 8, 8, 8}, {1, 1}}*)

How can I do it without introducing test[p_]? In other words I need Select's second argument to depend on "another" slot #, not the one that will be substituted by list elements during iteration. The closest question I was able to find is Second level depth pure function?, but I think my question is slightly different, because I don't have to put slot in the first argument of Select

Update: Answers extracted from comments.

Thanks a lot to everybody who answered in comments.

leo[l_] := Reap[Sow[#, #] & /@ l, 1 | 8, #2 &][[2]];
jm[l_] := Function[p, Select[l, # == p &]] /@ {8, 1};
kgl[l_] := Select[l, Function[x, x == #]] & /@ {8, 1};
mar[l_] := With[{p = #}, Select[l, # == p &]] & /@ {8, 1};

Out of curiosity I decided to run simple benchmark to check performance

Benchmark[f_, n_] := Module[{l, results, samples},
   RandomSeed[314];
   samples = Table[RandomInteger[{1, 100}, n], {10}]; 
   results = Table[First@AbsoluteTiming[f[l]], {l, samples}];
   Mean[results]];
testRange = 10^# &@{3, 4, 5, 6};
TableForm[
 Table[Benchmark[fun, n]/n, {fun, {leo, jm, kgl, mar}}, {n, 
   testRange}], 
 TableHeadings -> {{"leo", "jm", "kgl", "mar"}, testRange}]

enter image description here

The results are normalized over list length. Interestingly kgl[] is about two times slower than jm[].

IMHO the Select[l, Function[x, x == #]] & /@ {8, 1} (by @kglr) is the most visually appealing.

It was also asked why do I care if all I need is just making two simple Selects?

I think it's more clear and concise to have one line that does something twice rather than having two almost identical lines. My original example is oversimplified probably.

Imagine I want to select all prime numbers and also all numbers that are prime squared. If I use syntax by @kglr I can do

{primes, primeSq} = 
 Select[l, Function[x, #[x]]] & /@ {PrimeQ, PrimeQ@Sqrt[#] &}

Which is very self explanatory.

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
BlacKow
  • 6,428
  • 18
  • 32
  • 3
    With[{p=#}, Select[l, # == p &]]& /@{8,1} is one possibility. Or use Function. – Marius Ladegård Meyer Mar 18 '16 at 18:03
  • 5
    Why not Function[p, Select[l, # == p &]] /@ {8, 1}? – J. M.'s missing motivation Mar 18 '16 at 18:03
  • @MariusLadegårdMeyer, @J.M. It still kinda introduces a named parameter and also wraps my function into something. I'm more after some sort of # slot of upper level? – BlacKow Mar 18 '16 at 18:07
  • @LeonidShifrin Yes, I mentioned it in my question. My question is different because the first argument in Select is the same in my case. So I was hoping that my question has simpler solution. – BlacKow Mar 18 '16 at 18:08
  • Note that in some cases (depending on what test) is, you can use an operator form like EqualTo[#]. Here is such an example from earlier today. – Martin Ender Mar 18 '16 at 18:19
  • 1
    I retracted the close vote. However, note that as soon as you start categorizing things, your operation is no longer a simple select, so there is no reason to expect that Select without additional steps can handle it - categorization is semantically different from selection. You can use Reap and Sow with specific tags: Reap[Sow[#, #] & /@ l, 1 | 8, #2 &][[2]], which maps mor directly to your needs, it seems. – Leonid Shifrin Mar 18 '16 at 18:21
  • @LeonidShifrin The Reap Sow approach doesn't conserve order - it gives {{1, 1}, {8, 8, 8, 8}} instead of {{8, 8, 8, 8}, {1, 1}}. Also I think it's way more complex than Function[p, Select[l, # == p &]] /@ {8, 1} – BlacKow Mar 18 '16 at 18:32
  • @BlacKow Reap - Sow is a conceptually simpler solution, and also with much better complexity, if you look for a categorization problem (which you do, de facto). OTOH, if all you need is a couple of selections, then I don't see why asking this question at all. As I said, your problem is really not a pure selection problem as soon as you start combining elements into groups. If Map and Select work for you - fine. – Leonid Shifrin Mar 18 '16 at 18:46
  • @LeonidShifrin This is not about Select really, instead of Select there could be any other function that takes pure function as an argument. Honestly I was hoping that I'm missing some magic syntax a la "&#" that would be identical to Function[p, Select[l, # == p &]] – BlacKow Mar 18 '16 at 18:51
  • 2
    Select[l, Function[x, x == #]] & /@ {8, 1}? – kglr Mar 18 '16 at 19:00
  • @BlacKow I don't think your real problem is syntactic. You may think that it is, but I think it is really semantic. Which is what I tried to convey in my previous comments. – Leonid Shifrin Mar 18 '16 at 19:18
  • 1
    @BlacKow, from the Slot docs: "When pure functions are nested, the meaning of slots may become ambiguous, in which case parameters must be specified using an explicit Function construction with named parameters." – Marius Ladegård Meyer Mar 18 '16 at 20:01
  • One could think that named slot #a ,#b... have been invented to solve this problem (after the doc was written), but it is not the case, not at all. – andre314 Mar 18 '16 at 20:54
  • @kglr Care to put it as an answer? I like it better than others. – BlacKow Mar 18 '16 at 22:02
  • @BlacKow, just posted my comment as an answer. – kglr Mar 18 '16 at 22:08

1 Answers1

4
l = {3, 6, 1, 4, 4, 6, 1, 6, 3, 6, 8, 10, 8, 7, 5, 4, 8, 4, 4, 8}; 
Select[l, Function[x, x == #]] & /@ {8, 1}

{{8, 8, 8, 8}, {1, 1}}

kglr
  • 394,356
  • 18
  • 477
  • 896