Imprimir o nome de uma variável [fechado]


20

Escreva uma função (não um programa completo), para que, se a função for chamada com uma única variável global (ou o equivalente mais próximo do seu idioma) como argumento, ela produz (ou seja, imprime ou retorna) o nome dessa variável. Se o argumento não for uma variável, envie um valor de falsey. (Você não precisa lidar com o caso em que o argumento é uma variável, mas não global.)

Não deve haver uma conexão em tempo de compilação entre a chamada da função e a definição da função (notavelmente, a definição da função não pode ser uma macro ou construção semelhante que receba argumentos na forma de um fragmento literal do código-fonte, nem na árvore de sintaxe de texto nem abstrata Formato). Ou seja: em linguagens que suportam compilação separada, o programa deve funcionar mesmo que a chamada da função seja compilada primeiro (sem conhecimento da definição da função, mas possivelmente com uma assinatura de tipo ou equivalente), a definição da função será compilada posteriormente. Se o idioma não tiver compilação separada, você deve, no entanto, procurar uma separação semelhante entre a chamada e a definição.

Se você estiver usando uma linguagem compilada, poderá ler a forma compilada do programa completo a partir do disco e presumir que o programa foi compilado com informações de depuração. (Portanto, são permitidas soluções que funcionam anexando um depurador de um programa a si próprio.)

Observe que essa tarefa pode não ser possível em todos os idiomas.

Exemplos:

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

Comentários não são para discussão prolongada; esta conversa foi movida para o bate-papo .
Dennis

Respostas:


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

Atualmente, isso funciona com algumas dicas:

  1. Se você usar um IDE para compilar isso, ele poderá não funcionar, a menos que seja executado como Admin (dependendo de onde os arquivos de classe temporários são salvos)
  2. Você deve compilar usando javaccom a -gbandeira. Isso gera todas as informações de depuração, incluindo nomes de variáveis ​​locais no arquivo de classe compilado.
  3. Isso usa uma API Java interna com.sun.tools.javapque analisa o bytecode de um arquivo de classe e produz um resultado legível por humanos. Essa API é acessível apenas nas bibliotecas JDK, portanto, você deve usar o tempo de execução java JDK ou adicionar tools.jar ao seu caminho de classe.

Agora isso deve funcionar mesmo que o método seja chamado várias vezes no programa. Infelizmente ainda não funciona se você tiver várias chamadas em uma única linha. (Para quem faz, veja abaixo)

Experimente online!


Explicação

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 primeira parte obtém algumas informações gerais sobre em que classe estamos e qual é o nome da função. Isso é feito criando uma exceção e analisando as 2 primeiras entradas do rastreamento de pilha.

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

A primeira entrada é a linha na qual a exceção é lançada, da qual podemos obter o methodName e a segunda entrada é de onde a função foi chamada.

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

Nesta linha, estamos executando o executável javap que acompanha o JDK. Este programa analisa o arquivo de classe (bytecode) e apresenta um resultado legível por humanos. Usaremos isso para "análise" rudimentar.

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 fazendo algumas coisas diferentes aqui. Primeiro, estamos lendo a saída javap linha por linha em uma lista. Segundo, estamos criando um mapa de índices de linha de bytecode para índices de linha javap. Isso nos ajuda mais tarde a determinar qual chamada de método queremos analisar. Finalmente, estamos usando o número de linha conhecido no rastreamento de pilha para determinar qual índice de linha 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;
    }
}

Aqui, estamos repetindo as linhas javap mais uma vez para encontrar o local em que nosso método está sendo chamado e onde a Tabela Variável Local é iniciada. Precisamos da linha onde o método é chamado, porque a linha anterior a ele contém a chamada para carregar a variável e identifica qual variável (por índice) a ser carregada. A Tabela de Variáveis ​​Locais nos ajuda a procurar o nome da variável com base no índice obtido.

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 está realmente analisando a chamada de carregamento para obter o índice da variável. Isso pode gerar uma exceção se a função não for realmente chamada com uma variável, para que possamos retornar nulo aqui.

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, analisamos o nome da variável na linha da Tabela de Variáveis ​​Locais. Retorne nulo se não for encontrado, embora eu não tenha visto nenhuma razão para que isso aconteça.

