21
\$\begingroup\$

Write a function (not a full program), so that if the function is called with a single global variable (or your language's closest equivalent) as its argument, it outputs (i.e. prints or returns) the name of that variable. If the argument is not a variable, output a falsey value instead. (You don't have to handle the case where the argument is a variable, but not global.)

There must not be a compile-time connection between the function call and function definition (notably, the function definition cannot be a macro or similar construct that receives arguments in the form of a literal fragment of source code, neither in text nor abstract syntax tree form). That is: in languages which support separate compilation, the program must work even if the function call is compiled first (with no knowledge of the function definition but possibly a type signature or equivalent), then the function definition is compiled afterwards. If the language doesn't have separate compilation, you must nonetheless aim for a similar separation between the call and definition.

If using a compiled language, you may read the compiled form of the complete program from disk, and may assume that the program was compiled with debug information. (Thus, solutions that work by attaching a debugger from a program to itself are allowed.)

Note that this task may not be possible in every language.

Examples:

var apple = "Hello"
p(apple) // apple

var nil = "Nothing"
p(nil) // nil

var SOMETHING = 69
p(SOMETHING) // SOMETHING

function foo(){}
p(foo) // foo

p(3.14159) // false

p(function foo(){}) // false
\$\endgroup\$
1
  • \$\begingroup\$ Comments are not for extended discussion; this conversation has been moved to chat. \$\endgroup\$
    – Dennis
    Commented Jun 17, 2017 at 21:44

19 Answers 19

32
+50
\$\begingroup\$

Java

String getParamName(String param) throws Exception {
    StackTraceElement[] strace = new Exception().getStackTrace();
    String methodName = strace[0].getMethodName();
    int lineNum = strace[1].getLineNumber();

    String className = strace[1].getClassName().replaceAll(".{5}$", "");
    String classPath = Class.forName(className).getProtectionDomain().getCodeSource().getLocation().getPath() + className + ".class";

    StringWriter javapOut = new StringWriter();
    com.sun.tools.javap.Main.run(new String[] {"-l", "-c", classPath}, new PrintWriter(javapOut));
    List<String> javapLines = Arrays.asList(javapOut.toString().split("\\r?\\n"));
    int byteCodeStart = -1;
    Map<Integer, Integer> byteCodePointerToJavaPLine = new HashMap<Integer, Integer>();
    Pattern byteCodeIndexPattern = Pattern.compile("^\\s*(\\d+): ");
    for (int n = 0;n < javapLines.size();n++) {
        String javapLine = javapLines.get(n);
        if (byteCodeStart > -1 && (javapLine == null || "".equals(javapLine))) {
            break;
        }
        Matcher byteCodeIndexMatcher = byteCodeIndexPattern.matcher(javapLine);
        if (byteCodeIndexMatcher.find()) {
            byteCodePointerToJavaPLine.put(Integer.parseInt(byteCodeIndexMatcher.group(1)), n);
        } else if (javapLine.contains("line " + lineNum + ":")) {
            byteCodeStart = Integer.parseInt(javapLine.substring(javapLine.indexOf(": ") + 2));
        }
    }

    int varLoadIndex = -1;
    int varTableIndex = -1;
    for (int i = byteCodePointerToJavaPLine.get(byteCodeStart) + 1;i < javapLines.size();i++) {
        if (varLoadIndex < 0 && javapLines.get(i).contains("Method " + methodName + ":")) {
            varLoadIndex = i;
            continue;
        }

        if (varLoadIndex > -1 && javapLines.get(i).contains("LocalVariableTable:")) {
            varTableIndex = i;
            break;
        }
    }

    String loadLine = javapLines.get(varLoadIndex - 1).trim();
    int varNumber;
    try {
        varNumber = Integer.parseInt(loadLine.substring(loadLine.indexOf("aload") + 6).trim());
    } catch (NumberFormatException e) {
        return null;
    }
    int j = varTableIndex + 2;
    while(!"".equals(javapLines.get(j))) {
        Matcher varName = Pattern.compile("\\s*" + varNumber + "\\s*([a-zA-Z_][a-zA-Z0-9_]*)").matcher(javapLines.get(j));  
        if (varName.find()) {
            return varName.group(1);
        }
        j++;
    }
    return null;
}

