Stampa il nome di una variabile [chiuso]


20

Scrivi una funzione (non un programma completo), in modo che se la funzione viene chiamata con una singola variabile globale (o l'equivalente più vicino della tua lingua) come argomento, genera (cioè stampa o restituisce) il nome di quella variabile. Se l'argomento non è una variabile, genera invece un valore false. (Non è necessario gestire il caso in cui l'argomento è una variabile, ma non globale.)

Non deve esserci una connessione in fase di compilazione tra la chiamata della funzione e la definizione della funzione (in particolare, la definizione della funzione non può essere una macro o un costrutto simile che riceve argomenti sotto forma di un frammento letterale del codice sorgente, né nel testo né nell'albero di sintassi astratto modulo). Cioè: nelle lingue che supportano la compilazione separata, il programma deve funzionare anche se la chiamata della funzione viene compilata per prima (senza conoscenza della definizione della funzione ma possibilmente una firma del tipo o equivalente), quindi la definizione della funzione viene successivamente compilata. Se la lingua non ha una compilation separata, è comunque necessario puntare a una separazione simile tra la chiamata e la definizione.

Se si utilizza un linguaggio compilato, è possibile leggere il modulo compilato del programma completo dal disco e assumere che il programma sia stato compilato con le informazioni di debug. (Pertanto, sono consentite soluzioni che funzionano collegando un debugger da un programma a se stesso.)

Si noti che questa attività potrebbe non essere possibile in tutte le lingue.

Esempi:

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

I commenti non sono per una discussione estesa; questa conversazione è stata spostata in chat .
Dennis,

Risposte:


31

Giava

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

Questo attualmente funziona con alcuni gotcha:

  1. Se si utilizza un IDE per compilarlo, potrebbe non funzionare a meno che non venga eseguito come amministratore (a seconda della posizione in cui vengono salvati i file di classe temporanei)
  2. È necessario compilare utilizzando javaccon il -gflag. Ciò genera tutte le informazioni di debug inclusi i nomi delle variabili locali nel file di classe compilato.
  3. Questo utilizza un'API Java interna com.sun.tools.javapche analizza il bytecode di un file di classe e produce un risultato leggibile dall'uomo. Questa API è accessibile solo nelle librerie JDK, quindi è necessario utilizzare il runtime java JDK o aggiungere tools.jar al percorso di classe.

Ora dovrebbe funzionare anche se il metodo viene chiamato più volte nel programma. Sfortunatamente non funziona ancora se hai più invocazioni su una sola riga. (Per uno che lo fa, vedi sotto)

Provalo online!


Spiegazione

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

Questa prima parte ottiene alcune informazioni generali su quale classe ci troviamo e qual è il nome della funzione. Ciò si ottiene creando un'eccezione e analizzando le prime 2 voci della traccia dello stack.

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

La prima voce è la riga da cui viene generata l'eccezione da cui è possibile catturare methodName e la seconda voce è da dove è stata chiamata la funzione.

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

In questa linea stiamo eseguendo l'eseguibile javap fornito con JDK. Questo programma analizza il file di classe (bytecode) e presenta un risultato leggibile dall'uomo. Lo useremo per un "analisi" rudimentale.

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

Stiamo facendo un paio di cose diverse qui. Innanzitutto, stiamo leggendo l'output javap riga per riga in un elenco. In secondo luogo stiamo creando una mappa di indici di linea bytecode per gli indici di linea javap. Questo ci aiuta in seguito a determinare quale metodo di chiamata vogliamo analizzare. Finalmente stiamo usando il numero di riga noto dalla traccia dello stack per determinare quale indice di linea bytecode vogliamo vedere.

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

Qui stiamo ripetendo le righe javap ancora una volta per trovare il punto in cui viene invocato il nostro metodo e dove inizia la tabella delle variabili locali. Abbiamo bisogno della linea in cui viene invocato il metodo perché la riga prima contiene la chiamata per caricare la variabile e identifica quale variabile (per indice) caricare. La tabella delle variabili locali ci aiuta effettivamente a cercare il nome della variabile in base all'indice che abbiamo acquisito.

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

Questa parte sta effettivamente analizzando la chiamata di caricamento per ottenere l'indice variabile. Questo può generare un'eccezione se la funzione non è effettivamente chiamata con una variabile, quindi possiamo restituire null qui.

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;

Infine analizziamo il nome della variabile dalla riga nella tabella delle variabili locali. Restituisce null se non viene trovato, anche se non ho visto alcun motivo per cui ciò dovrebbe accadere.

Mettere tutto insieme

 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;

Questo è fondamentalmente quello che stiamo guardando. Nel codice di esempio la prima chiamata è la riga 17. la riga 17 nella tabella LineNumber mostra che l'inizio di quella riga è l'indice della riga bytecode 18. Questo è il System.outcarico. Quindi abbiamo aload_2subito prima della chiamata del metodo, quindi cerchiamo la variabile nello slot 2 della LocalVariableTable che è strin questo caso.


Per divertimento, eccone uno che gestisce più chiamate di funzione sulla stessa linea. Questo fa sì che la funzione non sia idempotente, ma questo è il punto. Provalo online!


1
Adoro questa risposta. Stava pensando a qualcosa sulla stessa linea. Una nota, però, se si include chiamate multiple nella stessa riga del programma, allora non può determinare quale è la chiamata al metodo (non sono sicuro che questo può essere risolto con il tuo approccio attuale), per esempio, provare a spostare il System.out.println(e.getParamName(str));System.out.println(e.getParamName(str2));System.out.println(e.getParamName(str4));su una riga nel collegamento TIO.
PunPun1000,

È possibile recuperare la javapposizione come questa: Paths.get(System.getProperty("java.home"), "bin", "javap").
Olivier Grégoire,

@ OlivierGrégoire Grazie, ma sono passato a invocare javap attraverso l'API Java interno, quindi non ho più bisogno di ottenere la posizione esatta sul disco nel codice (solo il percorso di classe)
Colpisci il

@ PunPun1000 sì, non sono sicuro che ci sia un buon modo per risolverlo mantenendo una funzione idempotente, ma potrei mettere insieme qualcosa che usa semplicemente lo stato al di fuori della funzione per divertimento.
Colpisci il

1
@ PunPun1000 Aggiunta una versione in grado di gestire più chiamate su una linea. È più di una semplice funzione, quindi ho appena aggiunto un altro link TryItOnline anziché aggiornare la risposta.
Colpisci il

14

Python 2

Si tratta del codice più sporco che ho scritto ma funziona. ¯ \ _ (ツ) _ / ¯ Genera un errore su una variabile inesistente poiché Python non ti apprezzerà immediatamente per aver chiamato la funzione con una. Inoltre genera un errore su non variabili, ma questo può essere risolto con un tentativo / tranne se necessario.

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]

Provalo online!

Se ci è permesso di prendere l'argomento come una stringa, questo soddisfa i requisiti di emissione di un valore errato su un input non valido.

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

Provalo online!


Hai pubblicato quasi la stessa risposta, mentre scrivevo una spiegazione per la mia: D +1 per te
Dead Possum

1
@DeadPossum Pubblica e rispondi sempre e aggiungi una spiegazione in seguito come modifica. ;)
Arjun,

8
@Arjun, oh, quindi dobbiamo giocare a EditGolf e CodeGolf: P
Wossname

12

matematica

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

L' HoldFirstattributo impedisce fdi valutare il suo argomento prima di invocare la funzione. ValueQ @ xquindi controlla se l'argomento dato è una variabile a cui è stato assegnato un valore. Altrimenti torniamo solo Falseper corto circuito. Altrimenti, otteniamo il nome della variabile con ToString @ HoldForm @ x.


Dang, abusando del modo in cui Mathematica gestisce And... Mi piace come l'argomento giusto Andnon debba nemmeno essere un'espressione booleana.
JungHwan Min

So che non è il golf del codice, ma perché non solo f[x_] := ValueQ @ x && HoldForm @ x? Se non fosse per l'obbligo di verificare se l'input è una variabile, funzionerebbe HoldFormda solo.
ngenisis,

HoldAllinvece di HoldFirst?
Greg Martin,

1
@ngenisis Perché questo ritorna HoldForm[a]e no "a". Mentre sono visualizzati allo stesso modo nel notebook Mathematica, la funzione esiste indipendentemente dal frontend, quindi volevo avere una soluzione che restituisse una stringa.
Martin Ender,

1
Richiesta di rimozione degli spazi bianchi perché ... um ... because
CalculatorFeline

7

matematica

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

A Mathematica piace valutare tutto , quindi per farlo smettere, dobbiamo lottare contro di esso. Nella sua lingua.

Come?

f~SetAttributes~HoldAll;

Dice a Mathematica che ogni volta che fviene chiamata la funzione , i suoi argomenti non dovrebbero essere valutati (cioè mantenuti).


f[_] = False;

Quando fviene chiamato con un argomento, output False. (?!)


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

Quando fviene chiamato con un simbolo definito (che ha un valore), emette il nome del simbolo dell'input.

La riga precedente non ha la precedenza, nonostante sia stata definita in precedenza, perché questa definizione è più specifica. Come afferma la documentazione di Wolfram: Mathematica "cerca di mettere definizioni specifiche prima di definizioni più generali".

Mathematica è molto testarda e continua a cercare di valutare le variabili ogni volta che è possibile. L'extra se Unevaluatedne occupa.


7

C #

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

Versione completa / formattata:

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

Un altro modo per farlo è riflettere sul tipo in questo modo:

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

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

Tuttavia, devi chiamarlo come:

GetParameterName(new { apple });

Quindi fallisce anche 3.14159tornando Lengthe per questo devi anche chiamarlo come il seguente:

GetParameterName(new double[]{ 3.14159 });

In C # 6.0 abbiamo anche:

nameof

Ma questo non verrà compilato se gli passi qualcosa che non è una variabile, ad es 3.14159. Credo che valuti anche l'input in fase di compilazione, quindi sembra che non soddisfi anche questo requisito.


Questo è un po 'complicato ... perché la roba lambda è assolutamente essenziale (cioè non puoi semplicemente passare nella variabile, devi passare qualcosa che la compilazione riconoscerà dovrebbe diventare una Expression). Dovresti anche contare using System.Linq.Expressions;.
VisualMelon

Conta questo come "ricezione di argomenti in forma di albero di sintassi astratta"?
Matti Virkkunen,

@VisualMelon Non ho trovato altro modo per farlo in C #, quindi l'ho fatto in questo modo. Anche questo non è un golf di codice, quindi non importa davvero,
TheLethalCoder

... scusa, non avevo capito che non c'era un conteggio di byte!
VisualMelon,

6

MATLAB

varname = @(x) inputname(1);

All'interno di una funzione ci sono alcune variabili preimpostate come varargin o nargin, tra quelle che abbiamo anche inputname.

rubato da qui


Non ho mai saputo che esistesse. Ci ho giocato un po 'e forse ti piacerà questo gioiello:x=42;y=54; f=@(x)eval(inputname(1));f(x),f(y)
Sanchises,

Haha, eval==evil= D (funziona davvero con x=42;y=54; f=@(x)eval('inputname(1)');f(x),f(y))
flawr

Sì, è solo che una funzione anonima non conosce alcuna variabile dell'area di lavoro, quindi eval può solo valutare x, e lo èeval dell'area di lavoro usando l'argomento della funzione, non la variabile dell'area di lavoro.
Sanchises,

6

R

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

substituterestituisce l'albero di analisi per un'espressione non valutata. Il identicalcondizionale si assicura che questa espressione non valutata non sia identica all'espressione stessa; cioè che il parametro passato non è letterale.


4

Python 2

Alcune ricerche sono state fatte. E sembra essere possibile in Python, ma mi aspetto ancora che si possano trovare dei problemi.
La soluzione non è perfetta, poiché presuppone una struttura del codice. Eppure non l'ho ancora rotto.

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

Questo usa inspect per guardare l'ambito surround e trovare dove get_nameviene chiamata la funzione . Ad esempio inspect...[1]tornerà

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

E inspect...[1][4]ci mostrerà l'elenco con il codice, dove viene chiamata la funzione:

['print get_name( a )\n']

Successivamente ho usato regex per recuperare il nome dell'argomento

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

E .lstrip().rstrip() per rimuovere tutti gli spazi bianchi che possono essere inseriti in braccialetti.

Esempio con alcuni input complicati


4

PowerShell

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

$apple = 'hi'
t $apple
apple

t 3.141

Ma non funziona in modo affidabile, è rudimentale.


Questo non è un codice golf.
Okx,

@Okx oh, oops, quindi non lo è.
TessellatingHeckler,

4

PHP

È necessario che il valore della variabile sia univoco nella matrice delle variabili globali

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

Provalo online!

PHP , 96 byte

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

Provalo online!

È necessario che la variabile sia inizializzata direttamente alla chiamata di funzione.

Verifica se l'ultima variabile globale è uguale alla variabile di resa


Questo non è un codice golf.
Okx,

@Okx lo so Al momento non ho idea migliore.
Jörg Hülsermann,


4

JavaScript (ES6)

Per tutti i nomi di proprietà tra window( Object.getOwnPropertyNames(w)), provare a definire un getter per quella proprietà che restituisce il nome della proprietà.

Quindi, aggiungi una voce a una mappa M cui la chiave è il valore (possibilmente sovrascritto) della proprietà e il valore è il nome della proprietà.

La funzione fprende semplicemente una variabile (cioè una proprietà di window) e restituisce la voce Mper quel valore.

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)

