# How do I access the name of a list in a function?

Background:

  bar := {1, 1, 1};
foo[var_] := {Hold[var], 2 var}
SetAttributes[foo, HoldAll]
foo[bar]


Result, as expected:

  {Hold[bar], {2, 2, 2}}


What I then do ( which is clearly not correct, but I don't know another way ) is that I ToString the first element of the list, i.e. Hold[bar] and then extract the substring "bar" out of it.

What I want is just this:

  f[data_?VectorQ] := f[data, name_of_data]
f[data_?VectorQ, name_?StringQ] := "Save data as name" ...


So for example:

   ape = {1,2,3};
bear= {2,3,4};
f[ape] translates to f[{1,2,3"}, "ape"]
f[bear, "bird"] translates to f[{2,3,4}, "bird"]
f[ape+bear] translates to Null or False.


Question: How do I access the name of a list in a function ?

-

You need to use HoldForm instead of Hold.

Clear@f
SetAttributes[f, HoldAll]
f[data_?VectorQ] := f[data, ToString@HoldForm@data]
f[data_?VectorQ, name_?StringQ] := "Save data as " <> name


With this:

f[ape]
(* "Save data as ape" *)

f[ape, "bird"]
(* "Save data as bird" *)


To get Null or False for f[ape+bear], you can use StringFreeQ[name, "+"] to see if the input is of that form. Similarly, "{" for list arguments (no need to check for "}"). So the second definition for f above would be:

f[data_?VectorQ, name_?StringQ] :=
If[StringFreeQ[name, "+" | "{"], "Save data as " <> name, False]

f[{1, 2, 3}]
(* False *)

f[ape + bear]
(* False *)

-
Hmm, 4 seconds difference –  Heike May 5 '12 at 17:41
Oooh. That's the closest I've been involved in! :) –  rm -rf May 5 '12 at 17:47
Any reason for name_?StringQ rather than name_String? –  Mr.Wizard May 5 '12 at 18:21
@Mr.Wizard That's what the OP used for whatever reason and I stuck with it –  rm -rf May 5 '12 at 18:28
@R.M.: I think you need _?StringQ since _String would not work due to the HoldAll attribute. You could alternatively wrap an Evaluate around the second argument in the call to f which would probably a good idea anyway... –  Albert Retey May 5 '12 at 18:34

You could use HoldForm instead of Hold, for example

bar := {1, 1, 1};
foo[var_] := {ToString@HoldForm[var], var}
SetAttributes[foo, HoldAll]
foo[bar]

(* output: {"bar", {1, 1, 1}} *)


Edit

To distinguish between proper variable names and something like ape + bear you could do something like

foo[var_] := {var, Quiet@Check[SymbolName[Unevaluated[var]], False]}
SetAttributes[foo, HoldAll]

bar := {1, 1, 1};
ape = {2, 3, 4};

foo[bar]

(* ==> {{1, 1, 1}, "bar"} *)

foo[bar + ape]

(* ==> {{3, 4, 5}, False} *)

-
Is there a smart way to check that only f[varname] is OK and not f[{1,2,3}] or f[var1+var2+var3] etc.? they all pass VectorQ –  ndroock1 May 5 '12 at 17:48
@ndroock1 I've updated my answer –  Heike May 5 '12 at 18:04
As for the check whether the argument is a symbol you could just use foo[var_Symbol]:=... or even combine it with _?VectorQ as this: _Symbol?VectorQ. It might come as a surprise that this works but _Symbol will check the Head of the unevaluated argument due to the HoldAll attribute while the VectorQ will see the evaluated argument. And yes, Leonid would for sure consider the _?VectorQ part to be an evaluation leak. For this particular use case I would consider that no problem though... –  Albert Retey May 5 '12 at 18:47