This currently works with a few gotchas:

  1. If you use an IDE to compile this it might not work unless it is run as Admin (depending on where the temporary class files are saved)
  2. You must compile using javac with the -g flag. This generates all debugging information including local variable names in the compiled class file.
  3. This uses an internal Java API com.sun.tools.javap which parses the bytecode of a classfile and produces a human readable result. This API is only accessible in the JDK libraries so you must either use the JDK java runtime or add tools.jar to your classpath.

This should now work even if the method is called multiple times in the program. Unfortunately it does not yet work if you have multiple invocations on a single line. (For one that does, see below)

Try it online!


Explanation

StackTraceElement[] strace = new Exception().getStackTrace();
String methodName = strace[0].getMethodName();
int lineNum = strace[1].getLineNumber();

String className = strace[1].getClassName().replaceAll(".{5}$", "");
String classPath = Class.forName(className).getProtectionDomain().getCodeSource().getLocation().getPath() + className + ".class";

This first part gets some general information about what class we're in and what the name of the function is. This is accomplished by creating an exception and parsing the first 2 entries of the stack trace.

java.lang.Exception
    at E.getParamName(E.java:28)
    at E.main(E.java:17)

The first entry is the line that the exception is thrown on which we can grab the methodName from and the second entry is where the function was called from.

StringWriter javapOut = new StringWriter();
com.sun.tools.javap.Main.run(new String[] {"-l", "-c", classPath}, new PrintWriter(javapOut));

In this line we are executing the javap executable that comes with the JDK. This program parses the class file (bytecode) and presents a human-readable result. We'll use this for rudimentary "parsing".

List<String> javapLines = Arrays.asList(javapOut.toString().split("\\r?\\n"));
int byteCodeStart = -1;
Map<Integer, Integer> byteCodePointerToJavaPLine = new HashMap<Integer, Integer>();
Pattern byteCodeIndexPattern = Pattern.compile("^\\s*(\\d+): ");
for (int n = 0;n < javapLines.size();n++) {
    String javapLine = javapLines.get(n);
    if (byteCodeStart > -1 && (javapLine == null || "".equals(javapLine))) {
        break;
    }
    Matcher byteCodeIndexMatcher = byteCodeIndexPattern.matcher(javapLine);
    if (byteCodeIndexMatcher.find()) {
        byteCodePointerToJavaPLine.put(Integer.parseInt(byteCodeIndexMatcher.group(1)), n);
    } else if (javapLine.contains("line " + lineNum + ":")) {
        byteCodeStart = Integer.parseInt(javapLine.substring(javapLine.indexOf(": ") + 2));
    }
}

We're doing a couple different things here. First, we are reading the javap output line by line into a list. Second we are creating a map of bytecode line indexes to javap line indexes. This helps us later to determine which method invocation we want to analyze. Finally we are using the known line number from the stack trace to determine which bytecode line index we want to be looking at.

int varLoadIndex = -1;
int varTableIndex = -1;
for (int i = byteCodePointerToJavaPLine.get(byteCodeStart) + 1;i < javapLines.size();i++) {
    if (varLoadIndex < 0 && javapLines.get(i).contains("Method " + methodName + ":")) {
        varLoadIndex = i;
        continue;
    }

    if (varLoadIndex > -1 && javapLines.get(i).contains("LocalVariableTable:")) {
        varTableIndex = i;
        break;
    }
}

Here we are iterating over the javap lines one more time in order to find the spot where our method is being invoked and where the Local Variable Table starts. We need the line where the method is invoked because the line before it contains the call to load the variable and identifies which variable (by index) to load. The Local Variable Table helps us actually look up the name of the variable based on the index we grabbed.

