Imprimer le nom d'une variable [fermé]

20

Écrivez une fonction (pas un programme complet), de sorte que si la fonction est appelée avec une seule variable globale (ou l'équivalent le plus proche de votre langue) comme argument, elle génère (c'est-à-dire imprime ou renvoie) le nom de cette variable. Si l'argument n'est pas une variable, affichez une valeur de falsey à la place. (Vous n'avez pas à gérer le cas où l'argument est une variable, mais pas globale.)

Il ne doit pas y avoir de connexion au moment de la compilation entre l'appel de fonction et la définition de fonction (notamment, la définition de fonction ne peut pas être une macro ou une construction similaire qui reçoit des arguments sous la forme d'un fragment littéral de code source, ni dans le texte ni dans l'arbre de syntaxe abstrait forme). C'est-à-dire: dans les langages qui prennent en charge la compilation séparée, le programme doit fonctionner même si l'appel de fonction est compilé en premier (sans connaissance de la définition de fonction mais éventuellement d'une signature de type ou équivalent), la définition de fonction est ensuite compilée. Si la langue n'a pas de compilation séparée, vous devez néanmoins viser une séparation similaire entre l'appel et la définition.

Si vous utilisez un langage compilé, vous pouvez lire la forme compilée du programme complet à partir du disque et supposer que le programme a été compilé avec des informations de débogage. (Ainsi, les solutions qui fonctionnent en attachant un débogueur d'un programme à lui-même sont autorisées.)

Notez que cette tâche peut ne pas être possible dans toutes les langues.

Exemples:

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
Caleb Kleveter
la source
Les commentaires ne sont pas pour une discussion approfondie; cette conversation a été déplacée vers le chat .
Dennis

Réponses:

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

Cela fonctionne actuellement avec quelques pièges:

  1. Si vous utilisez un IDE pour le compiler, il peut ne pas fonctionner à moins qu'il ne soit exécuté en tant qu'administrateur (selon l'endroit où les fichiers de classe temporaires sont enregistrés)
  2. Vous devez compiler à l'aide javacde l' -gindicateur. Cela génère toutes les informations de débogage, y compris les noms de variables locales dans le fichier de classe compilé.
  3. Cela utilise une API Java interne com.sun.tools.javapqui analyse le bytecode d'un fichier de classe et produit un résultat lisible par l'homme. Cette API n'est accessible que dans les bibliothèques JDK, vous devez donc utiliser le runtime JDK java ou ajouter tools.jar à votre chemin de classe.

Cela devrait maintenant fonctionner même si la méthode est appelée plusieurs fois dans le programme. Malheureusement, cela ne fonctionne pas encore si vous avez plusieurs appels sur une seule ligne. (Pour celui qui le fait, voir ci-dessous)

Essayez-le en ligne!


Explication

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

Cette première partie contient des informations générales sur la classe dans laquelle nous nous trouvons et le nom de la fonction. Pour ce faire, créez une exception et analysez les 2 premières entrées de la trace de pile.

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

La première entrée est la ligne d'où l'exception est levée sur laquelle nous pouvons saisir le methodName et la deuxième entrée est l'endroit d'où la fonction a été appelée.

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

Dans cette ligne, nous exécutons l'exécutable javap fourni avec le JDK. Ce programme analyse le fichier de classe (bytecode) et présente un résultat lisible par l'homme. Nous l'utiliserons pour une "analyse" rudimentaire.

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

Nous faisons deux choses différentes ici. Tout d'abord, nous lisons la sortie javap ligne par ligne dans une liste. Deuxièmement, nous créons une carte des index de ligne de bytecode aux index de ligne javap. Cela nous aide plus tard à déterminer quelle invocation de méthode nous voulons analyser. Enfin, nous utilisons le numéro de ligne connu de la trace de pile pour déterminer quel index de ligne de bytecode nous voulons regarder.

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

Ici, nous parcourons les lignes javap une fois de plus afin de trouver l'endroit où notre méthode est invoquée et où commence la table de variables locales. Nous avons besoin de la ligne où la méthode est invoquée car la ligne avant elle contient l'appel pour charger la variable et identifie la variable (par index) à charger. La table des variables locales nous aide à rechercher le nom de la variable en fonction de l'index que nous avons récupéré.

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

Cette partie analyse réellement l'appel de chargement pour obtenir l'index des variables. Cela peut lever une exception si la fonction n'est pas réellement appelée avec une variable afin que nous puissions retourner null ici.

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;

Enfin, nous analysons le nom de la variable à partir de la ligne de la table des variables locales. Renvoie null s'il n'est pas trouvé, même si je n'ai vu aucune raison pour laquelle cela devrait se produire.

Mettre tous ensemble

 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;

C'est essentiellement ce que nous examinons. Dans l'exemple de code, la première invocation est la ligne 17. La ligne 17 dans le LineNumberTable montre que le début de cette ligne est l'index de la ligne de bytecode 18. C'est la System.outcharge. Ensuite, nous avons aload_2juste avant l'appel de la méthode, nous recherchons donc la variable dans l'emplacement 2 du LocalVariableTable qui est strdans ce cas.


Pour le plaisir, en voici un qui gère plusieurs appels de fonction sur la même ligne. Cela fait que la fonction n'est pas idempotente mais c'est un peu le point. Essayez-le en ligne!

Poussée
la source
1
J'adore cette réponse. Pensait à quelque chose dans le même sens. Cependant, si vous incluez plusieurs appels dans la même ligne du programme, il ne peut pas déterminer lequel appelle la méthode (pas sûr que cela puisse être corrigé avec votre approche actuelle), par exemple, essayez de déplacer le System.out.println(e.getParamName(str));System.out.println(e.getParamName(str2));System.out.println(e.getParamName(str4));sur une ligne dans le lien TIO.
PunPun1000
Vous pouvez récupérer l' javapemplacement comme ceci: Paths.get(System.getProperty("java.home"), "bin", "javap").
Olivier Grégoire
@ OlivierGrégoire Merci mais je suis passé à l'invocation de javap via l'api java interne donc je n'ai plus besoin d'obtenir l'emplacement exact sur le disque dans le code (juste le chemin de classe)
Poke
@ PunPun1000 ouais je ne sais pas s'il y a un bon moyen de résoudre ce problème tout en conservant une fonction idempotente mais je pourrais mettre quelque chose ensemble qui utilise simplement l'état en dehors de la fonction pour le plaisir.
Poke
1
@ PunPun1000 Ajout d'une version qui peut gérer plusieurs appels sur une seule ligne. C'est plus que la fonction, j'ai donc ajouté un autre lien TryItOnline plutôt que de mettre à jour la réponse.
Poke
14

Python 2

Il s'agit du code le plus sale que j'ai écrit, mais cela fonctionne. ¯ \ _ (ツ) _ / ¯ Lance une erreur sur une variable inexistante car Python vous déteste immédiatement pour avoir appelé la fonction avec une. Lance également une erreur sur les non-variables, mais cela peut être corrigé avec un essai / sauf si nécessaire.

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]

Essayez-le en ligne!

Si nous sommes autorisés à prendre l'argument comme une chaîne, cela satisfait les exigences de sortie d'une valeur falsifiée sur une entrée non valide.

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

Essayez-le en ligne!

totalement humain
la source
Vous avez posté presque la même réponse, alors que je tapais une explication à la mienne: D +1 pour vous
Dead Possum
1
@DeadPossum Toujours publier et répondre et ajouter une explication plus tard en tant que modification. ;)
Arjun
8
@Arjun, oh super donc nous devons jouer à EditGolf ainsi qu'à CodeGolf: P
Wossname
12

