8
$\begingroup$

First of all, I'm not sure about the title of this question, so please correct me if there is any title more sensible than mine. The problem is the following: I have a function f that checks if a variable as a value. For that, I need to pass the argument "by reference" that is I set HoldAll to f.

f[var_] := ValueQ[var];
SetAttributes[f, HoldAll];

f[x]
(* False *)

x = 10;    
f[x]
(* True *)

It works, as long as I call f passing the name of variable. But, if I want to call f on a list of variables, and I use something like a Map, it doesn't work

Map[f, {x, y, z}]
(* {False, False, False} *)

Note that x has a value and the result should be {True, False, False}.

Even more clear:

{x, y, z} = {1, 2, 3};
Map[f, {x, y, z}]
(* {False, False, False} *)

The problem is that Map evaluates x and pass the value of x to f, so ValueQ1 gives false. How to get the Map to give the right sequence {True, True, True}. It could be a very stupid solution but today I'm not able to get it. Of course, the actual code is a little bit more complex, I tried to simplify it so to provide a clear example. Thanks for any help.

EDIT Thanks to kguler, using Unevaluated works for the example I provided above, but not always for my real case. As for the other answer, that using Listable, it works as well but even that is not suitable for my real code because actually my f does many other things and cannot be easily applied to a list (moreover it has many other arguments, not just the variable). I'll try to add here a more realistic example of what I have. Considering this variation:

f[vars : {(_) ..}] := Map[ValueQ, vars];
SetAttributes[f, HoldAllComplete];

f[{x}]
(* {False} *)

x = 10; f[{x}]
(* {False} *)

f[{Unevaluated@x, Unevaluated@y}]
(* {True, False} *)

but if I have many variables and I don't know the names, for instance if I have all names of variables in another variable, like the following

variables = {x, y, z, q, w, e, r, t}

How to call f using Unevaluated? The only solution I can think of is to apply manually the Unevaluated to all variables. Is there another way?

EDIT II Somehow solutions provided by Mr.Wizard, Kuba and Algohi work fine in my case, but are not so easy as to add Unevaluated in the Map inside the f function, as suggested by kugler.

$\endgroup$
4
  • $\begingroup$ I think my answer anticipated your update. :-) If you find in inadequate please update your question one again to explain why. I'll be back later today to check. $\endgroup$
    – Mr.Wizard
    Mar 5, 2015 at 20:25
  • $\begingroup$ Thanks, I'm trying to understand if your solution works for my real case. I'll add a feedback as soon as possible (consider here is quite late, I'm in Italy). $\endgroup$
    – bobknight
    Mar 5, 2015 at 20:27
  • $\begingroup$ Regarding your EDIT II if you can give a more representative example of your function I will attempt to help you apply my method with the least effort or complication possible. $\endgroup$
    – Mr.Wizard
    Mar 6, 2015 at 2:40
  • $\begingroup$ I really appreciate your help, but the actual code is very long. It's a webMathematica application with a couple of packages calling each other. Basically, what I need is to pass input variables collected from a webMathematica page to an inner function (not directly coded into the page) to check if they have a value and if they are of expected type. In the inner function I use MSPValueQ (similar to ValueQ) and I need to pass the variables unevaluated. Thus, my efforts to simplify the question and avoid to put many lines of code that will make very hard to understand the core issue. $\endgroup$
    – bobknight
    Mar 6, 2015 at 3:12

5 Answers 5

7
$\begingroup$
Map[f, Unevaluated@{x, y, z}]
(* {True, True, True} *)

Update:

ClearAll[f, x, y, z, q, w, e, r, t]
f[vars : {(_) ..}] := Map[ValueQ, Unevaluated@vars];
SetAttributes[f, HoldAllComplete];

f[{x}]
(* {False} *)

x = 10; f[{x}]
(* {True} *)

f[{x, y}]
(* {True, False} *)

f[{x, y, z, q, w, e, r, t}]
(* {True, False, False, False, False, False, False, False} *)
$\endgroup$
6
  • $\begingroup$ thanks for the quick answer, unfortunately it works for the simplified example I posted but doesn't for the actual code. I'm trying to change the question so to post another piece of code to be more clear and precise. $\endgroup$
    – bobknight
    Mar 5, 2015 at 19:37
  • $\begingroup$ @bobknight I look forward to your clarifying update. $\endgroup$
    – Mr.Wizard
    Mar 5, 2015 at 19:54
  • 1
    $\begingroup$ @bobknight how about using Unevaluated in the definition of f, i.e., f[vars : {(_) ..}] := Map[ValueQ, Unevaluated@vars];? $\endgroup$
    – kglr
    Mar 5, 2015 at 21:15
  • $\begingroup$ Finally, that's the best way to solve the problem! Thanks a lot. Solutions provided by Mr.Wizard, Kuba and Algohi also have the right behavior but are not so easy as this one. $\endgroup$
    – bobknight
    Mar 6, 2015 at 1:17
  • $\begingroup$ @bobknight 1) so do you want this to work? vars := {x, y, z, q, w, e, r, t}; f[vars] 2) it would be nice to comment or whatever. My answer looks like I missed the point, no comments, no votes, and now I see it is correct :P. Just a note for future. $\endgroup$
    – Kuba
    Mar 6, 2015 at 6:24