String loadLine = javapLines.get(varLoadIndex - 1).trim();
int varNumber;
try {
    varNumber = Integer.parseInt(loadLine.substring(loadLine.indexOf("aload") + 6).trim());
} catch (NumberFormatException e) {
    return null;
}

This part is actually parsing the load call to get the variable index. This can throw an exception if the function isn't actually called with a variable so we can return null here.

int j = varTableIndex + 2;
while(!"".equals(javapLines.get(j))) {
    Matcher varName = Pattern.compile("\\s*" + varNumber + "\\s*([a-zA-Z_][a-zA-Z0-9_]*)").matcher(javapLines.get(j));  
    if (varName.find()) {
        return varName.group(1);
    }
    j++;
}
return null;

Finally we parse out the name of the variable from the line in the Local Variable Table. Return null if it isn't found although I've seen no reason why this should happen.

Putting it all together

 public static void main(java.lang.String[]);
    Code:
...
      18: getstatic     #19                 // Field java/lang/System.out:Ljava/io/PrintStream;
      21: aload_1
      22: aload_2
      23: invokevirtual #25                 // Method getParamName:(Ljava/lang/String;)Ljava/lang/String;
...
    LineNumberTable:
...
      line 17: 18
      line 18: 29
      line 19: 40
...
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0      83     0  args   [Ljava/lang/String;
          8      75     1     e   LE;
         11      72     2   str   Ljava/lang/String;
         14      69     3  str2   Ljava/lang/String;
         18      65     4  str4   Ljava/lang/String;
         77       5     5    e1   Ljava/lang/Exception;

This is basically what we're looking at. In the example code the first invocation is line 17. line 17 in the LineNumberTable shows that the beginning of that line is bytecode line index 18. That is the System.out load. Then we have aload_2 right before the method call so we look for the variable in slot 2 of the LocalVariableTable which is str in this case.


For fun, here's one which handles multiple function calls on the same line. This causes the function to not be idempotent but that's kind of the point. Try it online!

\$\endgroup\$
5
  • 1
    \$\begingroup\$ Love this answer. Was thinking of something along the same lines. One note though, if you include multiple calls in the same line of the program, then it can't determine which one is calling the method (not sure that this can be fixed with your current approach), for example, try moving the System.out.println(e.getParamName(str));System.out.println(e.getParamName(str2));System.out.println(e.getParamName(str4)); onto one line in the TIO link. \$\endgroup\$
    – PunPun1000
    Commented Jun 15, 2017 at 12:11
  • \$\begingroup\$ You can retrieve the javap location like this: Paths.get(System.getProperty("java.home"), "bin", "javap"). \$\endgroup\$ Commented Jun 15, 2017 at 13:11
  • \$\begingroup\$ @OlivierGrégoire Thanks but I switched to invoking javap through the internal java api so I don't need to get the exact location on disk in the code anymore (just the classpath) \$\endgroup\$
    – Poke
    Commented Jun 16, 2017 at 4:25
  • \$\begingroup\$ @PunPun1000 yeah I'm not sure if there's a nice way to fix that while maintaining an idempotent function but I might put something together that just uses state outside the function for fun. \$\endgroup\$
    – Poke
    Commented Jun 16, 2017 at 4:25
  • 1
    \$\begingroup\$ @PunPun1000 Added a version that can handle multiple calls on one line. It's more than just the function so I just added another TryItOnline link rather than updating the answer. \$\endgroup\$
    – Poke
    Commented Jun 19, 2017 at 13:48
14
\$\begingroup\$

Python 2

This is about the most dirty code I've written but it works. ¯\_(ツ)_/¯ Throws an error on a non-existent variable as Python will immediately dislike you for calling the function with one. Also throws an error on non-variables but this can be fixed with a try/except if needed.

import inspect
import re

def name_of(var):
    for i in inspect.getframeinfo(inspect.currentframe().f_back)[3]:
        return re.search(r'\bname_of\s*\(\s*([A-Za-z_][A-Za-z0-9_]*)\s*\)', i).groups()[0]

Try it online!

If we're allowed to take the argument as a string, this satisfies the requirements of outputting a falsy value on an invalid input.

import inspect
import re

def name_of(var):
    # return var :P

    try:
        eval(var)
    except NameError:
        return False

    for i in inspect.getframeinfo(inspect.currentframe().f_back)[3]:
        try:
            return re.search(r'\bname_of\s*\(\s*[\'"]([A-Za-z_][A-Za-z0-9_]*)[\'"]\s*\)', i).groups()[0]
        except AttributeError:
            return False

Try it online!

\$\endgroup\$
3
  • \$\begingroup\$ You posted almost same answer, while I was typing explanation to mine :D +1 for you \$\endgroup\$ Commented Jun 14, 2017 at 13:56
  • 1
    \$\begingroup\$ @DeadPossum Always post and answer and add an explanation later as an edit. ;) \$\endgroup\$
    – Arjun
    Commented Jun 14, 2017 at 14:32
  • 8
    \$\begingroup\$ @Arjun, oh great so we have to play EditGolf as well as CodeGolf :P \$\endgroup\$
    – Wossname
    Commented Jun 15, 2017 at 9:14
