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
fuente
Respuestas:
Java
Esto actualmente funciona con algunas trampas:
javac
con la-g
bandera. Esto genera toda la información de depuración, incluidos los nombres de variables locales en el archivo de clase compilado.com.sun.tools.javap
que 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
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.
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.
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.
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.
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.
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í.
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
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.out
carga. Luego tenemosaload_2
justo antes de la llamada al método, por lo que buscamos la variable en la ranura 2 de LocalVariableTable, que esstr
en 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!
fuente
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.javap
ubicación de la siguiente manera:Paths.get(System.getProperty("java.home"), "bin", "javap")
.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.
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.
Pruébalo en línea!
fuente
Mathematica
El
HoldFirst
atributo impidef
evaluar su argumento antes de invocar la función.ValueQ @ x
luego verifica si el argumento dado es una variable a la que se le ha dado un valor. Si no, simplemente regresamosFalse
debido a un cortocircuito. De lo contrario, obtenemos el nombre de la variable conToString @ HoldForm @ x
.fuente
And
... Me gusta cómo el argumento correcto deAnd
ni siquiera tiene que ser una expresión booleana.f[x_] := ValueQ @ x && HoldForm @ x
? Si no fuera por el requisito de verificar si la entrada es una variable,HoldForm
por sí sola funcionaría.HoldAll
en lugar deHoldFirst
?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.Mathematica
A Mathematica le gusta evaluar todo , así que para detenerlo, tenemos que luchar contra eso. En su propio idioma.
¿Cómo?
Le dice a Mathematica que cada vez que
f
se llama a una función , sus argumentos no deben ser evaluados (es decir, retenidos).Cuando
f
se llama con un argumento, salidaFalse
. (?!)Cuando
f
se 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
Unevaluated
se encarga de eso.fuente
C#
Versión completa / formateada:
Otra forma de hacerlo es reflexionando sobre el tipo de esta manera:
Sin embargo, debes llamarlo así:
Luego también falla
3.14159
al regresarLength
y para eso también debe llamarlo de la siguiente manera:En C # 6.0 también tenemos:
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.fuente
Expression
). También deberías estar contandousing System.Linq.Expressions;
.MATLAB
Dentro de una función hay algunas variables preestablecidas como
varargin
onargin
, entre las que también tenemosinputname
.robado de aquí
fuente
x=42;y=54; f=@(x)eval(inputname(1));f(x),f(y)
eval==evil
= D (en realidad funcionax=42;y=54; f=@(x)eval('inputname(1)');f(x),f(y)
)x
, y luego estáeval
presionando el argumento de la función, no la variable del espacio de trabajo.R
substitute
devuelve el árbol de análisis para una expresión no evaluada. Elidentical
condicional 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.fuente
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.
Esto usa inspeccionar para mirar el alcance envolvente y encontrar dónde
get_name
se llama la función . Por ejemploinspect...[1]
volveráY
inspect...[1][4]
nos mostrará la lista con el código, donde se llama a la función:Después de eso, usé regex para recuperar el nombre del argumento
Y
.lstrip().rstrip()
para eliminar todos los espacios en blanco que se pueden colocar en brazaletes.Ejemplo con alguna entrada complicada
fuente
Potencia Shell
Pero no funciona de manera confiable, es rudimentario.
fuente
PHP
Necesita que el valor de la variable sea único en la matriz de las variables globales
Pruébalo en línea!
PHP , 96 bytes
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
fuente
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
M
donde la clave es el valor (posiblemente anulado) de la propiedad, y el valor es el nombre de la propiedad.La función
f
simplemente toma una variable (es decir, una propiedad dewindow
) y devuelve la entradaM
para ese valor.Esto funciona para todas las variables globales integradas, excepto para
window
sí mismo, ya que no hay forma de distinguirlotop
(a menos que se ejecute en un marco):fuente
L not a function
. ¿Por qué sucedería eso ?Perl 5 + Devel :: Llamador
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$x
devuelve. 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).fuente
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:
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í .
fuente
Röda
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
a
son 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
for
bucles, por lo quename(_)
es equivalente aname(var) for var
. El nombre de la variable utilizada en elfor
ciclo 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 ena
iteración es una referencia, entonces es la variable dada aname
. De lo contrario, si el elemento era un valor normal, la variable dadaname
es 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 pasadof
no 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.
fuente
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.Pruébalo en línea!
fuente
PHP
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
: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
fuente
Potencia Shell
Nueva versión pero funciona a partir de PowerShell 3.0
¡Pruébelo en línea!
Versión previa
¡Pruébelo en línea!
fuente
tcl
manifestación
fuente
JavaScript (ES6)
Requiere que el valor de la variable pasada a la función sea único para esa variable. Devuelve
undefined
si no se pasó una variable.Intentalo
Por alguna razón, arroja un error de origen cruzado en un fragmento cuando se pasa un "literal".
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
fuente
hello--
). Además, necesitaría usar envar
lugar delet
.let
yvar
. A su segunda pregunta: Eso sucedió porque tiene una variable nombradaIN_GLOBAL_SCOPE
dentro de su alcance global y tiene un valor de1
. 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])
Rápido, 45 bytes
fuente
value of type 'Mirror' has no member 'label'