Imprimir el nombre de una variable [cerrado]


20

Escriba una función (no un programa completo), de modo que si la función se llama con una sola variable global (o el equivalente más cercano de su idioma) como argumento, genera (es decir, imprime o devuelve) el nombre de esa variable. Si el argumento no es una variable, genera un valor falsey en su lugar. (No tiene que manejar el caso en el que el argumento es una variable, pero no global).

No debe haber una conexión en tiempo de compilación entre la llamada a la función y la definición de la función (en particular, la definición de la función no puede ser una macro o construcción similar que reciba argumentos en forma de un fragmento literal del código fuente, ni en el texto ni en el árbol de sintaxis abstracta formar). Es decir: en los lenguajes que admiten compilación separada, el programa debe funcionar incluso si la llamada a la función se compila primero (sin conocimiento de la definición de la función pero posiblemente una firma de tipo o equivalente), luego la definición de la función se compila después. Si el lenguaje no tiene una compilación separada, debe buscar una separación similar entre la llamada y la definición.

Si usa un lenguaje compilado, puede leer la forma compilada del programa completo desde el disco, y puede suponer que el programa se compiló con información de depuración. (Por lo tanto, se permiten soluciones que funcionen adjuntando un depurador de un programa a sí mismo).

Tenga en cuenta que esta tarea puede no ser posible en todos los idiomas.

Ejemplos:

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

Los comentarios no son para discusión extendida; Esta conversación se ha movido al chat .
Dennis

Respuestas:


31

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;
}

Esto actualmente funciona con algunas trampas:

  1. Si usa un IDE para compilar esto, podría no funcionar a menos que se ejecute como administrador (dependiendo de dónde se guarden los archivos de clase temporales)
  2. Debe compilar usando javaccon la -gbandera. Esto genera toda la información de depuración, incluidos los nombres de variables locales en el archivo de clase compilado.
  3. Esto utiliza una API Java interna com.sun.tools.javapque analiza el código de bytes de un archivo de clase y produce un resultado legible para humanos. Solo se puede acceder a esta API en las bibliotecas JDK, por lo que debe usar el tiempo de ejecución JDK java o agregar tools.jar a su classpath.

Esto ahora debería funcionar incluso si el método se llama varias veces en el programa. Lamentablemente, todavía no funciona si tiene varias invocaciones en una sola línea. (Para uno que sí, ver abajo)

Pruébalo en línea!


Explicación

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";

Esta primera parte obtiene información general sobre en qué clase estamos y cuál es el nombre de la función. Esto se logra creando una excepción y analizando las 2 primeras entradas de la traza de la pila.

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

La primera entrada es la línea en la que se lanza la excepción en la que podemos tomar el methodName y la segunda entrada es desde donde se llamó a la función.

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

En esta línea, estamos ejecutando el ejecutable javap que viene con el JDK. Este programa analiza el archivo de clase (bytecode) y presenta un resultado legible para humanos. Usaremos esto para el "análisis" rudimentario.

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));
    }
}

Estamos haciendo un par de cosas diferentes aquí. Primero, estamos leyendo la salida de javap línea por línea en una lista. En segundo lugar, estamos creando un mapa de índices de línea de bytecode a índices de línea javap. Esto nos ayuda más adelante a determinar qué método de invocación queremos analizar. Finalmente, estamos utilizando el número de línea conocido de la traza de la pila para determinar qué índice de línea de bytecode queremos ver.

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;
    }
}

Aquí estamos iterando sobre las líneas javap una vez más para encontrar el lugar donde se invoca nuestro método y donde comienza la Tabla de variables locales. Necesitamos la línea donde se invoca el método porque la línea anterior contiene la llamada para cargar la variable e identifica qué variable (por índice) cargar. La tabla de variables locales nos ayuda a buscar el nombre de la variable según el índice que tomamos.

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;
}

Esta parte en realidad está analizando la llamada de carga para obtener el índice variable. Esto puede generar una excepción si la función no se llama realmente con una variable, por lo que podemos devolver nulo aquí.

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;