12
\$\begingroup\$

Mathematica

f[x_] := ValueQ @ x && ToString @ HoldForm @ x
SetAttributes[f, HoldFirst]

The HoldFirst attribute prevents f from evaluating its argument before invoking the function. ValueQ @ x then checks whether the given argument is a variable that has been given a value. If not we just return False due to short-circuiting. Otherwise, we obtain the variable name with ToString @ HoldForm @ x.

\$\endgroup\$
8
  • \$\begingroup\$ Dang, abusing how Mathematica handles And... I like how the right argument of And doesn't even have to be a Boolean expression. \$\endgroup\$ Commented Jun 14, 2017 at 13:46
  • \$\begingroup\$ I know it's not code golf, but why not just f[x_] := ValueQ @ x && HoldForm @ x?. If it weren't for the requirement to check if the input is a variable, HoldForm on its own would work. \$\endgroup\$
    – user61980
    Commented Jun 14, 2017 at 22:52
  • 1
    \$\begingroup\$ @ngenisis Because that returns HoldForm[a] and not "a". While they are displayed the same in the Mathematica notebook, the function exists independently of the frontend, so I wanted to have a solution that returns a string. \$\endgroup\$ Commented Jun 16, 2017 at 6:59
  • 1
    \$\begingroup\$ Request to remove whitespace because...um... because \$\endgroup\$ Commented Jun 19, 2017 at 23:37
  • 1
    \$\begingroup\$ @CalculatorFeline request declined. \$\endgroup\$ Commented Jun 20, 2017 at 4:34
7
\$\begingroup\$

Mathematica

f~SetAttributes~HoldAll;
f[_] = False;
f[x_Symbol?ValueQ] := SymbolName@Unevaluated@x;

Mathematica likes to evaluate everything, so to make it stop, we have to fight against it. In its own language.

How?

f~SetAttributes~HoldAll;

Tells Mathematica that whenever function f is called, its arguments shouldn't be evaluated (i.e. held).


f[_] = False;

When f is called with an argument, output False. (?!)


f[x_Symbol?ValueQ] := SymbolName@Unevaluated@x;

When f is called with a defined Symbol (which has a value), output the symbol name of the input.

The previous line does not take precedence, despite being defined earlier, because this definition is more specific. As the Wolfram Documentation states: Mathematica "tries to put specific definitions before more general definitions."

Mathematica is very stubborn and keeps trying to evaluate variables whenever possible. The extra Unevaluated takes care of that.

\$\endgroup\$
0
7
\$\begingroup\$

C#

