13

This function finds n given the nth prime:

findPrime[n_] := If[PrimeQ[n], i = 1; While[Prime[i] < n, i = i + 1]; i, False];

This works:

Map[findPrime, {7, 8, 37, 127}]

(* {4, False, 12, 31} *)

And this works:

Thread[PrimeQ[{7, 8, 37, 127}]]

(* {True, False, True, True}  *)

But this doesn't work:

Thread[findPrime[{7, 8, 37, 127}]]

(* {1, False, 1, 1}  *)

Why doesn't that work? Does Thread only work on built-in functions?

And why does Thread exist? It seems to be redundant with Map.

Jerry Guern
  • 4,602
  • 18
  • 47
  • 1
    That's interesting. The problem is, that findPrime[{7,8,37,127}] evaluates first, only then does Thread act. Also, the capabilities of Thread, though similar to Map at first glance, are different and in your example one of those differences just bit you in the arse :-) You should check the documentation of MapThread, if it's not immediately clear how different Thread is from Map. – LLlAMnYP Jun 05 '15 at 23:00
  • 1
    BTW: do you know about PrimePi[]? – J. M.'s missing motivation Jun 05 '15 at 23:16
  • @J.M. I sure didn't. Thank you. But hey, if I had known about PrimePi[], I wouldn't have learned what I learned today about Map[] and Thread[] and SetAttributes[]. – Jerry Guern Jun 05 '15 at 23:22
  • 1
    This question is answered here: (6588), (26686), (84960). I favor closing this question as a duplicate of all of these but I cannot do that alone. Also related: (3217), (33046) – Mr.Wizard Jun 06 '15 at 00:26
  • 1
    @Mr.Wizard, I don't see that any of your linked questions addresses the first part of this question (pitfall of premature evaluation of argument of Thread). The second part (difference between Thread and MapThread) is certainly covered in those, though. – Simon Rochester Jun 06 '15 at 02:04
  • @Simon If the post about Thread and MapThread is understood it will also be understood why Thread[findPrime[{7, 8, 37, 127}]] "doesn't work" -- it is exactly the effect under discussion. – Mr.Wizard Jun 06 '15 at 05:37
  • 1
    @Mr.Wizard I see your point. I guess it seemed odd to me because the question itself isn't a duplicate, even if the answer is to be found in the answer to the other question. Also, that post seems to be pitched at a somewhat more advanced level than the question and answers here, so I don't know if someone with the question here would easily make the connection to the answer there. But you certainly know better than me what's appropriate to do with it. – Simon Rochester Jun 06 '15 at 06:10
  • @SimonRochester Thank you for making that point, that a lot of us don't have the advanced understanding to make some connections like that. Sometimes the experts are a little hard on us semi-noobs. – Jerry Guern Dec 14 '15 at 23:46
  • @Mr.Wizard One reason these "duplicate" questions get asked is because it's so hard to find those other questions. When it became clear to me that Thread wasn't doing what the help file says it does (as usual), I started searching here for the difference between Thread and Map. I got two results: one unrelated and this page. Maybe I should be using Google to search SE instead, is that how you find them? I'm really frustrated and don't mean to be rude/mouthy, but let's assume we're not all over the site like you are. (I do appreciate all the help/input I've found from you on the site.) – Travis Bemrose Mar 11 '16 at 14:50
  • 2
    @TravisBemrose I always use Google, not the SE's Search. If you put "Mathematica" followed by whatever you're looking for, most of the results you get will be Wolfram or SE anyway, and Google does a much, much better job. – Jerry Guern Mar 11 '16 at 15:03

4 Answers4

12

PrimeQ is a Listable and Map will list findPrime over the list.

you can do this:

SetAttributes[findPrime, Listable]

and then:

Thread[findPrime[{7, 8, 37, 127}]] 

or just directly:

findPrime[{7, 8, 37, 127}]

(*{4, False, 12, 31}*)

If you want to keep findPrime free of any Attribute, you can do the following:

ClearAttributes[findPrime, Listable]


Activate@Thread[Inactive[findPrime][{7, 8, 37, 127}]]

or

Thread[Unevaluated@findPrime[{7, 8, 37, 127}]]

(*{4, False, 12, 31}*)
Basheer Algohi
  • 19,917
  • 1
  • 31
  • 78
  • Oh! I've never heard of SetAttributes[]. This is totally new to me. THIS is exactly why I post these nit-picky questions, to get insights like this. Thanks! – Jerry Guern Jun 05 '15 at 23:14
12

It's sad to see a question about Thread sidestep the discussion about Thread so I'll try to fill in the void, though it is becoming redundant after Simon's answer.

The normal sequence of evaluation for something like f[a, b, c] is to evaluate a, b, and c first (let's say, they are 5, 1, and 2, respectively). So then we get f[5, 1, 2]. And then Mathematica evaluates that if it has a rule set up for it. Thread is no exception.

As in the OP:

findPrime[n_] := If[PrimeQ[n], i = 1; While[Prime[i] < n, i = i + 1]; i, False];

Therefore:

Thread[findPrime[{7,8,37,127}]]

evaluates to

Thread[
  If[PrimeQ[{7,8,37,127}], i = 1; While[Prime[i] < {7,8,37,127}, i = i + 1]; i, False];
]

wherein PrimeQ[{7,8,37,127}] evaluates to {True,False,True,True}, after which we have

Thread[
  If[{True,False,True,True}, i = 1; While[Prime[i] < {7,8,37,127}, i = i + 1]; i, False];
]

Finally we reach a point where Mathematica doesn't know what to do next, so it threads If over the first argument, while the other two arguments are the same for each case. Also, because Prime[i] < {7,8,37,127} cannot be evaluated as true or false, the While loop does not work any cycles. So after thread operates we get

{
  If[True, i = 1; i, False];,
  If[False, i = 1; i, False];,
  If[True, i = 1; i, False];,
  If[True, i = 1; i, False];,
}

That's why Simon suggested to restrict the input to findPrime. If findPrime were to accept only integers as arguments and not do anything if given other types of arguments, such as list,

Thread[findPrime[{7,8,37,127}]]

would have no chance of prematurely evaluating the argument of Thread, so the only thing to do would be to go right ahead and convert to

{findPrime[7], findPrime[8], findPrime[37], findPrime[127]}

To address the other question:

Also Thread[f[{a,b,c},x,{d,e,f}]] evaluates to {f[a,x,d],f[b,x,e],f[c,x,f]}, let's see you do that with Map or even MapThread :-)

LLlAMnYP
  • 11,486
  • 26
  • 65
7

Another fix is to restrict the input argument.

Clear[findPrime]; 
findPrime[n_Integer] := If[PrimeQ[n], i = 1; While[Prime[i] < n, i = i + 1]; i, False];

That will keep findPrime from operating on the input until after Thread has had a chance to do its job:

Thread[findPrime[{7, 8, 37, 127}]]
(* {4, False, 12, 31} *)

Also, as mentioned in a comment by @LLlAMnYP, Thread is closer in capability to MapThread than to Map. Aside from the differences in functionality (MapThread has level specification, Thread has head and sequence specification, and automatic duplication of non-list arguments), the syntax of Thread can be very nice in situations like making lists of rules:

Thread[{a, b, c} -> {1, 2, 3}]
(* {a -> 1, b -> 2, c -> 3} *)
Simon Rochester
  • 6,211
  • 1
  • 28
  • 40
4

In the same spirit as Simon's answer, we can also use Unevaluated.

Thread@Unevaluated@findPrime@{7, 8, 37, 127}
Jacob Akkerboom
  • 12,215
  • 45
  • 79