Finalmente analizamos el nombre de la variable de la línea en la Tabla de variables locales. Devuelva nulo si no se encuentra, aunque no he visto ninguna razón por la que esto debería suceder.

Poniendolo todo junto

 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;

Esto es básicamente lo que estamos viendo. En el código de ejemplo, la primera invocación es la línea 17. la línea 17 en la LineNumberTable muestra que el comienzo de esa línea es el bytecode line index 18. Esa es la System.outcarga. Luego tenemos aload_2justo antes de la llamada al método, por lo que buscamos la variable en la ranura 2 de LocalVariableTable, que es stren este caso.


Por diversión, aquí hay uno que maneja múltiples llamadas a funciones en la misma línea. Esto hace que la función no sea idempotente, pero ese es el punto. Pruébalo en línea!


1
Amo esta respuesta. Estaba pensando en algo en la misma línea. Una nota sin embargo, si se incluyen varias llamadas en la misma línea del programa, entonces no puede determinar qué se está llamando el método (no estoy seguro de que esto se puede solucionar con su enfoque actual), por ejemplo, trate de mover el System.out.println(e.getParamName(str));System.out.println(e.getParamName(str2));System.out.println(e.getParamName(str4));a Una línea en el enlace TIO.
PunPun1000

Puede recuperar la javapubicación de la siguiente manera: Paths.get(System.getProperty("java.home"), "bin", "javap").
Olivier Grégoire

@ OlivierGrégoire Gracias, pero cambié a invocar javap a través de la API interna de Java, por lo que ya no necesito obtener la ubicación exacta en el disco en el código (solo el classpath)
Poke

@ PunPun1000, sí, no estoy seguro de si hay una buena manera de arreglar eso mientras se mantiene una función idempotente, pero podría armar algo que solo use el estado fuera de la función por diversión.
Poke

1
@ PunPun1000 Se agregó una versión que puede manejar múltiples llamadas en una línea. Es más que solo la función, así que acabo de agregar otro enlace TryItOnline en lugar de actualizar la respuesta.
Poke

14

Python 2

Este es el código más sucio que he escrito pero funciona. ¯ \ _ (ツ) _ / ¯ Lanza un error en una variable inexistente ya que Python inmediatamente no le gustará por llamar a la función con una. También arroja un error en no variables, pero esto se puede solucionar con un try / excepto si es necesario.

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]

Pruébalo en línea!

Si se nos permite tomar el argumento como una cadena, esto satisface los requisitos de generar un valor falso en una entrada no válida.

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

Pruébalo en línea!


Publicaste casi la misma respuesta, mientras escribía una explicación para la mía: D +1 para ti
Dead Possum

1
@DeadPossum Siempre publique y responda y agregue una explicación más tarde como edición. ;)
Arjun

8
@Arjun, genial, así que tenemos que jugar EditGolf y CodeGolf: P
Wossname

12

Mathematica

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

El HoldFirstatributo impide fevaluar su argumento antes de invocar la función. ValueQ @ xluego verifica si el argumento dado es una variable a la que se le ha dado un valor. Si no, simplemente regresamos Falsedebido a un cortocircuito. De lo contrario, obtenemos el nombre de la variable con ToString @ HoldForm @ x.


Dang, abusando de cómo maneja Mathematica And... Me gusta cómo el argumento correcto de Andni siquiera tiene que ser una expresión booleana.
JungHwan Min

Sé que no es código golf, pero ¿por qué no solo f[x_] := ValueQ @ x && HoldForm @ x? Si no fuera por el requisito de verificar si la entrada es una variable, HoldFormpor sí sola funcionaría.
ngenisis

HoldAllen lugar de HoldFirst?
Greg Martin

1
@ngenisis Porque eso vuelve HoldForm[a]y no "a". Si bien se muestran de la misma manera en el cuaderno de Mathematica, la función existe independientemente de la interfaz, por lo que quería tener una solución que devuelva una cadena.
Martin Ender

1
Solicitud para eliminar espacios en blanco porque ... um ... porque
CalculatorFeline