string n<T>(Expression<Func<T>>m) =>
    (m.Body as MemberExpression)?.Member?.Name ?? null;

Full/Formatted version:

using System;
using System.Linq.Expressions;

class P
{
    static void Main()
    {
        var apple = "Hello";
        var nil = "Nothing";
        var SOMETHING = 69;

        Console.WriteLine(n(() => apple));
        Console.WriteLine(n(() => nil));
        Console.WriteLine(n(() => SOMETHING));
        Console.WriteLine(n(() => 3.14159));

        Console.ReadLine();
    }

    static string n<T>(Expression<Func<T>>m)
        => (m.Body as MemberExpression)?.Member?.Name ?? null;
}

Another way to do this is by reflecting on the type like so:

public static string GetParameterName<T>(T item)
{
    var properties = typeof(T).GetProperties();

    return properties.Length > 0 ? properties[0].Name : null;
}

However, you have to call it like:

GetParameterName(new { apple });

It then also fails for 3.14159 by returning Length and for that you also have to call it like the following instead:

GetParameterName(new double[]{ 3.14159 });

In C# 6.0 we also have:

nameof

But this won't compile if you pass it something that isn't a variable e.g. 3.14159. I believe it also evaluates the input at compile time so it seems like it fails that requirement as well.

\$\endgroup\$
4
  • \$\begingroup\$ This is a bit dodgy... because the lambda stuff is absolutely essential (i.e. you can't just pass in the variable, you have to pass something that the compile will recognise should become an Expression). You should also be counting using System.Linq.Expressions;. \$\endgroup\$ Commented Jun 14, 2017 at 18:50
  • \$\begingroup\$ Does this count as "receiving arguments in abstract syntax tree form" though? \$\endgroup\$ Commented Jun 14, 2017 at 21:53
  • \$\begingroup\$ @VisualMelon I couldn't find any other way to do this in C# so I did it this way. Also this is not code-golf so it doesn't really matter, \$\endgroup\$ Commented Jun 15, 2017 at 8:14
  • \$\begingroup\$ ... sorry, didn't realise there wasn't a byte count! \$\endgroup\$ Commented Jun 15, 2017 at 10:28
6
\$\begingroup\$

MATLAB

varname = @(x) inputname(1);

Within a function there are a few preset variables like varargin or nargin, among those we also have inputname.

stolen from here

\$\endgroup\$
3
  • \$\begingroup\$ I never knew this existed. I played around with this a bit, and perhaps you'll like this gem: x=42;y=54; f=@(x)eval(inputname(1));f(x),f(y) \$\endgroup\$
    – Sanchises
    Commented Jun 23, 2017 at 21:03
  • \$\begingroup\$ Haha, eval==evil =D (it actually works with x=42;y=54; f=@(x)eval('inputname(1)');f(x),f(y)) \$\endgroup\$
    – flawr
    Commented Jun 23, 2017 at 21:07
  • \$\begingroup\$ Yeah, it's just that an anonymous function does not know about any workspace variable, so eval can only evaluate x, and is then eval-ing the function argument, not the workspace variable. \$\endgroup\$
    – Sanchises
    Commented Jun 23, 2017 at 21:10
6
\$\begingroup\$

R

function(x) if (identical(substitute(x), x)) FALSE else substitute(x)

substitute returns the parse tree for an unevaluated expression. The identical conditional makes sure that this unevaluated expression isn't identical to the expression itself; i.e. that the passed in parameter isn't a literal.

\$\endgroup\$
5
\$\begingroup\$

Python 2

Some research has been done. And it seems to be possible in python, but I still expect some troubles to be found.
Solution is not perfect, as it assumes some structure of the code. Yet I didn't break it still.

import inspect
import re

def get_name(var):
    called = inspect.getouterframes(inspect.currentframe())[1][4][0]
    return re.search('(?<=get_name\().*(?=\))', called).group().lstrip().rstrip()

This uses inspect to look at surround scope and find where function get_name is called. For example inspect...[1] will return

(< frame object at 0x01E29030>, 'C:/Users/---/PycharmProjects/CodeGolf/Name_var.py', 14, '< module>', ['print get_name( a )\n'], 0)

And inspect...[1][4] will show us list with code, where function is called:

['print get_name( a )\n']

After that I used regex to retrieve name of the argument

re.search('(?<=get_name\().*(?=\))', called).group()

And .lstrip().rstrip() to remove all whitespaces that may be placed into bracets.

Example with some tricky input

\$\endgroup\$
0
4
\$\begingroup\$

PowerShell

function t($t){(variable($MyInvocation.Line.Split(' $'))[-1]-ea si).Name}

$apple = 'hi'
t $apple
apple

t 3.141

But it doesn't work reliably, it's rudimentary.

\$\endgroup\$
2
  • \$\begingroup\$ This is not code golf. \$\endgroup\$
    – Okx
    Commented Jun 19, 2017 at 9:32
  • \$\begingroup\$ @Okx oh, oops, so it's not. \$\endgroup\$ Commented Jun 19, 2017 at 16:55
4
\$\begingroup\$

PHP

Needs that the variable value is unique in the array of the global variables

function v($i){echo"$".array_search($i,$GLOBALS);}

Try it online!

PHP, 96 bytes

function v($i){
echo($i==end($g=$GLOBALS))?"$".key(array_slice($g,-1)):0;
}
$hello=4;
v($hello);

Try it online!

Needs that the variable is initialized directly to the function call.

Checks if the last global variable is equal surrendering variable

\$\endgroup\$
3
4
\$\begingroup\$

JavaScript (ES6)

For all property names in window (Object.getOwnPropertyNames(w)), attempt to define a getter for that property that returns the property name.

Then, add an entry to a Map M where the key is the (possibly overridden) value of the property, and the value is the name of the property.

The function f simply takes a variable (i.e. a property of window) and returns the entry in M for that value.

let
    w = window,
    M = new Map(),
    L = console.log,
    P = Object.getOwnPropertyNames(w),
    D = Object.defineProperty

for(const p of P){
    try {
        D(w, p, {
            get(){
                return p
            }
        })
    } catch(e){ L(e) }

    try {
        M.set(w[p], p)
    } catch(e){ L(e) }
}

let f = v => M.get(v)

This works for all built-in global variables except window itself, as there is no way to distinguish it from top (unless run in a frame):

L( P.filter(p => p !== f(w[p])) )
// -> ['window']
\$\endgroup\$
3
  • \$\begingroup\$ For some reason, I am getting an error: L not a function. Why would that happen? \$\endgroup\$ Commented Jun 16, 2017 at 17:20
  • \$\begingroup\$ Wow! That's deeper into ES6 than I've seen in a while. \$\endgroup\$
    – MD XF
    Commented Jun 21, 2017 at 16:52
  • \$\begingroup\$ @CalebKleveter D'oh! I forgot a comma on the second line. That may or may not have been problematic. \$\endgroup\$
    – darrylyeo
    Commented Jun 25, 2017 at 3:29
3
\$\begingroup\$

Perl 5 + Devel::Caller

use 5.010;
use Devel::Caller qw/caller_vars/;
use Scalar::Util qw/refaddr/;

sub v {
   # find all operands used in the call, formatted as variable names
   my @varsused = caller_vars(0,1);
   scalar @varsused == 1 or return; # exactly one operand, or return false
   $varsused[0] =~ s/^\$// or return; # the operand actually is a variable
   # it's possible we were given an expression like "~$test" which has only
   # one operand, but is not a variable in its own right; compare memory
   # addresses to work this out
   refaddr \ ($_[0]) == refaddr \ (${$varsused[0]}) or return;
   return '$' . $varsused[0];
}

# some test code

our $test = 1;
our $test2 = 2;
our $test3 = 3;

say v($test2);
say v(2);
say v(~$test3);
say v($test);
say v($test + 1);
say v(++$test);
say v($test3);

We use Devel::Caller (a debugger-like module) to walk the call stack, looking for the call to the function, and return all the operands within the argument, returning them as variable names. If there's more (or less) than one operand, we weren't called with a variable. If the operand wasn't a variable, it won't have a name and we can detect that too.

The trickiest case is if we get a one-operand expression involving a variable, such as ~$x. We can figure out if this has occurred by taking a reference to the variable directly from the symbol table (using the ${…} symbolic reference syntax) and comparing its memory address to the value we were passed as an argument (which is, conveniently, passed by reference). If they're different, we have an expression rather than a lone variable.

Note that if we call this function with a preincrement or predecrement expression on a single variable, as in v(--$x), we get $x returned. This is because it's actually the variable itself that's being passed to the function in this case; it just gets incremented or decremented beforehand. I hope this doesn't disqualify the answer. (In a way, it makes it better, because it shows that we're checking the argument itself rather than just reading the source code.)