Juntando tudo

 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;

Isso é basicamente o que estamos vendo. No código de exemplo, a primeira chamada é a linha 17. a linha 17 na LineNumberTable mostra que o início dessa linha é o índice de linha de bytecode 18. Essa é a System.outcarga. Então, temos aload_2um pouco antes da chamada do método, então procuramos a variável no slot 2 da LocalVariableTable, que é strnesse caso.


Por diversão, aqui está uma que lida com várias chamadas de função na mesma linha. Isso faz com que a função não seja idempotente, mas esse é o ponto. Experimente online!


1
Ame esta resposta. Estava pensando em algo na mesma linha. Porém, se você incluir várias chamadas na mesma linha do programa, não será possível determinar qual delas está chamando o método (não tendo certeza de que isso possa ser corrigido com sua abordagem atual), por exemplo, tente passar System.out.println(e.getParamName(str));System.out.println(e.getParamName(str2));System.out.println(e.getParamName(str4));para uma linha no link TIO.
PunPun1000

Você pode recuperar o javaplocal como este: Paths.get(System.getProperty("java.home"), "bin", "javap").
Olivier Grégoire

@ OlivierGrégoire Obrigado, mas mudei para invocar o javap através da API java interna, para que não precise mais obter o local exato no disco no código (apenas o caminho de classe)
Poke

@ PunPun1000: Sim, eu não tenho certeza se existe uma boa maneira de corrigir isso, mantendo uma função idempotente, mas eu posso colocar algo que apenas usa o estado fora da função por diversão.
Puxa

1
@ PunPun1000 Adicionada uma versão que pode lidar com várias chamadas em uma linha. É mais do que apenas a função, então acabei de adicionar outro link TryItOnline em vez de atualizar a resposta.
Junção Poke

14

Python 2

Este é o código mais sujo que eu escrevi, mas funciona. ¯ \ _ (ツ) _ / ¯ Lança um erro em uma variável inexistente, pois o Python imediatamente não gosta de você por chamar a função com uma. Também gera um erro nas não variáveis, mas isso pode ser corrigido com uma tentativa / exceto se necessário.

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]

Experimente online!

Se for permitido aceitar o argumento como uma sequência, isso satisfaz os requisitos de saída de um valor falso em uma entrada invá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

Experimente online!


Você postou quase mesma resposta, enquanto eu estava escrevendo explicação para a minha: D +1 para você
Morto Possum

1
@DeadPossum Sempre poste e responda e adicione uma explicação posteriormente como uma edição. ;)
Arjun

8
@Arjun, oh ótimo, então temos que jogar EditGolf e CodeGolf: P
Wossname 15/17

12

Mathematica

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

O HoldFirstatributo impede fde avaliar seu argumento antes de chamar a função. ValueQ @ xdepois verifica se o argumento fornecido é uma variável que recebeu um valor. Caso contrário, apenas retornamos Falsedevido a curto-circuito. Caso contrário, obteremos o nome da variável com ToString @ HoldForm @ x.


Caramba, abusando de como o Mathematica lida And... Eu gosto de como o argumento certo Andnem precisa ser uma expressão booleana.
JungHwan Min

Eu sei que não é código de golfe, mas por que não apenas f[x_] := ValueQ @ x && HoldForm @ x? Se não fosse o requisito verificar se a entrada é uma variável, HoldFormpor si só funcionaria.
Ngêneseis

HoldAllem vez de HoldFirst?
Greg Martin

1
@ngenisis Porque isso retorna HoldForm[a]e não "a". Enquanto elas são exibidas da mesma forma no bloco de notas do Mathematica, a função existe independentemente do frontend, então eu queria ter uma solução que retornasse uma string.
Martin Ender

1
Pedido para remover espaço em branco porque ... um ... porque
CalculatorFeline