Funziona con tutte le variabili globali incorporate ad eccezione di se windowstesso, in quanto non esiste alcun modo per distinguerlo da top(se non eseguito in un frame):

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

Per qualche ragione, sto ottenendo un errore: L not a function. Perché sarebbe che succederà?
Caleb Kleveter,

Wow! È più profondo in ES6 di quanto non abbia visto da un po '.
MD XF,

@CalebKleveter D'oh! Ho dimenticato una virgola sulla seconda riga. Ciò può o non può essere stato problematico.
darrylyeo,

3

Perl 5 + Devel :: Caller

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

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

# some test code

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

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

Usiamo Devel :: Caller (un modulo simile al debugger) per eseguire lo stack di chiamate, cercare la chiamata alla funzione e restituire tutti gli operandi all'interno dell'argomento, restituendoli come nomi di variabili. Se c'è più (o meno) di un operando, non siamo stati chiamati con una variabile. Se l'operando non era una variabile, non avrebbe un nome e possiamo rilevarlo anche noi.

Il caso più difficile è se otteniamo un'espressione di un operando che coinvolge una variabile, ad esempio ~$x. Possiamo capire se ciò si è verificato prendendo un riferimento alla variabile direttamente dalla tabella dei simboli (usando la ${…}sintassi del riferimento simbolico) e confrontando il suo indirizzo di memoria con il valore che ci è stato passato come argomento (che è, convenientemente, passato per riferimento ). Se sono diversi, abbiamo un'espressione piuttosto che una variabile solitaria.

