4
$\begingroup$

I am debugging a ListPlot implementation and I need to see the command. To avoid huge list of numbers, i just need the ListPlot like command to return the name of variables it is going to plot. My idea is to do something like HoldForm but it seems that HoldForm does not work with Map if the variable is a list. E.g.

f = {{1, 1}, {2, 2}};
g = {{3, 4}, {5, 6}};
MapThread[q[HoldForm@#1, #2] &, {{f, g}, {a, b}}]

returns

{q[{{1,1},{2,2}},a],q[{{3,4},{5,6}},b]}

Obviously, I want:

{q[{f,a],q[g,b]}

After trial and error I found this to work:

f = {{1, 1}, {2, 2}};
g = {{3, 4}, {5, 6}};
MapThread[
 q[HoldForm@#1, #2] &, {{Unevaluated@f, Unevaluated@g}, {a, b}}]

But it is way too cumbersome, I really do not want to put Unevaluated in front of each list.

Surprisingly, this does not work

MapThread[q[HoldForm@#1, #2] &, {Map[Unevaluated, {f, g}], {a, b}}]

Is there any way to achieve this without having to put Unevaluated in front of each data list, or even better just keep some kind of Hold in the function (or first argument of MapThread? Or is this unachievable as the f and g are expanded before getting to the slot? In that case why I cannot map Unevaluated as in the last example?

$\endgroup$
1

3 Answers 3

7
$\begingroup$

Another question about evaluation control. The keypoint is, if a function doesn't have a Hold* attribute, its argument(s) will always be evaluated before going into the function. So in your case, you need to stop the automatic evaluation 3 times. One for q, you've already done it with HoldForm, but I prefer a Hold* attribute; one for 2nd argument of MapThread, you only need one Unevaluated; one for &, this is easy to ignore, but don't forget its FullForm is Function, so automatic evaluation happens when arguments pass through it! To sum up, the following is a possible solution:

f = {{1, 1}, {2, 2}};
g = {{3, 4}, {5, 6}};
SetAttributes[q, HoldFirst];
MapThread[Function[{arg1, arg2}, q[arg1, arg2], HoldFirst], 
          Unevaluated@{{f, g}, {a, b}}]

Your last sample doesn't work because you've forgotten Map doesn't have Hold* attribute, either. To fix it, you need to stop the automatic evaluation of 2nd argument of Map. The following is a possible solution:

ClearAll@q
MapThread[q[HoldForm@#1, #2] &, {Map[Unevaluated, Unevaluated@{f, g}], {a, b}}]

As to Unevaluated, it is a special function, it owns some properties that cannot be explained in usual way. I'd recommend reading this:

https://library.wolfram.com/infocenter/Conferences/377

Some other posts showing its special behavior:

Why one needs two Unevaluated to show 1+1 correctly in TreeForm?

Why Unevaluated doesn't work on unheld side of Rule/RuleDelayed?

$\endgroup$
6
$\begingroup$

You have a problem with the pre-existing definitions for f and g. You can temporarily over-ride these, using Block

Block[{f, g}, MapThread[q[HoldForm@#1, #2] &, {{f, g}, {a, b}}]]
(* {q[f,a],q[g,b]} *)
$\endgroup$
4
$\begingroup$

You can make an auxiliary function which maps HoldForm over all elements of the list.

SetAttributes[holdList, HoldAll];
holdList[lst_] := ReleaseHold@Map[HoldForm, Hold@lst, {-1}]

MapThread[q[#1, #2] &, holdList@{{f, g}, {a, b}}]
(* {q[f, a], q[g, b]} *)
$\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.