7

Mathematica

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

O Mathematica gosta de avaliar tudo , então, para fazê-lo parar, temos que lutar contra isso. Em seu próprio idioma.

Quão?

f~SetAttributes~HoldAll;

Diz ao Mathematica que sempre que uma função fé chamada, seus argumentos não devem ser avaliados (ou seja, mantidos).


f[_] = False;

Quando fé chamado com um argumento, produz saída False. (?!)


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

Quando fé chamado com um símbolo definido (que possui um valor), imprima o nome do símbolo da entrada.

A linha anterior não tem precedência, apesar de ter sido definida anteriormente, porque essa definição é mais específica. Como a documentação da Wolfram afirma: o Mathematica "tenta colocar definições específicas antes de definições mais gerais".

O Mathematica é muito teimoso e continua tentando avaliar variáveis ​​sempre que possível. O extra Unevaluatedcuida disso.


7

C #

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

Versão completa / formatada:

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

Outra maneira de fazer isso é refletindo sobre o tipo da seguinte maneira:

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

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

No entanto, você deve chamar assim:

GetParameterName(new { apple });

Ele também falha 3.14159ao retornar Lengthe, para isso, você também deve chamá-lo da seguinte maneira:

GetParameterName(new double[]{ 3.14159 });

No C # 6.0, também temos:

nameof

Mas isso não será compilado se você passar algo que não é uma variável, por exemplo 3.14159. Eu acredito que ele também avalia a entrada no tempo de compilação, então parece que também falha nesse requisito.


Isso é um pouco complicado ... porque o material lambda é absolutamente essencial (ou seja, você não pode simplesmente passar a variável, você deve passar algo que a compilação reconhecerá que deve se tornar um Expression). Você também deve estar contando using System.Linq.Expressions;.
VisualMelon

Isso conta como "recebendo argumentos em forma de árvore de sintaxe abstrata"?
Matti Virkkunen

@VisualMelon Não consegui encontrar outra maneira de fazer isso em C #, então fiz dessa maneira. Além disso, este não é o código-golfe para que ele realmente não importa,
TheLethalCoder

... desculpe, não percebi que não havia contagem de bytes!
VisualMelon

6

MATLAB

varname = @(x) inputname(1);

Dentro de uma função, existem algumas variáveis ​​predefinidas como varargin ou nargin, entre aquelas que também temos inputname.

roubado daqui


Eu nunca soube que isso existia. Eu brinquei com isso um pouco, e talvez você vai gostar esta jóia:x=42;y=54; f=@(x)eval(inputname(1));f(x),f(y)
Sanchises

Haha, eval==evil= D (ele realmente funciona com x=42;y=54; f=@(x)eval('inputname(1)');f(x),f(y))
flawr

Sim, é apenas que uma função anônima não conhece nenhuma variável da área de trabalho; portanto, o eval só pode avaliar xe está evalusando o argumento da função, não a variável da área de trabalho.
Sanchises

6

R

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

substituteretorna a árvore de análise para uma expressão não avaliada. O identicalcondicional garante que essa expressão não avaliada não seja idêntica à própria expressão; ou seja, que o parâmetro passado não é literal.


4

Python 2

Alguma pesquisa foi feita. E parece possível em python, mas ainda espero encontrar alguns problemas.
A solução não é perfeita, pois assume alguma estrutura do código. No entanto, eu não o quebrei ainda.

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

Isso usa o inspecionar para examinar o escopo surround e descobrir onde a função get_nameé chamada. Por exemplo inspect...[1], retornará

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

E inspect...[1][4]nos mostrará a lista com o código, onde a função é chamada:

['print get_name( a )\n']

Depois disso, usei o regex para recuperar o nome do argumento

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

E .lstrip().rstrip()para remover todos os espaços em branco que podem ser colocados em braceletes.

Exemplo com alguma entrada complicada


4

PowerShell

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

$apple = 'hi'
t $apple
apple

t 3.141

Mas não funciona de maneira confiável, é rudimentar.