Nota che se chiamiamo questa funzione con un'espressione preincrement o predecrement su una singola variabile, come in v(--$x), verremo $xrestituiti. Questo perché in realtà è la variabile stessa che viene passata alla funzione in questo caso; viene semplicemente incrementato o decrementato in anticipo. Spero che questo non squalizzi la risposta. (In un certo senso, lo rende migliore, perché dimostra che stiamo controllando l'argomento stesso piuttosto che leggere solo il codice sorgente.)


3

PHP

Mentre gli altri invii PHP verificano solo se il valore dato corrisponde a un valore globale, questa versione funziona prendendo un riferimento al valore:

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

Ora dovrebbe funzionare anche se la variabile globale è un riferimento a un altro valore al momento della chiamata.

Provalo qui .


Grande e grazie mille per lo sforzo di apprendimento
Jörg Hülsermann,

@ JörgHülsermann Anche trovato un modo per migliorarlo!
Christoph,

3

Röda

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

Provalo online!

Röda ha una funzione integrata per questo - name. Sfortunatamente, tuttavia, non restituisce un valore errato quando non viene fornita una variabile.

Questo codice abusa di diverse funzionalità della semantica di riferimento. Spiegazione riga per riga:

f(&a...) {

La funzione accetta un numero variabile di argomenti di riferimento (&a... ). Questo è l'unico modo in Röda per creare un elenco di riferimenti.

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

Alla seconda riga gli elementi di a vengono spinti nel flusso ( a()). Questi elementi sono riferimenti se alla funzione sono state fornite variabili e valori normali in caso contrario.

Gli elementi vengono ripetuti utilizzando un ciclo di trattino basso. La sintassi di sottolineatura è zucchero di sintassi per i forloop, quindi name(_)è equivalente a name(var) for var. Il nome della variabile utilizzata nel forciclo è di forma<sfvN> cui N varia. (Ie. " name(<sfv1>) for <sfv1>") È illegale contenere una variabile definita dall'utente< o >, quindi i nomi generati non si scontrano con i nomi delle variabili esistenti.

La name()funzione restituisce il nome della variabile specificata. Se l'elemento in aiterazione è un riferimento, allora è la variabile assegnata name. Altrimenti, se l'elemento era un valore normale, la variabile data aname è la variabile di sottolineatura<sfvN> . Ciò è dovuto alla semantica dei riferimenti in Röda: se una funzione accetta un riferimento e alla funzione viene passata una variabile di riferimento, il valore passato non punta alla variabile di riferimento ma alla variabile alla quale punta la variabile di riferimento.

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

Alla terza riga esaminiamo se il nome della variabile nello stream contiene a < carattere. In tal caso, si tratta di una variabile di sottolineatura e il valore passato fnon era un riferimento. Altrimenti viene emesso il nome del riferimento.

Questa soluzione non funziona se la variabile assegnata alla funzione è un riferimento o una variabile di sottolineatura. Tuttavia, la domanda specifica che devono essere gestite solo variabili globali e che non possono essere riferimenti o caratteri di sottolineatura in Röda.


1

Rubino , 46 byte

Sembra il codice più sporco che abbia mai scritto per Ruby.

Richiede che le variabili globali che chiami abbiano un valore univoco che non si trova su nessuna altra variabile globale, perché l'unico modo per fare questa sfida in Ruby è fare una ricerca su tutte le variabili globali e restituire la prima corrispondenza. OP dice che va bene ed è libero di giudicare se la mia soluzione è valida.

Si noti che le variabili globali iniziano con $in Ruby, perché se si desidera aggiungere ulteriori elementi di test case.

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

Provalo online!


1

PHP

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

se viene trovato un valore, stampa il nome della variabile ed esce. non stampare nulla e non uscire altro.

61 byte per restituire il nome della variabile o NULL:

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

Non troverà funzioni nominate, ma solo quelle assegnate alle variabili.
E una funzione PHP non può rilevare se un parametro è stato fornito per riferimento o per valore. La funzione restituirà semplicemente il nome in cui il valore corrisponde al valore del parametro della funzione.

Provalo online


1

PowerShell

Nuova versione ma funziona a partire da PowerShell 3.0

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

Provalo online!

Versione precedente

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

Provalo online!



0

JavaScript (ES6)

Richiede che il valore della variabile passato alla funzione sia univoco per quella variabile. Restituisce undefinedse non è stata passata una variabile.

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

Provalo

Per qualche motivo, genera un errore di origine incrociata in uno snippet quando viene passato un "valore letterale".

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


Spiegazione

Scorri tutte le voci dell'oggetto globale ( this), controllando se il valore di ognuna è strettamente uguale al valore dell'argomento passato alla funzione. Se viene trovata una voce corrispondente, viene restituita la chiave (nome), uscendo dalla funzione.


Alternativa

Con gli stessi requisiti di cui sopra

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

2
Penso che ciò fallisca se due globi hanno lo stesso valore.
Martin Ender,

1
Sto ottenendo un output molto strano .
Caleb Kleveter,

@MartinEnder; sì, presuppone che il valore assegnato alla variabile passata sia univoco per quella variabile; dimenticato di includerlo quando ho pubblicato.
Shaggy,

@CalebKleveter; alcuni di questi sono dovuti al fatto che il valore della variabile che stai passando non è univoco per quella variabile e alcuni è dovuto a nomi di variabili non validi (ad es hello--.). Inoltre, dovresti usare varpiuttosto che let.
Shaggy,

1
@CalebKleveter, vedi qui per maggiori informazioni sulla differenza di scoping tra lete var. Alla tua seconda domanda: è successo perché hai una variabile nominata IN_GLOBAL_SCOPEnel tuo ambito globale e ha un valore di 1. Puoi controllare le variabili esistenti nel tuo ambito globale e i loro valori eseguendo questo prima di testare quanto sopra:for(x in this)console.log(x+": "+this[x])
Shaggy

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.