13
$\begingroup$

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.

$\endgroup$
10
  • 1
    $\begingroup$ 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. $\endgroup$
    – LLlAMnYP
    Commented Jun 5, 2015 at 23:00
  • 1
    $\begingroup$ BTW: do you know about PrimePi[]? $\endgroup$ Commented Jun 5, 2015 at 23:16
  • 1
    $\begingroup$ 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) $\endgroup$
    – Mr.Wizard
    Commented Jun 6, 2015 at 0:26
  • 1
    $\begingroup$ @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. $\endgroup$ Commented Jun 6, 2015 at 2:04
  • 2
    $\begingroup$ @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. $\endgroup$ Commented Mar 11, 2016 at 15:03

4 Answers 4

12
$\begingroup$

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}*)
$\endgroup$
1
  • $\begingroup$ 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! $\endgroup$ Commented Jun 5, 2015 at 23:14
12
$\begingroup$

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 :-)

$\endgroup$
3
  • $\begingroup$ Nice answer. It's like running Trace but with explanations! $\endgroup$ Commented Jun 6, 2015 at 0:10
  • $\begingroup$ @Simon, one would hope TracePrint[] could do things in a conversational manner… :D $\endgroup$ Commented Jun 6, 2015 at 0:14
  • $\begingroup$ New feature in Wolfram Alpha (coming soon, TM)? $\endgroup$
    – LLlAMnYP
    Commented Jun 6, 2015 at 0:20
7
$\begingroup$

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} *)
$\endgroup$
4
  • $\begingroup$ I don't see what that fixed. $\endgroup$ Commented Jun 5, 2015 at 23:13
  • $\begingroup$ Well isn't Thread[findPrime[{7,8,37,127}]] now returning the correct result? $\endgroup$
    – LLlAMnYP
    Commented Jun 5, 2015 at 23:15
  • $\begingroup$ @LLlAMnYP I'm sorry, I should have said: I don't understand why that fixed it. $\endgroup$ Commented Jun 5, 2015 at 23:24
  • 1
    $\begingroup$ @Jerry, as noted in the second paragraph, findprime here has been restricted to work only on integers, and will remain inert when applied to a list. At least, up until you put in Thread[]. $\endgroup$ Commented Jun 5, 2015 at 23:43
4
$\begingroup$

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

Thread@Unevaluated@findPrime@{7, 8, 37, 127}
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.