Mathematica

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

le HoldFirst attribut empêche fd'évaluer son argument avant d'appeler la fonction. ValueQ @ xvérifie ensuite si l'argument donné est une variable qui a reçu une valeur. Sinon, nous revenons simplement en Falseraison d'un court-circuit. Sinon, nous obtenons le nom de variable avec ToString @ HoldForm @ x.

Martin Ender
la source
Dang, abusant de la façon dont Mathematica gère And... J'aime que le bon argument de Andne doive même pas être une expression booléenne.
JungHwan Min
Je sais que ce n'est pas du golf de code, mais pourquoi pas f[x_] := ValueQ @ x && HoldForm @ x? S'il n'y avait pas eu besoin de vérifier si l'entrée est une variable, cela HoldFormsuffirait à lui seul.
ngenisis
HoldAllau lieu de HoldFirst?
Greg Martin
1
@ngenisis Parce que ça revient HoldForm[a]et non "a". Bien qu'ils soient affichés de la même manière dans le bloc-notes Mathematica, la fonction existe indépendamment du frontend, donc je voulais avoir une solution qui renvoie une chaîne.
Martin Ender
1
Demande de suppression d'espaces parce que ... euh ... parce que
CalculatorFeline
7

Mathematica

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

Mathematica aime évaluer tout , alors pour l'arrêter, il faut lutter contre cela. Dans sa propre langue.

Comment?

f~SetAttributes~HoldAll;

