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
fonte
Respostas:
Java
Atualmente, isso funciona com algumas dicas:
javac
com a-g
bandeira. Isso gera todas as informações de depuração, incluindo nomes de variáveis locais no arquivo de classe compilado.com.sun.tools.javap
que 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
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.
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.
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.
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.
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.
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.
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
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.out
carga. Então, temosaload_2
um pouco antes da chamada do método, então procuramos a variável no slot 2 da LocalVariableTable, que éstr
nesse 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!
fonte
System.out.println(e.getParamName(str));System.out.println(e.getParamName(str2));System.out.println(e.getParamName(str4));
para uma linha no link TIO.javap
local como este:Paths.get(System.getProperty("java.home"), "bin", "javap")
.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.
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.
Experimente online!
fonte
Mathematica
O
HoldFirst
atributo impedef
de avaliar seu argumento antes de chamar a função.ValueQ @ x
depois verifica se o argumento fornecido é uma variável que recebeu um valor. Caso contrário, apenas retornamosFalse
devido a curto-circuito. Caso contrário, obteremos o nome da variável comToString @ HoldForm @ x
.fonte
And
... Eu gosto de como o argumento certoAnd
nem precisa ser uma expressão booleana.f[x_] := ValueQ @ x && HoldForm @ x
? Se não fosse o requisito verificar se a entrada é uma variável,HoldForm
por si só funcionaria.HoldAll
em vez deHoldFirst
?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.Mathematica
O Mathematica gosta de avaliar tudo , então, para fazê-lo parar, temos que lutar contra isso. Em seu próprio idioma.
Quão?
Diz ao Mathematica que sempre que uma função
f
é chamada, seus argumentos não devem ser avaliados (ou seja, mantidos).Quando
f
é chamado com um argumento, produz saídaFalse
. (?!)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
Unevaluated
cuida disso.fonte
C #
Versão completa / formatada:
Outra maneira de fazer isso é refletindo sobre o tipo da seguinte maneira:
No entanto, você deve chamar assim:
Ele também falha
3.14159
ao retornarLength
e, para isso, você também deve chamá-lo da seguinte maneira:No C # 6.0, também temos:
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.fonte
Expression
). Você também deve estar contandousing System.Linq.Expressions;
.MATLAB
Dentro de uma função, existem algumas variáveis predefinidas como
varargin
ounargin
, entre aquelas que também temosinputname
.roubado daqui
fonte
x=42;y=54; f=@(x)eval(inputname(1));f(x),f(y)
eval==evil
= D (ele realmente funciona comx=42;y=54; f=@(x)eval('inputname(1)');f(x),f(y)
)x
e estáeval
usando o argumento da função, não a variável da área de trabalho.R
substitute
retorna a árvore de análise para uma expressão não avaliada. Oidentical
condicional 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.fonte
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.
Isso usa o inspecionar para examinar o escopo surround e descobrir onde a função
get_name
é chamada. Por exemploinspect...[1]
, retornaráE
inspect...[1][4]
nos mostrará a lista com o código, onde a função é chamada:Depois disso, usei o regex para recuperar o nome do argumento
E
.lstrip().rstrip()
para remover todos os espaços em branco que podem ser colocados em braceletes.Exemplo com alguma entrada complicada
fonte
PowerShell
Mas não funciona de maneira confiável, é rudimentar.
fonte
PHP
Precisa que o valor da variável seja único na matriz das variáveis globais
Experimente online!
PHP , 96 bytes
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
fonte
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
f
simplesmente pega uma variável (isto é, uma propriedade dewindow
) e retorna a entradaM
para esse valor.Isso funciona para todas as variáveis globais incorporadas, exceto
window
ela própria, pois não há como distingui-latop
(a menos que seja executada em um quadro):fonte
L not a function
. Por que isso aconteceria?Perl 5 + Devel :: Chamador
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$x
retornados. 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.)fonte
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:
Agora, isso deve funcionar mesmo que a variável global seja uma referência a outro valor no momento da chamada.
Teste aqui .
fonte
Röda
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
a
sã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
for
loops, portantoname(_)
é equivalente aname(var) for var
. O nome da variável usada nofor
loop é 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 ema
iteração for uma referência, é a variável fornecidaname
. Caso contrário, se o elemento for um valor normal, a variável atribuídaname
será 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 paraf
nã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.
fonte
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.Experimente online!
fonte
PHP
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
: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
fonte
PowerShell
Nova versão, mas funciona a partir do PowerShell 3.0
Experimente Online!
Versão anterior
Experimente Online!
fonte
tcl
demonstração
fonte
JavaScript (ES6)
Requer que o valor da variável passada para a função seja exclusivo dessa variável. Retorna
undefined
se uma variável não foi passada.Tente
Por alguma razão, gera um erro de origem cruzada em um snippet quando um "literal" é passado.
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
fonte
hello--
). Além disso, você precisaria usar emvar
vez delet
.let
evar
. Para sua segunda pergunta: isso aconteceu porque você tem uma variável nomeadaIN_GLOBAL_SCOPE
dentro 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])
Rápido, 45 bytes
fonte
value of type 'Mirror' has no member 'label'