É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
la source
Réponses:
Java
Cela fonctionne actuellement avec quelques pièges:
javac
de l'-g
indicateur. Cela génère toutes les informations de débogage, y compris les noms de variables locales dans le fichier de classe compilé.com.sun.tools.javap
qui 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
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.
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.
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.
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.
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é.
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.
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
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.out
charge. Ensuite, nous avonsaload_2
juste avant l'appel de la méthode, nous recherchons donc la variable dans l'emplacement 2 du LocalVariableTable qui eststr
dans 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!
la source
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.javap
emplacement comme ceci:Paths.get(System.getProperty("java.home"), "bin", "javap")
.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.
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.
Essayez-le en ligne!
la source
Mathematica
le
HoldFirst
attribut empêchef
d'évaluer son argument avant d'appeler la fonction.ValueQ @ x
vérifie ensuite si l'argument donné est une variable qui a reçu une valeur. Sinon, nous revenons simplement enFalse
raison d'un court-circuit. Sinon, nous obtenons le nom de variable avecToString @ HoldForm @ x
.la source
And
... J'aime que le bon argument deAnd
ne doive même pas être une expression booléenne.f[x_] := ValueQ @ x && HoldForm @ x
? S'il n'y avait pas eu besoin de vérifier si l'entrée est une variable, celaHoldForm
suffirait à lui seul.HoldAll
au lieu deHoldFirst
?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.Mathematica
Mathematica aime évaluer tout , alors pour l'arrêter, il faut lutter contre cela. Dans sa propre langue.
Comment?
Indique à Mathematica que chaque fois qu'une fonction
f
est appelée, ses arguments ne doivent pas être évalués (c'est-à-dire maintenus).Quand
f
est appelé avec un argument, sortieFalse
. (?!)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
Unevaluated
occupe.la source
C #
Version complète / formatée:
Une autre façon de le faire est de réfléchir sur le type comme ceci:
Cependant, vous devez l'appeler comme:
Il échoue également
3.14159
en retournantLength
et pour cela, vous devez également l'appeler comme suit à la place:En C # 6.0, nous avons également:
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.la source
Expression
). Vous devriez également compterusing System.Linq.Expressions;
.MATLAB
Dans une fonction, il y a quelques variables prédéfinies comme
varargin
ounargin
, parmi celles que nous avons égalementinputname
.volé d'ici
la source
x=42;y=54; f=@(x)eval(inputname(1));f(x),f(y)
eval==evil
= D (cela fonctionne en fait avecx=42;y=54; f=@(x)eval('inputname(1)');f(x),f(y)
)x
, et est alors eneval
train d'argumenter la fonction, pas la variable d'espace de travail.R
substitute
renvoie l'arbre d'analyse pour une expression non évaluée. Leidentical
conditionnel 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.la source
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é.
Cela utilise inspect pour regarder la portée surround et trouver où la fonction
get_name
est appelée. Par exempleinspect...[1]
, reviendraEt
inspect...[1][4]
nous montrera la liste avec le code, où la fonction est appelée:Après cela, j'ai utilisé l'expression régulière pour récupérer le nom de l'argument
Et
.lstrip().rstrip()
pour supprimer tous les espaces blancs qui peuvent être placés dans des bracelets.Exemple avec une entrée délicate
la source
PowerShell
Mais cela ne fonctionne pas de manière fiable, c'est rudimentaire.
la source
PHP
Nécessite que la valeur de la variable soit unique dans le tableau des variables globales
Essayez-le en ligne!
PHP , 96 octets
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
la source
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
f
prend simplement une variable (c'est-à-dire une propriété dewindow
) et retourne l'entréeM
pour cette valeur.Cela fonctionne pour toutes les variables globales intégrées sauf
window
elle-même, car il n'y a aucun moyen de la distinguertop
(sauf si elle est exécutée dans un cadre):la source
L not a function
. Pourquoi cela arriverait - il?Perl 5 + Devel :: Appelant
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$x
retourné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
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:
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 .
la source
Röda
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
a
sont 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
for
boucles, ellename(_)
est donc équivalente àname(var) for var
. Le nom de la variable utilisée dans lafor
boucle 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 àname
est 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 àf
n'é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.
la source
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.Essayez-le en ligne!
la source
PHP
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
: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
la source
PowerShell
Nouvelle version mais fonctionne à partir de PowerShell 3.0
Essayez-le en ligne!
La version précédente
Essayez-le en ligne!
la source
tcl
démo
la source
JavaScript (ES6)
Nécessite que la valeur de la variable transmise à la fonction soit unique à cette variable. Renvoie
undefined
si une variable n'a pas été transmise.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é.
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
la source
hello--
). En outre, vous devez utiliservar
plutôt quelet
.let
etvar
. À votre deuxième question: cela s'est produit parce que vous avez une variable nomméeIN_GLOBAL_SCOPE
dans votre portée globale et qu'elle a une valeur de1
. 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])
Swift, 45 octets
la source
value of type 'Mirror' has no member 'label'