Este não é um código de golfe.
Okx,

@ Ok, oh, oops, então não é.
TessellatingHeckler

4

PHP

Precisa que o valor da variável seja único na matriz das variáveis ​​globais

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

Experimente online!

PHP , 96 bytes

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

Experimente online!

Precisa que a variável seja inicializada diretamente na chamada da função.

Verifica se a última variável global é igual a variável de rendição


Este não é um código de golfe.
Okx,

@ Ok, eu sei No momento, não tenho uma idéia melhor.
Jörg Hülsermann 19/06/19


4

JavaScript (ES6)

Para todos os nomes de propriedades em window(Object.getOwnPropertyNames(w) ), tente definir um getter para essa propriedade que retorne o nome da propriedade.

Em seguida, adicione uma entrada a um mapa M que a chave seja o valor (possivelmente substituído) da propriedade e o valor seja o nome da propriedade.

A função fsimplesmente pega uma variável (isto é, uma propriedade de window) e retorna a entrada Mpara esse 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)

Isso funciona para todas as variáveis ​​globais incorporadas, exceto windowela própria, pois não há como distingui-la top(a menos que seja executada em um quadro):

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

Por alguma razão, eu estou recebendo um erro: L not a function. Por que isso aconteceria?
Caleb Kleveter

Uau! Isso é mais profundo no ES6 do que já vi há algum tempo.
MD XF

@CalebKleveter D'oh! Esqueci uma vírgula na segunda linha. Isso pode ou não ter sido problemático.
darrylyeo

3

Perl 5 + Devel :: Chamador

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

Usamos Devel :: Caller (um módulo semelhante ao depurador) para percorrer a pilha de chamadas, procurando a chamada para a função e retornando todos os operandos no argumento, retornando-os como nomes de variáveis. Se houver mais (ou menos) que um operando, não fomos chamados com uma variável. Se o operando não fosse uma variável, ele não terá um nome e também podemos detectá-lo.

O caso mais complicado é se obtivermos uma expressão de um operando envolvendo uma variável, como ~$x. Podemos descobrir se isso ocorreu fazendo uma referência à variável diretamente da tabela de símbolos (usando o${…} sintaxe de referência simbólica) e comparando seu endereço de memória com o valor que fomos passados ​​como argumento (que é, convenientemente, passado por referência ) Se forem diferentes, temos uma expressão em vez de uma variável solitária.

Observe que, se chamarmos essa função com uma expressão de pré-incremento ou pré-incremento em uma única variável, como em v(--$x), seremos $xretornados. Isso ocorre porque, na verdade, é a própria variável que está sendo passada para a função; apenas é incrementado ou diminuído de antemão. Espero que isso não desqualifique a resposta. (De certa forma, isso melhora, porque mostra que estamos verificando o próprio argumento, em vez de apenas ler o código-fonte.)


3

PHP

Enquanto os outros envios do PHP testam apenas se o valor fornecido corresponde ao valor global, esta versão funciona com uma referência ao 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";
}

Agora, isso deve funcionar mesmo que a variável global seja uma referência a outro valor no momento da chamada.

Teste aqui .


Grande e Muito obrigado pelo esforço de aprendizagem
Jörg Hülsermann

@ JörgHülsermann Até encontrou uma maneira de melhorá-lo!
Christoph

3

Röda

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

Experimente online!

Röda tem uma função embutida para isso - name. Infelizmente, porém, ele não retorna um valor falso quando não recebe uma variável.

Esse código abusa de vários recursos da semântica de referência. Explicação linha por linha:

f(&a...) {

A função recebe um número variável de argumentos de referência ( &a...). Essa é a única maneira no Röda de criar uma lista de referências.

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

Na segunda linha, os elementos de asão empurrados para o fluxo ( a()). Esses elementos são referências se a função recebeu variáveis ​​e valores normais caso contrário.

Os elementos são iterados usando um loop de sublinhado. A sintaxe de sublinhado é açúcar de sintaxe para forloops, portanto name(_)é equivalente a name(var) for var. O nome da variável usada no forloop é da forma em <sfvN>que N varia. (Ie. " name(<sfv1>) for <sfv1>") É ilegal que uma variável definida pelo usuário contenha <ou >, portanto, os nomes gerados não colidem com os nomes de variáveis ​​existentes.

A name()função retorna o nome da variável especificada. Se o elemento em aiteração for uma referência, é a variável fornecida name. Caso contrário, se o elemento for um valor normal, a variável atribuída nameserá a variável sublinhado <sfvN>. Isso ocorre devido à semântica das referências no Röda: se uma função aceita uma referência e a função recebe uma variável de referência, o valor passado não aponta para a variável de referência, mas para a variável para a qual a variável de referência aponta.

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

Na terceira linha, examinamos se o nome da variável no fluxo contém um <caractere. Nesse caso, é uma variável de sublinhado e o valor passado para fnão era uma referência. Caso contrário, emitimos o nome da referência.

Esta solução não funciona se a variável fornecida à função for uma referência ou uma variável de sublinhado. No entanto, a pergunta especifica que apenas variáveis ​​globais devem ser tratadas e não podem ser referências ou sublinhado de variáveis ​​no Röda.


1

Ruby , 46 bytes

Parece o código mais sujo que eu já escrevi para Ruby.

Requer que as variáveis ​​globais que você chama tenham um valor único que não esteja em nenhuma outra variável global, porque a única maneira de fazer esse desafio no Ruby é fazer uma pesquisa em todas as variáveis ​​globais e retornar a primeira correspondência. O OP diz que está tudo bem e é livre para julgar se minha solução é válida.

Observe que as variáveis ​​globais começam com $Ruby, pois se você deseja adicionar itens adicionais de caso de teste.

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

Experimente online!


1

PHP

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

se o valor for encontrado, imprima o nome da variável e saia. imprima nada e não saia mais.

61 bytes para retornar o nome da variável ou NULL:

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

Ele não encontrará funções nomeadas, apenas aquelas atribuídas a variáveis.
E uma função PHP não pode detectar se um parâmetro foi fornecido por referência ou por valor. A função retornará apenas o primeiro nome onde o valor corresponde ao valor do parâmetro da função.

Teste on-line


1

PowerShell

Nova versão, mas funciona a partir do PowerShell 3.0

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

Experimente Online!

Versão anterior

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

Experimente Online!



0

JavaScript (ES6)

Requer que o valor da variável passada para a função seja exclusivo dessa variável. Retorna undefinedse uma variável não foi passada.

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

Tente

Por alguma razão, gera um erro de origem cruzada em um snippet quando um "literal" é passado.

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


Explicação

Faça um loop em todas as entradas do objeto global ( this), verificando se o valor de cada uma é estritamente igual ao valor do argumento passado para a função. Se uma entrada correspondente for encontrada, sua chave (nome) será retornada, saindo da função.


Alternativo

Com os mesmos requisitos acima

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

2
Eu acho que isso falhará se duas globais tiverem o mesmo valor.
Martin Ender

1
Estou recebendo uma saída muito estranha .
Caleb Kleveter

@MartinEnder; sim, assume que o valor atribuído à variável passada é exclusivo para essa variável; esqueci de incluir isso quando eu postei.
Shaggy

@CalebKleveter; algumas delas são devidas ao fato de o valor da variável que você está passando não ser exclusivo dessa variável e outras devido a nomes de variáveis ​​inválidos (por exemplo, hello--). Além disso, você precisaria usar em varvez de let.
Shaggy

1
@ CalebKleveter, veja aqui para mais informações sobre a diferença de escopo entre lete var. Para sua segunda pergunta: isso aconteceu porque você tem uma variável nomeada IN_GLOBAL_SCOPEdentro do seu escopo global e ela tem um valor de1 . Você pode verificar as variáveis ​​existentes no seu escopo global e seus valores executando isso antes de testar o acima:for(x in this)console.log(x+": "+this[x])
Shaggy

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.