6
$\begingroup$

I am trying to use Thread to apply a function involving RandomReal but it does not work as I expected.

If I have the function

f[x_, y_] := x + y

I can use Thread to map over a list of x values of interest

Thread[f[{1, 2, 3}, 4]]

I cannot, however, do this if I have the function

f[x_, y_] := RandomReal[{x , y}]

If I call the Thread function again I get the following error error message error message

I expected to get as output the results of

{f[1,4], f[2,4], f[3,4]}

so basically 3 random numbers. Probably more importantly than why this doesn't work - is how can I replicate this behaviour when using RandomReal in the function?

$\endgroup$
2
  • $\begingroup$ Side note: f[#, 4]&/@{1,2,3} is more natural in this case. $\endgroup$
    – xzczd
    Commented Jun 30, 2020 at 3:57
  • $\begingroup$ @xzczd I didn't see your comment but we're thinking the same. The mechanism of Thread is somewhat a bit of complicated and I rarely use it. $\endgroup$ Commented Jun 30, 2020 at 4:44

1 Answer 1

10
$\begingroup$
Thread[Unevaluated@f[{1, 2, 3}, 4]]
{1.946182809, 2.842898138, 3.571798651}

Explanation

Documentation of Thread:

Thread evaluates the whole expression before threading.

f[x_, y_] := x + y

Thread[f[{1, 2, 3}, 4]]
=> Thread[{1, 2, 3} + 4] (* Evaluates *)
=> Thread[{5, 6, 7}]
=> {5, 6, 7} (* Threads... trivially *)

Since + is Listable, nothing went wrong even though f was evaluated first.

f[x_, y_] := RandomReal[{x, y}]

Thread[f[{1, 2, 3}, 4]]
=> Thread[RandomReal[{1, 2, 3}, 4]] (* Evaluates... *)
=> (* Error *)

"Evaluate first" passes wrong arguments to RandomReal.

f[x_, y_] := RandomReal[{x, y}]

Thread[Unevaluated@f[{1, 2, 3}, 4]]
=> Thread[f[{1, 2, 3}, 4]] (* Evaluates *)
=> {f[1,4],f[2,4],f[3,4]} (* Threads *)
=> ...

Unevaluated helps f survive the evaluation.


f[x_, y_] := RandomReal[{x, y}]
list = {1, 2, 3};

Thread[Unevaluated@f[list, 4]]
=> Thread[f[list, 4]] (* Evaluates *)
=> f[list, 4] (* Threads... Nothing to thread! *)
=> RandomReal[{1, 2, 3}, 4]
=> (* Error *)

list should not survive the evaluation, while f should. We can use Inactive on f and Activate afterwards:

f[x_, y_] := RandomReal[{x, y}]
list = {1, 2, 3};

Activate@Thread[Inactive[f][list, 4]]
=> Activate@Thread[Inactive[f][{1, 2, 3}, 4]] (* Evaluates *)
=> Activate@{Inactive[f][1,4], Inactive[f][2,4], Inactive[f][3,4]} (* Threads *)
=> {f[1,4], f[2,4], f[3,4]} (* Activates *)
=> ...

Actually, there're many simpler and faster ways to do this, like:

f[#, 4] & /@ list
$\endgroup$
4
  • $\begingroup$ Hope I can get away with a follow-up question. Why does this fail if you replace the {1,2,3} with something like list = {1,2,3}; Thread[Unevaluated@f[list, 4]] ? Is it because of the Unevaluated? $\endgroup$
    – Esme_
    Commented Jun 29, 2020 at 22:57
  • $\begingroup$ @Esme_ Yes, in this case the {1,2,3} is not explicit. In other words, Thread cannot see the {1,2,3} inside list. A possible solution: list = {1, 2, 3}; With[{list = list}, Thread[Unevaluated@f[list, 4]]], another: list = {1, 2, 3}; Thread[Unevaluated@f[#, 4]] &@list. $\endgroup$
    – xzczd
    Commented Jun 30, 2020 at 3:48
  • $\begingroup$ @Esme_ Yes, please see my updated answer. $\endgroup$ Commented Jun 30, 2020 at 4:04
  • $\begingroup$ Thanks SneezeFor16Min and xzczd for the explanations and detailed answers. Despite many years of using Mathematica I'm still only touching the surface! $\endgroup$
    – Esme_
    Commented Jun 30, 2020 at 5:47

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.