7
$\begingroup$

Or set another attribute, Listable and be done with Map:

SetAttributes[f,{HoldAll,Listable}];

x=10; y=.; z=.;
f[{x,y,z}]
(* {True, False, False} *)
$\endgroup$
1
  • $\begingroup$ Beat me to it. +1 $\endgroup$
    – Mr.Wizard
    Mar 5, 2015 at 19:53
3
$\begingroup$

If you can't use Listable to your code is probably to use:

ClearAll[f]


   (*first definition handles cases when you use list:={x,y};f[list]*)
f[listof_Symbol] := f @@ (Hold[listof] /. OwnValues[listof]); 

   (*general definition handles list of symbols*)
f[vars_ : {(_) ..}] := ReleaseHold @ Map[ValueQ, Hold[vars], {2}];

 (*as in kugler's answer the line above can be written as:
   Map[ValueQ, Unevaluated[vars]];
  *)

SetAttributes[f, HoldAllComplete];

ClearAll[x,y]

f[{x}]
{False}
x = 10; f[{x}]
 {True}
f[{Unevaluated@x, Unevaluated@y}]
{True, False}
f[{x, y}]
{True, False}
list := {x, y}; 
f[list]
{True, False}
$\endgroup$
1
  • $\begingroup$ Yes, it works. Sorry, I didn't commented your solution yesterday night, because it was quite late and I was sleeping on the keyboard :-) $\endgroup$
    – bobknight
    Mar 6, 2015 at 7:28
2
$\begingroup$

The two standard approaches to this problem, Unevaluated and Listable, have already been posted.

If you need a different kind of evaluation control please consider my original method:(1),(2),(3),(4)

Load step How do I evaluate only one step of an expression? then:

SetAttributes[f2, HoldAll]

f2[s_Symbol] := step[s] /. {_[x_List] :> f2 /@ Unevaluated[x], _ :> ValueQ[s]}

Now:

x = 1;
q := {x, y, z};

f2[q]
{True, False, False}
$\endgroup$
0
0
$\begingroup$

you can change the attribute of Map:

Clear[f]
f[var_] := ValueQ[var] 

SetAttributes[f, HoldAll]
SetAttributes[Map, HoldAll]
{x, y, z} = {1, 2, 3};
Map[f, {x, y, z, k}]

(*{True, True, True, False}*)
$\endgroup$
6
  • $\begingroup$ The down-vote is not mine. However I can understand it as this is dangerous. One would do better to create a new Map derivative using Internal`InheritedBlock if this is desired, but using Unevaluated is simpler. $\endgroup$
    – Mr.Wizard
    Mar 5, 2015 at 20:21
  • $\begingroup$ @Mr.Wizard the OP replied that Unevaluated did not work for him. also I believe this can be done locally if the case is that it creates global issue $\endgroup$ Mar 5, 2015 at 20:24
  • $\begingroup$ I suggest that you include that local method. $\endgroup$
    – Mr.Wizard
    Mar 5, 2015 at 20:26
  • $\begingroup$ I believe this could be don but I don't know how. $\endgroup$ Mar 5, 2015 at 20:29
  • $\begingroup$ Oh, I understand. :-) $\endgroup$
    – Mr.Wizard
    Mar 5, 2015 at 20:33

Your Answer

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

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