Indique à Mathematica que chaque fois qu'une fonction fest appelée, ses arguments ne doivent pas être évalués (c'est-à-dire maintenus).


f[_] = False;

Quand fest appelé avec un argument, sortie False. (?!)


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

Quand f est appelé avec un symbole défini (qui a une valeur), affichez le nom du symbole de l'entrée.

La ligne précédente n'a pas la priorité, bien qu'elle ait été définie précédemment, car cette définition est plus spécifique. Comme l'indique la documentation Wolfram: Mathematica "essaie de placer les définitions spécifiques avant les définitions plus générales".

Mathematica est très têtu et continue d'essayer d'évaluer les variables autant que possible. L'extra s'en Unevaluatedoccupe.

JungHwan Min
la source
7

C #

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

Version complète / formatée:

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

Une autre façon de le faire est de réfléchir sur le type comme ceci:

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

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

Cependant, vous devez l'appeler comme:

GetParameterName(new { apple });

Il échoue également 3.14159en retournant Lengthet pour cela, vous devez également l'appeler comme suit à la place:

GetParameterName(new double[]{ 3.14159 });

En C # 6.0, nous avons également:

nameof

Mais cela ne se compilera pas si vous lui passez quelque chose qui n'est pas une variable par exemple 3.14159. Je crois qu'il évalue également l'entrée au moment de la compilation, il semble donc qu'il échoue également à cette exigence.

TheLethalCoder
la source
C'est un peu douteux ... parce que le truc lambda est absolument essentiel (c'est-à-dire que vous ne pouvez pas simplement passer la variable, vous devez passer quelque chose que la compilation reconnaîtra devrait devenir un Expression). Vous devriez également compter using System.Linq.Expressions;.
VisualMelon
Est-ce que cela compte comme "recevoir des arguments sous forme d'arborescence de syntaxe abstraite"?
Matti Virkkunen
@VisualMelon Je ne pouvais pas trouver d'autre moyen de le faire en C # donc je l'ai fait de cette façon. De plus, ce n'est pas du golf de code, donc cela n'a pas vraiment d'importance,
TheLethalCoder
... désolé, je n'avais pas réalisé qu'il n'y avait pas de compte d'octets!
VisualMelon
6

MATLAB

varname = @(x) inputname(1);

Dans une fonction, il y a quelques variables prédéfinies comme varargin ou nargin, parmi celles que nous avons également inputname.

volé d'ici

flawr
la source
Je n'ai jamais su que cela existait. J'ai un peu joué avec ça, et peut-être que vous aimerez ce joyau:x=42;y=54; f=@(x)eval(inputname(1));f(x),f(y)
Sanchises
Haha, eval==evil= D (cela fonctionne en fait avec x=42;y=54; f=@(x)eval('inputname(1)');f(x),f(y))
flawr
Ouais, c'est juste qu'une fonction anonyme ne connaît aucune variable d'espace de travail, donc eval ne peut qu'évaluer x, et est alors en evaltrain d'argumenter la fonction, pas la variable d'espace de travail.
Sanchises
6

R

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

substituterenvoie l'arbre d'analyse pour une expression non évaluée. Le identicalconditionnel s'assure que cette expression non évaluée n'est pas identique à l'expression elle-même; c'est-à-dire que le paramètre passé n'est pas un littéral.

mb7744
la source
4

Python 2

Des recherches ont été faites. Et cela semble possible en python, mais je m'attends toujours à trouver des problèmes.
La solution n'est pas parfaite, car elle suppose une certaine structure du code. Pourtant je ne l'ai pas encore cassé.

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

Cela utilise inspect pour regarder la portée surround et trouver où la fonction get_nameest appelée. Par exemple inspect...[1], reviendra

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

Et inspect...[1][4]nous montrera la liste avec le code, où la fonction est appelée:

['print get_name( a )\n']

Après cela, j'ai utilisé l'expression régulière pour récupérer le nom de l'argument

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

Et .lstrip().rstrip()pour supprimer tous les espaces blancs qui peuvent être placés dans des bracelets.

Exemple avec une entrée délicate

Possum mort
la source
4

PowerShell

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

$apple = 'hi'
t $apple
apple

t 3.141

Mais cela ne fonctionne pas de manière fiable, c'est rudimentaire.

TessellatingHeckler
la source
Ce n'est pas du golf de code.
Okx
@Okx oh, oups, donc ce n'est pas le cas.
TessellatingHeckler
4

PHP

Nécessite que la valeur de la variable soit unique dans le tableau des variables globales

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

Essayez-le en ligne!

