4
$\begingroup$

Let's say I want to assign to a variable the value $2020$ if it has not already been defined and do nothing if it has. In short, for a variable $a$, I want to achieve the functionality of If[Not[ValueQ[a]], a=2020]. Now, if the variable itself is very long, I want to avoid having to type it 2 or more times, so I tried to use pure functions. If $a$ has not been defined,

Clear[a]
If[Not[ValueQ[#]], #=2020] &[a];
a

(* 2020 *)

This works as expected. However, in the opposite case, it won't work since we're trying to assign e.g. $10=2020$,

a = 10;
If[Not[ValueQ[#]], #=2020] &[a];
a

(* Set::setraw: Cannot assign to raw object 10. *)
(* 10 *)

In theory, this does leave $a$ with the behaviour I want, so I could just suppress the error and move on, but of course I'm looking for an error-free solution. I also tried:

a = 10;
a := 2020 /; Not[ValueQ[a]];
a

(* During evaluation of In[1]:= $RecursionLimit::reclim2: Recursion depth of 1024 exceeded during 
evaluation of HoldPattern[a]:>2020/;!ValueQ[a]. *)
(* 2020 *)

I don't understand why this sets $a=2020$ even though the condition is not met. Also, I don't think it would work with #.

$\endgroup$
1
  • 5
    $\begingroup$ Pure functions don't hold their arguments by default, so a evaluates before it's ever inserted into the body. You can use something like Function[var, body, HoldFirst] to prevent a from evaluating prematurely. $\endgroup$ Commented May 15, 2020 at 12:03

2 Answers 2

3
$\begingroup$

You say that you want to reproduce the behavior of If[Not[ValueQ[a]], a = 2020]. This will assign a value and return it if a is not assigned, or return Null if a already has a value.

I wonder if the following function would work for you then:

ClearAll[condassign]
condassign[variable_Symbol, value_: 2020] := (variable = value)
condassign[variable_?NumericQ, value_: 2020] := Null

The value itself can be given as a second argument, but if omitted it is assumed to be 2020.

Here are two examples:

a =.                            (* Clear any value in a              *)
condassign[a]                   (* Out: 2020 because a had not value *)
Print@condassign[a]             (* Out: Null now that a has a value  *)

The Print expression above is only to show the Null return value, which otherwise would not appear explicitly.

$\endgroup$
4
  • $\begingroup$ Thanks, that works well enough. I wonder how to generalise it though. For example, if I have a list b={1,2,3} defined somewhere else in my code, then condassign[b] is not going to work. I would have to add condassign[variable_?ListQ, value_: 2020] := Null and similarly for other types of data. Is there a way to do it in one line, e.g. something to the effect of AnyQ? I was thinking about using Names["Global`*"] to get a full list of defined symbols, but I don't know what the correct syntax would be after variable_?... . $\endgroup$
    – dzejkob
    Commented May 17, 2020 at 20:51
  • $\begingroup$ @dzejkob Truthfully, I worry that you may be making your life harder than you need. I have never encountered the need to do what you ask, so I wonder: perhaps one could that a completely different approach? Why do you need this functionality? What problem are you trying to solve with it? Maybe there's another way around it. $\endgroup$
    – MarcoB
    Commented May 17, 2020 at 21:05
  • $\begingroup$ You are right, this is definitely not a critical issue. I have a module which checks if there exists a global list of replacements, then creates it/updates the existing one with new rules (different input to module may lead to new rules). These replacements are then used on a different quantity which is the final output. This runs in a Do loop, so list is employed and updated every time. If[Not[ValueQ[a]], a = {}] works fine, I guess my attempt to rewrite it as a pure function was just an exercise for the sake of it - I feel that I'm not comfortable with pure functions as a beginner... $\endgroup$
    – dzejkob
    Commented May 17, 2020 at 21:34
  • 1
    $\begingroup$ @dzejkob If I understand your comment here, you don't need to write a version of the function for every type of head. Just condassign[v_Symbol,val_:2020]:=(v=val);condassign[v_,val_]:=Null will evaluate to Null anytime v isn't a symbol. $\endgroup$
    – N.J.Evans
    Commented May 18, 2020 at 19:06
2
$\begingroup$
Clear[a]
If[Head[a] === Symbol, a = 2020]
a                                       (* 2020 *)

a=10;
If[Head[a] === Symbol, a = 2020]
a                                        (* 10 *)
$\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.