7

Mathematica

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

A Mathematica le gusta evaluar todo , así que para detenerlo, tenemos que luchar contra eso. En su propio idioma.

¿Cómo?

f~SetAttributes~HoldAll;

Le dice a Mathematica que cada vez que fse llama a una función , sus argumentos no deben ser evaluados (es decir, retenidos).


f[_] = False;

Cuando fse llama con un argumento, salida False. (?!)


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

Cuando fse llama con un símbolo definido (que tiene un valor), muestra el nombre del símbolo de la entrada.

La línea anterior no tiene prioridad, a pesar de estar definida anteriormente, porque esta definición es más específica. Como dice la documentación de Wolfram: Mathematica "trata de poner definiciones específicas antes que definiciones más generales".

Mathematica es muy terco y sigue intentando evaluar variables siempre que sea posible. El extra Unevaluatedse encarga de eso.


7

C#

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

Versión completa / formateada:

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;
}

Otra forma de hacerlo es reflexionando sobre el tipo de esta manera:

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

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

Sin embargo, debes llamarlo así:

GetParameterName(new { apple });

Luego también falla 3.14159al regresar Lengthy para eso también debe llamarlo de la siguiente manera:

GetParameterName(new double[]{ 3.14159 });

En C # 6.0 también tenemos:

nameof

Pero esto no se compilará si le pasa algo que no sea una variable, por ejemplo 3.14159. Creo que también evalúa la entrada en tiempo de compilación, por lo que parece que también falla ese requisito.


Esto es un poco dudoso ... porque el material lambda es absolutamente esencial (es decir, no puedes pasar la variable, tienes que pasar algo que la compilación reconocerá debería convertirse en un Expression). También deberías estar contando using System.Linq.Expressions;.
VisualMelon

Sin embargo, ¿esto cuenta como "recibir argumentos en forma de árbol de sintaxis abstracta"?
Matti Virkkunen

@VisualMelon No pude encontrar otra forma de hacer esto en C #, así que lo hice de esta manera. También esto no es código de golf por lo que en realidad no importa,
TheLethalCoder

... lo siento, ¡no me di cuenta de que no había un conteo de bytes!
VisualMelon

6

MATLAB

varname = @(x) inputname(1);

Dentro de una función hay algunas variables preestablecidas como varargin o nargin, entre las que también tenemos inputname.

robado de aquí


Nunca supe que esto existía. x=42;y=54; f=@(x)eval(inputname(1));f(x),f(y)
Jugué

Jaja, eval==evil= D (en realidad funciona x=42;y=54; f=@(x)eval('inputname(1)');f(x),f(y))
flawr

Sí, es solo que una función anónima no conoce ninguna variable del espacio de trabajo, por lo que eval solo puede evaluar x, y luego está evalpresionando el argumento de la función, no la variable del espacio de trabajo.
Sanchises

6

R

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

substitutedevuelve el árbol de análisis para una expresión no evaluada. El identicalcondicional se asegura de que esta expresión no evaluada no sea idéntica a la expresión misma; es decir, que el parámetro pasado no es literal.


4

Python 2

Se han realizado algunas investigaciones. Y parece posible en Python, pero aún espero encontrar algunos problemas.
La solución no es perfecta, ya que supone una estructura del código. Sin embargo, no lo rompí todavía.

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()

Esto usa inspeccionar para mirar el alcance envolvente y encontrar dónde get_namese llama la función . Por ejemplo inspect...[1]volverá

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

Y inspect...[1][4]nos mostrará la lista con el código, donde se llama a la función:

['print get_name( a )\n']

Después de eso, usé regex para recuperar el nombre del argumento

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

Y .lstrip().rstrip()para eliminar todos los espacios en blanco que se pueden colocar en brazaletes.

Ejemplo con alguna entrada complicada


4

Potencia Shell

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

$apple = 'hi'
t $apple
apple

t 3.141

Pero no funciona de manera confiable, es rudimentario.


Esto no es código golf.
Okx

@Okx oh, vaya, entonces no lo es.
TessellatingHeckler