\$\endgroup\$
3
\$\begingroup\$

PHP

While the other PHP submissions only test if the given value matches a value of a global, this version works by taking a reference to the value:

// take a reference to the global variable
function f(&$i){
  foreach(array_reverse($GLOBALS) as $key => $value)
    if($key != 'GLOBALS') {
      // Set the value of each global to its name
      $GLOBALS[$key] = $key;
    }

  foreach($GLOBALS as $key => $value)
    if($key != 'GLOBALS' && $key != $value) {
      // The values mismatch so it had a reference to another value
      // we delete it
      unset($GLOBALS[$key]);
      // and update the value to its name again
      $GLOBALS[$key] = $key;
    }

  echo '$', is_array($i) ? 'GLOBALS' : $i, "\n";
}

This should now work even if the global variable is a reference to another value at the time of calling.

Test it here.

\$\endgroup\$
2
  • \$\begingroup\$ Great and Thank You very much for the learning effort \$\endgroup\$ Commented Jun 21, 2017 at 20:18
  • \$\begingroup\$ @JörgHülsermann Even found a way to improve it ! \$\endgroup\$
    – Christoph
    Commented Jun 22, 2017 at 6:03
3
\$\begingroup\$

Röda

f(&a...) {
	a() | name(_) | for str do
		false if [ "<" in str ] else [str]
	done
}