PHP , 96 octets

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

Essayez-le en ligne!

Nécessite que la variable soit initialisée directement à l'appel de fonction.

Vérifie si la dernière variable globale est une variable de remise égale

Jörg Hülsermann
la source
Ce n'est pas du golf de code.
Okx
@Okx je sais Pour le moment je n'ai pas de meilleure idée.
Jörg Hülsermann
4

JavaScript (ES6)

Pour tous les noms de propriété dans window(Object.getOwnPropertyNames(w) ), essayez de définir un getter pour cette propriété qui renvoie le nom de la propriété.

Ensuite, ajoutez une entrée à une carte M où la clé est la valeur (éventuellement remplacée) de la propriété et la valeur est le nom de la propriété.

La fonction fprend simplement une variable (c'est-à-dire une propriété de window) et retourne l'entrée Mpour cette valeur.

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)

Cela fonctionne pour toutes les variables globales intégrées sauf windowelle-même, car il n'y a aucun moyen de la distinguer top(sauf si elle est exécutée dans un cadre):

L( P.filter(p => p !== f(w[p])) )
// -> ['window']
darrylyeo
la source
Pour une raison quelconque, je reçois une erreur: L not a function. Pourquoi cela arriverait - il?
Caleb Kleveter
Hou la la! C'est plus profond dans ES6 que je ne l'ai vu depuis un moment.
MD XF
@CalebKleveter D'oh! J'ai oublié une virgule sur la deuxième ligne. Cela peut ou non poser problème.
darrylyeo
3

Perl 5 + Devel :: Appelant

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

Nous utilisons Devel :: Caller (un module de type débogueur) pour parcourir la pile d'appels, rechercher l'appel à la fonction et renvoyer tous les opérandes dans l'argument, en les renvoyant sous la forme de noms de variables. S'il y a plus (ou moins) d'un opérande, nous n'avons pas été appelés avec une variable. Si l'opérande n'était pas une variable, il n'aura pas de nom et nous pouvons le détecter aussi.

Le cas le plus délicat est que si nous obtenons une expression à un opérande impliquant une variable, telle que ~$x . Nous pouvons déterminer si cela s'est produit en prenant une référence à la variable directement à partir de la table des symboles (en utilisant le${…} syntaxe de référence symbolique) et en comparant son adresse mémoire à la valeur qui nous a été transmise en argument (qui, commodément, est transmise par référence ). S'ils sont différents, nous avons une expression plutôt qu'une variable isolée.

Notez que si nous appelons cette fonction avec une expression de pré-incrémentation ou de pré-incrémentation sur une seule variable, comme dans v(--$x), nous sommes $xretournés. C'est parce que c'est en fait la variable elle-même qui est passée à la fonction dans ce cas; il est simplement incrémenté ou décrémenté au préalable. J'espère que cela ne disqualifie pas la réponse. (D'une certaine manière, cela le rend meilleur, car cela montre que nous vérifions l'argument lui-même plutôt que de simplement lire le code source.)


la source
3

PHP

Alors que les autres soumissions PHP ne testent que si la valeur donnée correspond à la valeur d'un global, cette version fonctionne en prenant une référence à la valeur:

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

Cela devrait maintenant fonctionner même si la variable globale est une référence à une autre valeur au moment de l'appel.

Testez-le ici .

Christoph
la source
Super et merci beaucoup pour l'effort d'apprentissage
Jörg Hülsermann
@ JörgHülsermann Même trouvé un moyen de l'améliorer!
Christoph
3

Röda

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

Essayez-le en ligne!

Röda a une fonction intégrée pour cela - name. Malheureusement, cependant, il ne renvoie pas de valeur falsifiée lorsqu'il ne reçoit pas de variable.

Ce code abuse de plusieurs fonctionnalités de la sémantique de référence. Explication ligne par ligne:

f(&a...) {

La fonction prend un nombre variable d'arguments de référence ( &a...). C'est le seul moyen dans Röda de créer une liste de références.

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

À la deuxième ligne, les éléments de asont poussés vers le flux ( a()). Ces éléments sont des références si la fonction a reçu des variables et des valeurs normales dans le cas contraire.

Les éléments sont itérés à l'aide d'une boucle de soulignement. La syntaxe de soulignement est le sucre de syntaxe pour les forboucles, elle name(_)est donc équivalente àname(var) for var . Le nom de la variable utilisée dans la forboucle est de forme <sfvN>où N varie. (C'est à dire. "name(<sfv1>) for <sfv1> ") Il est illégal pour une variable définie par l'utilisateur de contenir <ou> , de sorte que les noms générés ne se heurtent pas aux noms de variables existants.

La name()fonction renvoie le nom de la variable donnée. Si l'élément dansa cours d'itération est une référence, alors c'est la variable donnée à name. Sinon, si l'élément était une valeur normale, la variable donnée à nameest la variable de soulignement<sfvN> . Cela est dû à la sémantique des références dans Röda: si une fonction accepte une référence et que la fonction reçoit une variable de référence, la valeur transmise ne pointe pas vers la variable de référence mais vers la variable vers laquelle la variable de référence pointe.

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

À la troisième ligne, nous examinons si le nom de variable dans le flux contient un < caractère. Si c'est le cas, il s'agit d'une variable de soulignement et la valeur transmise à fn'était pas une référence. Sinon, nous sortons le nom de la référence.

Cette solution ne fonctionne pas si la variable donnée à la fonction est une variable de référence ou de soulignement. Cependant, la question spécifie que seules les variables globales doivent être gérées et qu'elles ne peuvent pas être des références ou des variables de soulignement dans Röda.

fergusq
la source
1

Rubis , 46 octets

Se sent comme le code le plus sale que j'ai jamais écrit pour Ruby.

Nécessite que les variables globales que vous appelez aient une valeur unique qui ne se trouve sur aucune autre variable globale, car la seule façon de relever ce défi dans Ruby est de faire une recherche sur toutes les variables globales et de renvoyer la première correspondance. OP dit que ça va et est libre de juger si ma solution est valide.

Notez que les variables globales commencent par $Ruby, car si vous souhaitez ajouter des éléments de cas de test supplémentaires.

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

Essayez-le en ligne!

Encre de valeur
la source
1

PHP

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

si une valeur est trouvée, imprimez le nom de la variable et quittez. n'imprimez rien et ne quittez rien d'autre.

61 octets pour retourner le nom de la variable ou NULL:

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

Il ne trouvera pas les fonctions nommées, seulement celles assignées aux variables.
Et une fonction PHP ne peut pas détecter si un paramètre a été fourni par référence ou par valeur. La fonction renverra simplement le prénom où la valeur correspond à la valeur du paramètre de fonction.

Testez-le en ligne

Titus
la source
1

PowerShell

Nouvelle version mais fonctionne à partir de PowerShell 3.0

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

Essayez-le en ligne!

La version précédente

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

Essayez-le en ligne!

Andrei Odegov
la source
0

tcl

proc p a {puts [subst $a]}

démo

sergiol
la source
0

JavaScript (ES6)

Nécessite que la valeur de la variable transmise à la fonction soit unique à cette variable. Renvoie undefinedsi une variable n'a pas été transmise.

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

Essayez-le

Pour une raison quelconque, il renvoie une erreur d'origine croisée dans un extrait de code lorsqu'un "littéral" est passé.

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


Explication

Parcourez toutes les entrées de l'objet global ( this), en vérifiant si la valeur de chacune est strictement égale à la valeur de l'argument passé à la fonction. Si une entrée correspondante est trouvée, sa clé (nom) est renvoyée, quittant la fonction.


Alternative

Avec les mêmes exigences que ci-dessus

arg=>
    [...Object.keys(this)].filter(key=>
        this[key]==arg
    ).pop()
Hirsute
la source
2
Je pense que cela échoue si deux globaux ont la même valeur.
Martin Ender
1
Je reçois une sortie très bizarre .
Caleb Kleveter
@MartinEnder; oui, il suppose que la valeur attribuée à la variable transmise est unique à cette variable; J'ai oublié de l'inclure lorsque j'ai posté.
Shaggy
@CalebKleveter; certains d'entre eux sont dus au fait que la valeur de la variable que vous transmettez n'est pas unique à cette variable et certains sont dus à des noms de variables invalides (par exemple, hello--). En outre, vous devez utiliser varplutôt que let.
Shaggy
1
@CalebKleveter, voir ici pour plus d'informations sur la différence de portée entre letet var. À votre deuxième question: cela s'est produit parce que vous avez une variable nommée IN_GLOBAL_SCOPEdans votre portée globale et qu'elle a une valeur de 1. Vous pouvez vérifier les variables existantes dans votre portée globale et leurs valeurs en exécutant ceci avant de tester ce qui précède:for(x in this)console.log(x+": "+this[x])
Shaggy
0

Swift, 45 octets

var a = 3
print(Mirror(reflecting: a).label!)
Dimitrie-Toma Furdui
la source
Je ne peux pas faire ça. value of type 'Mirror' has no member 'label'
J'obtiens