4

PHP

Necesita que el valor de la variable sea único en la matriz de las variables globales

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

Pruébalo en línea!

PHP , 96 bytes

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

Pruébalo en línea!

Necesita que la variable se inicialice directamente a la llamada a la función.

Comprueba si la última variable global es igual a la variable de rendición


Esto no es código golf.
Okx

@ Ok, lo sé En este momento no tengo una mejor idea.
Jörg Hülsermann


4

JavaScript (ES6)

Para todos los nombres de propiedad en window( Object.getOwnPropertyNames(w)), intente definir un captador para esa propiedad que devuelva el nombre de la propiedad.

Luego, agregue una entrada a un Mapa Mdonde la clave es el valor (posiblemente anulado) de la propiedad, y el valor es el nombre de la propiedad.

La función fsimplemente toma una variable (es decir, una propiedad de window) y devuelve la entrada Mpara ese valor.

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)

Esto funciona para todas las variables globales integradas, excepto para windowsí mismo, ya que no hay forma de distinguirlo top(a menos que se ejecute en un marco):

L( P.filter(p => p !== f(w[p])) )
// -> ['window']

Por alguna razón, estoy consiguiendo un error: L not a function. ¿Por qué sucedería eso ?
Caleb Kleveter

¡Guauu! Eso es más profundo en ES6 de lo que he visto en mucho tiempo.
MD XF

@CalebKleveter D'oh! Olvidé una coma en la segunda línea. Eso puede o no haber sido problemático.
darrylyeo

3

Perl 5 + Devel :: Llamador

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);

Utilizamos Devel :: Caller (un módulo similar al depurador) para recorrer la pila de llamadas, buscar la llamada a la función y devolver todos los operandos dentro del argumento, devolviéndolos como nombres de variables. Si hay más (o menos) de un operando, no se nos llamó con una variable. Si el operando no era una variable, no tendrá un nombre y también podemos detectarlo.

El caso más complicado es si obtenemos una expresión de un operando que involucra una variable, como ~$x. Podemos determinar si esto ha ocurrido tomando una referencia a la variable directamente de la tabla de símbolos (usando la ${…}sintaxis de referencia simbólica) y comparando su dirección de memoria con el valor que se pasó como argumento (que, convenientemente, se pasa por referencia ) Si son diferentes, tenemos una expresión en lugar de una variable solitaria.

Tenga en cuenta que si llamamos a esta función con una expresión de preincremento o precremento en una sola variable, como en v(--$x), nos $xdevuelve. Esto se debe a que en este caso es la variable en sí la que se pasa a la función; simplemente se incrementa o disminuye de antemano. Espero que esto no descalifique la respuesta. (En cierto modo, lo mejora, porque muestra que estamos comprobando el argumento en sí mismo en lugar de solo leer el código fuente).


3

PHP

Mientras que las otras presentaciones de PHP solo prueban si el valor dado coincide con un valor global, esta versión funciona tomando una referencia al valor:

// 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";
}

Esto debería funcionar ahora incluso si la variable global es una referencia a otro valor en el momento de la llamada.

Pruébalo aquí .


Genial y muchas gracias por el esfuerzo de aprendizaje
Jörg Hülsermann

@ JörgHülsermann ¡Incluso encontró una manera de mejorarlo!
Christoph

3

Röda

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

Pruébalo en línea!

Röda tiene una función integrada para esto - name. Desafortunadamente, sin embargo, no devuelve un valor falso cuando no se le da una variable.

Este código abusa de varias características de la semántica de referencia. Explicación línea por línea:

f(&a...) {

La función toma un número variable de argumentos de referencia ( &a...). Esta es la única forma en Röda para crear una lista de referencias.

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

En la segunda línea, los elementos de ason empujados a la secuencia ( a()). Estos elementos son referencias si a la función se le dieron variables y valores normales de lo contrario.

Los elementos se repiten utilizando un bucle de subrayado. La sintaxis de subrayado es el azúcar de sintaxis para los forbucles, por lo que name(_)es equivalente a name(var) for var. El nombre de la variable utilizada en el forciclo es de forma <sfvN>donde N varía. (Es decir, " name(<sfv1>) for <sfv1>") Es ilegal que una variable definida por el usuario contenga <o >, por lo que los nombres generados no colisionan con los nombres de variables existentes.

La name()función devuelve el nombre de la variable dada. Si el elemento en aiteración es una referencia, entonces es la variable dada a name. De lo contrario, si el elemento era un valor normal, la variable dada namees la variable de subrayado <sfvN>. Esto se debe a la semántica de las referencias en Röda: si una función acepta una referencia y se le pasa una variable de referencia, el valor pasado no apunta a la variable de referencia sino a la variable a la que apunta la variable de referencia.

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

En la tercera línea examinamos si el nombre de la variable en la secuencia contiene un <carácter. Si es así, es una variable de subrayado y el valor pasado fno era una referencia. De lo contrario, mostramos el nombre de la referencia.

Esta solución no funciona si la variable dada a la función es una referencia o una variable de subrayado. Sin embargo, la pregunta especifica que solo se deben manejar las variables globales, y no pueden ser referencias o subrayar variables en Röda.


1

Ruby , 46 bytes

Se siente como el código más sucio que he escrito para Ruby.

Requiere que las variables globales que llame tengan un valor único que no esté en ninguna otra variable global, porque la única forma de hacer este desafío en Ruby es hacer una búsqueda en todas las variables globales y devolver la primera coincidencia. OP dice que está bien, y es libre de juzgar si mi solución es válida.

Tenga en cuenta que las variables globales comienzan $en Ruby, por si desea agregar cosas de casos de prueba adicionales.

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

Pruébalo en línea!


1

PHP

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

si se encuentra el valor, imprima el nombre de la variable y salga. no imprima nada y no salga más.

61 bytes para devolver el nombre de la variable o NULL:

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

No encontrará funciones con nombre, solo aquellas asignadas a variables.
Y una función PHP no puede detectar si un parámetro se proporcionó por referencia o por valor. La función solo devolverá el primer nombre donde el valor coincida con el valor del parámetro de la función.

Pruébelo en línea


1

Potencia Shell

Nueva versión pero funciona a partir de PowerShell 3.0

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

¡Pruébelo en línea!

Versión previa

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

¡Pruébelo en línea!



0

JavaScript (ES6)

Requiere que el valor de la variable pasada a la función sea único para esa variable. Devuelve undefinedsi no se pasó una variable.

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

Intentalo

Por alguna razón, arroja un error de origen cruzado en un fragmento cuando se pasa un "literal".

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))


Explicación

Recorra todas las entradas en el objeto global ( this), verificando si el valor de cada una es estrictamente igual al valor del argumento pasado a la función. Si se encuentra una entrada coincidente, se devuelve su clave (nombre), saliendo de la función.


Alternativa

Con los mismos requisitos que arriba

arg=>
    [...Object.keys(this)].filter(key=>
        this[key]==arg
    ).pop()

2
Creo que esto falla si dos globales tienen el mismo valor.
Martin Ender

1
Estoy obteniendo un resultado muy extraño .
Caleb Kleveter

@MartinEnder; sí, supone que el valor asignado a la variable pasada es único para esa variable; olvidé incluir eso cuando publiqué.
Shaggy

@CalebKleveter; algunos de ellos se deben al hecho de que el valor de la variable que está pasando no es exclusivo de esa variable y parte se debe a nombres de variables no válidos (por ejemplo, hello--). Además, necesitaría usar en varlugar de let.
Shaggy

1
@CalebKleveter, consulte aquí para obtener más información sobre la diferencia en el alcance entre lety var. A su segunda pregunta: Eso sucedió porque tiene una variable nombrada IN_GLOBAL_SCOPEdentro de su alcance global y tiene un valor de 1. Puede verificar las variables existentes en su alcance global y sus valores ejecutando esto antes de probar lo anterior:for(x in this)console.log(x+": "+this[x])
Shaggy

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.