Try it online!

Röda has a builtin function for this – name. Unfortunately, though, it doesn't return a falsy value when not given a variable.

This code abuses several features of reference semantics. Explanation line by line:

f(&a...) {

The function takes a variable number of reference arguments (&a...). This is the only way in Röda to create a list of references.

a() | name(_) | for str do

At the second line the elements of a are pushed to the stream (a()). These elements are references if the function was given variables and normal values otherwise.

The elements are iterated using an underscore loop. The underscore syntax is syntax sugar for for loops, so name(_) is equivalent to name(var) for var. The name of the variable used in the for loop is of form <sfvN> where N varies. (Ie. "name(<sfv1>) for <sfv1>") It is illegal for a user-defined variable to contain < or >, so the generated names don't collide with existing variable names.

The name() function returns the name of the given variable. If the element in a being iterated is a reference, then it is the variable given to name. Otherwise, if the element was a normal value, the variable given to name is the underscore variable <sfvN>. This is due to the semantics of references in Röda: if a function accepts a reference and the function is passed a reference variable, the passed value does not point to the reference variable but to the variable the reference variable points to.

false if [ "<" in str ] else [str]

At the third line we examine if the variable name in the stream contains a < character. If so, it is an underscore variable and the value passed to f was not a reference. Otherwise we output the name of the reference.

This solution does not work if the variable given to the function is a reference or an underscore variable. However, the question specifies that only global variables must be handled, and they can't be references or underscore variables in Röda.

\$\endgroup\$
1
\$\begingroup\$

Ruby, 46 bytes

Feels like the dirtiest code I've ever written for Ruby.

Requires that the global variables you call has a unique value that isn't on any other global variable, because the only way to do this challenge in Ruby is to do an search on all global variables and return the first match. OP says it's ok, and is free to judge if my solution is valid.

Note that global variables start with $ in Ruby, for if you want to add extra test case stuff.

->v{global_variables.find{|n|eval(n.to_s)==v}}

Try it online!

\$\endgroup\$
1
\$\begingroup\$

PHP

function f($v){foreach($GLOBALS as$n=>$x)$x!=$v?:die($n);}

if value is found, print variable name and exit. print nothing and don´t exit else.

61 bytes to return variable name or NULL:

function f($v){foreach($GLOBALS as$n=>$x)if($x==$v)return$n;}

It will not find named functions, only those assigned to variables.
And a PHP function cannot detect wether a parameter was provided by reference or by value. The function will just return the first name where the value matches the function parameter value.

Test it online

\$\endgroup\$
1
\$\begingroup\$

PowerShell

New version but works starting from PowerShell 3.0

function p{[scriptblock]::create($myinvocation.line).ast.findall({$args[0]-is[Management.Automation.Language.VariableExpressionAst]},0)[0]|% v*h|% u*|%{($_,!1)[!$_]}}

Try It Online!

Previous version

function p{$t=[management.automation.psparser]::tokenize($myinvocation.line,[ref]@())|? type -match '^[cv]'|? start|% content;($t,!1)[!$t]}

Try It Online!

\$\endgroup\$
0
\$\begingroup\$

tcl

proc p a {puts [subst $a]}

demo

\$\endgroup\$
0
\$\begingroup\$

JavaScript (ES6)

Requires that the value of the variable passed to the function is unique to that variable. Returns undefined if a variable wasn't passed.

arg=>{
    for(key in this)
        if(this[key]===arg)
            return key
}

Try it

For some reason, it throws a cross-origin error in a Snippet when a "literal" is passed.

var fn=
    arg=>{
        for(key in this)
            if(this[key]===arg)
                return key
    },
str="string",
int=8,
arr=[1,2,3],
obj={a:1}
console.log(fn(fn))
console.log(fn(str))
console.log(fn(int))
console.log(fn(arr))
console.log(fn(obj))


Explanation

Loop through all the entries in the global object (this), checking if the value of each one is strictly equal to the value of the argument passed to the function. If a matching entry is found then its key (name) is returned, exiting the function.


Alternative

With the same requirements as above

arg=>
    [...Object.keys(this)].filter(key=>
        this[key]==arg
    ).pop()
\$\endgroup\$
8
  • 2
    \$\begingroup\$ I think this fails if two globals have the same value. \$\endgroup\$ Commented Jun 14, 2017 at 13:52
  • 1
    \$\begingroup\$ I am getting some very weird output. \$\endgroup\$ Commented Jun 14, 2017 at 13:55
  • \$\begingroup\$ @MartinEnder; yes, it assumes that the value assigned to the variable passed is unique to that variable; forgot to include that when I posted. \$\endgroup\$
    – Shaggy
    Commented Jun 14, 2017 at 14:05
  • \$\begingroup\$ @CalebKleveter; some of those are due to the fact that the value of the variable you're passing isn't unique to that variable and some of it is due to invalid variable names (e.g., hello--). Also, you'd need to use var rather than let. \$\endgroup\$
    – Shaggy
    Commented Jun 14, 2017 at 14:10
  • 1
    \$\begingroup\$ @CalebKleveter, see here for more info on the difference in scoping between let and var. To your second question: That happened because you have a variable named IN_GLOBAL_SCOPE within your global scope and it has a value of 1. You can check the existing variables in your global scope and their values by running this before testing the above: for(x in this)console.log(x+": "+this[x]) \$\endgroup\$
    – Shaggy
    Commented Jun 14, 2017 at 14:53

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