Роздрукувати ім'я змінної [закрито]


20

Напишіть функцію (не повну програму), так що якщо функція викликається однією глобальною змінною (або найближчим еквівалентом вашої мови) як її аргументом, вона виводить (тобто друкує або повертає) назву цієї змінної. Якщо аргумент не є змінною, виведіть натомість значення фальси. (Вам не доведеться обробляти випадок, коли аргумент є змінною, але не глобальною.)

Між викликом функції та визначенням функції не повинно бути з'єднання в часі компіляції (зокрема, визначення функції не може бути макросом або подібною конструкцією, яка отримує аргументи у вигляді буквального фрагмента вихідного коду, ні в тексті, ні в абстрактному синтаксичному дереві форма). Тобто: мовами, які підтримують окрему компіляцію, програма повинна працювати навіть у тому випадку, якщо виклик функції скомпонований спочатку (не знаючи визначення функції, але, можливо, підпис типу чи еквівалент), потім визначення функції складається після цього. Якщо мова не має окремої компіляції, ви, тим не менш, повинні прагнути подібного поділу між викликом і визначенням.

Якщо ви користуєтесь мовою компіляції, ви можете прочитати скомпільовану форму повної програми з диска і, можливо, припустити, що програма була складена з інформацією про налагодження. (Таким чином, рішення, які працюють, приєднуючи налагоджувач від програми до себе, дозволено.)

Зауважте, що це завдання може бути неможливим не всіма мовами.

Приклади:

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

Коментарі не для розширеного обговорення; ця розмова переміщена до чату .
Денніс

Відповіді:


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

Наразі це працює з кількома дітками:

  1. Якщо ви використовуєте IDE для компіляції, це може не працювати, якщо він не запускається як адміністратор (залежно від того, де зберігаються тимчасові файли класу)
  2. Ви повинні компілювати за javacдопомогою -gпрапора. Це генерує всю інформацію про налагодження, включаючи імена локальних змінних у складеному файлі класу.
  3. Для цього використовується внутрішній Java API, com.sun.tools.javapякий аналізує байт-код класового файлу та дає результат, прочитаний людиною. Цей API доступний лише в бібліотеках JDK, тому вам потрібно або використовувати час виконання програми JDK java, або додати tools.jar у свій класний шлях.

Тепер це має працювати, навіть якщо метод викликається кілька разів у програмі. На жаль, він ще не працює, якщо у вас є кілька викликів у одному рядку. (Про те, що це робить, дивіться нижче)

Спробуйте в Інтернеті!


Пояснення

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

Ця перша частина отримує загальну інформацію про те, в якому класі ми перебуваємо і як називається функція. Це здійснюється шляхом створення винятку та розбору перших 2 записів трасування стека.

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

Перший запис - це рядок, з якого викинуто виняток, з якого ми можемо схопити методName, а другий запис - звідки була викликана функція.

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

У цьому рядку ми виконуємо виконуваний файл javap, що поставляється разом з JDK. Ця програма аналізує файл класу (байт-код) і представляє читабельний для людини результат. Ми будемо використовувати це для рудиментарного "розбору".

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

Тут ми робимо кілька різних речей. По-перше, ми читаємо вихідний рядок javap в список. По-друге, ми створюємо карту індексів рядок байт-кодів до індексів рядків Javavap. Це допомагає нам пізніше визначити, який виклик методу ми хочемо проаналізувати. Нарешті, ми використовуємо відомий номер рядка зі сліду стека, щоб визначити, який індекс рядка байт-коду ми хочемо дивитись.

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

Ось ми повторюємо повтор через лінії javap, щоб знайти місце, де викликається наш метод і де починається таблиця локальної змінної. Рядок, у якому викликається метод, нам потрібен, оскільки рядок перед ним містить виклик для завантаження змінної та визначає, яку змінну (за індексом) завантажити. Таблиця локальної змінної допомагає нам реально шукати назву змінної на основі індексу, який ми захопили.

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;

Нарешті ми розбираємо назву змінної з рядка в таблиці локальної змінної. Поверніть нуль, якщо його не знайдено, хоча я не бачив причин, чому це повинно статися.

Збираючи все це разом

 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;

Це в основному те, що ми дивимось. У прикладі коду першим викликом є ​​рядок 17. Рядок 17 у рядку LineNumberTable показує, що початок цього рядка є індекс 18. рядок байт-коду. Це System.outнавантаження. Тоді ми маємо aload_2прямо перед викликом методу, тому шукаємо змінну у слоті 2 LocalVariableTable, яка є strв цьому випадку.


Для задоволення, ось один, який обробляє декілька функціональних дзвінків на одній лінії. Це призводить до того, що функція не є ідентичною, але в цьому полягає суть. Спробуйте в Інтернеті!


1
Люблю цю відповідь. Думав про щось по одній лінії. Одне зауваження, якщо ви включаєте кілька дзвінків в один рядок програми, то він не може визначити, хто з них викликає метод (не впевнений, що це можна виправити при вашому поточному підході), наприклад, спробуйте перейти System.out.println(e.getParamName(str));System.out.println(e.getParamName(str2));System.out.println(e.getParamName(str4));на один рядок у посиланні TIO.
PunPun1000

Ви можете отримати javapмісце розташування , як це: Paths.get(System.getProperty("java.home"), "bin", "javap").
Олів'є Грегоар

@ OlivierGrégoire Дякую, але я перейшов на виклик javap через внутрішню програму java api, тому мені більше не потрібно отримувати точне місце розташування на диску в коді (лише класний шлях)
Poke

@ PunPun1000 так, я не впевнений, чи є приємний спосіб виправити це, зберігаючи ідентичну функцію, але я можу скласти щось разом, що просто використовує стан поза функцією для розваги.
Поки

1
@ PunPun1000 Додана версія, яка може обробляти кілька дзвінків на одній лінії. Це більше, ніж просто функція, тому я просто додав ще одне посилання TryItOnline, а не оновлення відповіді.
Поп

14

Пітон 2

Це про найбільш брудний код, який я написав, але він працює. ¯ \ _ (ツ) _ / ¯ Вмикає помилку на неіснуючій змінній, оскільки Python негайно не сподобається вам за виклик функції однією. Також видає помилку на незмінних, але це можна виправити за допомогою спроби / за винятком необхідності.

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]

Спробуйте в Інтернеті!

Якщо нам дозволяється сприймати аргумент як рядок, це задовольняє вимогам виведення помилкового значення на недійсний вхід.

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

Спробуйте в Інтернеті!


Ви опублікували майже таку ж відповідь, поки я набирав моє пояснення: D +1 для вас
Dead Possum

1
@DeadPossum Завжди публікуйте відповіді та додайте пояснення пізніше як редагування. ;)
Арджун

8
@ Арджун, чудово, тому нам доведеться грати в EditGolf, а також CodeGolf: P
Wossname

12

Математика

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

HoldFirstАтрибут запобігає fвід оцінки його аргументу перед викликом функції. ValueQ @ xпотім перевіряє, чи є даний аргумент змінною, якій було надано значення. Якщо ні, то ми просто повертаємося Falseчерез коротке замикання. В іншому випадку ми отримаємо ім'я змінної з ToString @ HoldForm @ x.


Данг, зловживаючи способом Andобробки Mathematica ... Мені подобається, як правильний аргумент Andнавіть не повинен бути булевим виразом.
JungHwan Min

Я знаю, що це не код гольфу, але чому б не просто f[x_] := ValueQ @ x && HoldForm @ x?. Якби не вимога перевірити, чи є вхідною змінною, HoldFormвона сама по собі спрацювала б.
ngenisis

HoldAllзамість HoldFirst?
Грег Мартін

1
@ngenisis Тому що це повертається, HoldForm[a]а ні "a". Хоча вони відображаються однаково у зошиті Mathematica, функція існує незалежно від інтерфейсу, тому я хотів мати рішення, яке повертає рядок.
Мартін Ендер

1
Запит на видалення пробілів, тому що ... гм ... тому, що
CalculatorFeline

7

Математика

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

Математика любить все оцінювати , тому, щоб зупинити це, ми повинні боротися проти цього. Своєю мовою.

Як?

f~SetAttributes~HoldAll;

Повідомляє Mathematica, що всякий раз, коли fвикликається функція , її аргументи не слід оцінювати (тобто проводити).


f[_] = False;

Коли fвикликається аргументом, виведіть False. (?!)


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

Коли fвикликається певним символом (який має значення), виведіть ім'я символу введення.

Попередній рядок не має переваги, незважаючи на те, що визначено раніше, оскільки це визначення є більш конкретним. Як зазначається в документації Вольфрама: Mathematica "намагається поставити конкретні визначення перед більш загальними визначеннями".

Mathematica дуже впертий і постійно намагається оцінити змінні, коли це можливо. Додатковий Unevaluatedпіклується про це.


7

C #

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

Повна / відформатована версія:

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

Ще один спосіб зробити це, подумавши про тип типу:

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

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

Однак ви повинні назвати це так:

GetParameterName(new { apple });

Потім він також не вдається 3.14159повернутися, Lengthі для цього вам також доведеться називати його так:

GetParameterName(new double[]{ 3.14159 });

У C # 6.0 ми також маємо:

nameof

Але це не компілюється, якщо ви передасте йому щось, що не є змінною, наприклад 3.14159. Я вважаю, що він також оцінює вхід під час компіляції, тому, здається, він також не відповідає цій вимозі.


Це трохи хитро ... тому що лямбда-матеріал абсолютно необхідний (тобто ви не можете просто передати змінну, ви повинні передати щось, що компілятор визнає, що повинен стати Expression). Ви також повинні рахувати using System.Linq.Expressions;.
VisualMelon

Чи вважає це "отримання аргументів у формі абстрактного синтаксичного дерева"?
Матті Вірккунен

@VisualMelon Я не міг знайти іншого способу зробити це в C #, тому я зробив це так. Також це не код-гольф, тому це насправді не має значення,
TheLethalCoder

... вибачте, не зрозумів, що немає байта!
VisualMelon

6

MATLAB

varname = @(x) inputname(1);

У межах функції є кілька заданих змінних, таких як varargin або nargin, серед тих, які ми також маємо inputname.

вкрадено звідси


Я ніколи не знав, що це існує. Я трохи x=42;y=54; f=@(x)eval(inputname(1));f(x),f(y)
погрався

Ха-ха, eval==evil= D (це насправді працює з x=42;y=54; f=@(x)eval('inputname(1)');f(x),f(y))
flawr

Так, просто анонімна функція не знає про жодну змінну робочої області, тому eval може лише оцінювати x, а потім eval- аргумент функції, а не змінну робочої області.
Санчіз

6

R

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

substituteповертає дерево розбору для неоціненого вираження. В identicalумовному переконується , що це невичісленное вираз не збігається з самим виразом; тобто, що переданий параметр не є буквальним.


4

Пітон 2

Було проведено деякі дослідження. І це, мабуть, можливо в python, але я все ще очікую, що будуть знайдені деякі неприємності.
Рішення не є ідеальним, оскільки передбачає певну структуру коду. І все-таки я її ще не зламав.

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

При цьому використовується інспектувати, щоб переглянути область об'ємного звучання та знайти, де get_nameвикликається функція . Наприклад inspect...[1]повернеться

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

І inspect...[1][4]покаже нам список з кодом, де функція називається:

['print get_name( a )\n']

Після цього я використовував регулярний вираз для отримання назви аргументу

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

І .lstrip().rstrip()видалити всі пробіли, які можуть бути розміщені в брекети.

Приклад з деяким хитрим введенням


4

PowerShell

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

$apple = 'hi'
t $apple
apple

t 3.141

Але це не працює надійно, це рудиментарно.


Це не код гольфу.
Окс

@ Okx ой, ой, так це не так.
TessellatingHeckler

4

PHP

Необхідно, щоб значення змінної було унікальним у масиві глобальних змінних

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

Спробуйте в Інтернеті!

PHP , 96 байт

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

Спробуйте в Інтернеті!

Необхідно, щоб змінна ініціалізувалася безпосередньо до виклику функції.

Перевіряє, чи остання глобальна змінна дорівнює змінній здачі


Це не код гольфу.
Окс

@Okx Я знаю На даний момент я не маю кращого уявлення.
Йорг Гюльсерманн


4

JavaScript (ES6)

Для всіх імен властивостей у window(Object.getOwnPropertyNames(w) ) спробуйте визначити getter для цього властивості, який повертає ім'я властивості.

Потім додайте запис до карти M де ключовим є значення (можливо, переокремлене) властивості, а значення - назва власності.

Функція fпросто приймає змінну (тобто властивість window) і повертає запис Mдля цього значення.

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)

Це працює для всіх вбудованих глобальних змінних, окрім windowсамої себе, оскільки немає способу відрізнити її від top(якщо не працювати у кадрі):

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

З різних причин, я отримую повідомлення про помилку: L not a function. Чому це сталося?
Калеб Клеветер

Оце Так! Це глибше в ES6, ніж я бачив за якийсь час.
MD XF

@CalebKleveter D'oh! Я забув кому у другому рядку. Це може бути або не бути проблематичним.
darrylyeo

3

Perl 5 + Devel :: Абонент

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

Ми використовуємо Devel :: Caller (модуль, схожий на відладчик), щоб ходити по стеку викликів, шукаючи виклик функції і повертаючи всі операнди в межах аргументу, повертаючи їх як імена змінних. Якщо є більше (або менше), ніж один операнд, нас не називають змінною. Якщо операнд не був змінною, він не буде мати імені, і ми можемо виявити його також.

Найскладніший випадок, якщо ми отримаємо однооперандний вираз із залученням змінної, наприклад ~$x. Ми можемо з’ясувати, чи це сталося, взявши посилання на змінну безпосередньо з символьної таблиці (використовуючи${…} синтаксис опорного символічного символу) і порівнявши її адресу пам'яті зі значенням, яке ми передали як аргумент (що зручно, передається посиланням ). Якщо вони різні, у нас є вираз, а не самотня змінна.

Зауважимо, що якщо ми називаємо цю функцію за допомогою попереднього або попереднього виразу на одній змінній, як у v(--$x), ми $xповертаємось. Це тому, що насправді саме ця змінна в цьому випадку передається функції; він заздалегідь просто збільшується або зменшується. Я сподіваюся, що це не дискваліфікує відповідь. (Деяким чином, це робить його кращим, оскільки це показує, що ми перевіряємо сам аргумент, а не просто читаємо вихідний код.)


3

PHP

Хоча інші подання PHP перевіряють лише те, чи задане значення відповідає загальному значенню, ця версія працює, приймаючи посилання на значення:

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

Тепер це має працювати, навіть якщо глобальна змінна є посиланням на інше значення на момент виклику.

Перевірте це тут .



@ JörgHülsermann Навіть знайшов спосіб покращити його!
Крістоф

3

Röda

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

Спробуйте в Інтернеті!

Röda має вбудовану функцію для цього - name. На жаль, він не повертає хибне значення, коли йому не дано змінну.

Цей код зловживає кількома особливостями опорної семантики. Пояснення за рядком:

f(&a...) {

Функція приймає змінну кількість опорних аргументів ( &a...). Це єдиний спосіб у Röda створити список посилань.

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

На другому рядку елементи aвисуваються до потоку ( a()). Ці елементи є посиланнями, якщо функція отримала змінні та нормальні значення в іншому випадку.

Елементи повторюються за допомогою циклу підкреслення. Синтаксис підкреслення - це синтаксичний цукор для forциклів, тому name(_)він еквівалентний name(var) for var. Назва змінної, що використовується в forциклі, має форму, <sfvN>де N змінюється. (Тобто. " name(<sfv1>) for <sfv1>") Заборонено, щоб визначена користувачем змінна містила <або >, тому створені імена не стикаються з існуючими іменами змінних.

name()Функція повертає ім'я даної змінної. Якщо aітераційний елемент є посиланням, то він є змінною name. В іншому випадку, якщо елемент був нормальним значенням, змінною, що надається, nameє змінна підкреслення <sfvN>. Це пов’язано з семантикою посилань у Röda: якщо функція приймає посилання, а функція передається опорною змінною, передане значення вказує не на опорну змінну, а на змінну, на яку вказує посилальна змінна.

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

У третьому рядку ми перевіряємо, чи ім'я змінної у потоці містить <символ. Якщо це так, це змінна підкреслення і значення, передане вf не було посиланням. В іншому випадку ми виводимо ім’я посилання.

Це рішення не працює, якщо змінна, надана функції, є посиланням або змінною підкреслення. Однак питання вказує на те, що в Röda слід обробляти лише глобальні змінні, і вони не можуть бути посиланнями або підкресленнями змінних.


1

Рубін , 46 байт

Відчуває себе найбруднішим кодом, який я коли-небудь писав для Рубі.

Вимагає, щоб глобальна змінна, яку ви викликаєте, мала унікальне значення, яке не має жодної іншої глобальної змінної, тому що єдиний спосіб вирішити цю проблему в Ruby - це пошук усіх глобальних змінних і повернення першого збігу. ОП каже, що це нормально, і вільне судити, чи моє рішення справедливе.

Зауважте, що глобальні змінні починаються з $Ruby, бо якщо ви хочете додати додаткові матеріали тестового випадку.

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

Спробуйте в Інтернеті!


1

PHP

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

якщо значення знайдено, надрукуйте ім'я змінної та вихід. нічого не друкувати і більше не виходити.

61 байт для повернення імені змінної або NULL:

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

Він не знайде іменованих функцій, лише ті, що присвоєні змінним.
І функція PHP не може виявити, чи був параметр, наданий посиланням або значенням. Функція просто поверне ім'я, де значення відповідає значенню параметра параметра.

Перевірте це в Інтернеті


1

PowerShell

Нова версія, але працює починаючи з PowerShell 3.0

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

Спробуйте в Інтернеті!

Попередня версія

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

Спробуйте в Інтернеті!



0

JavaScript (ES6)

Потрібно, щоб значення змінної, переданої функції, було унікальним для цієї змінної. Повертається, undefinedякщо змінна не була передана.

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

Спробуй це

Чомусь він видає помилку перехресного походження у фрагменті, коли передається "буквальний".

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


Пояснення

Перегляньте всі записи в глобальному об'єкті ( this), перевіривши, чи значення кожного з них суворо дорівнює значенню аргументу, переданого функції. Якщо знайдено відповідний запис, його ключ (ім'я) повертається, виходячи з функції.


Альтернатива

З тими ж вимогами, що і вище

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

2
Я думаю, що це не вдасться, якщо два глобалі мають однакове значення.
Мартін Ендер


@MartinEnder; так, передбачається, що значення, присвоєне змінній, що передається, є унікальною для цієї змінної; забув включити це, коли я розмістив повідомлення.
Кудлатий

@CalebKleveter; деякі з них пов’язані з тим, що значення змінної, яку ви передаєте, не є унікальною для цієї змінної, а частина - через недійсні імена змінних (наприклад, hello--). Крім того, вам потрібно використовувати, varа не let.
Кудлатий

1
@CalebKleveter, див. Тут для отримання додаткової інформації про різницю в області визначення розміру між letта var. До вашого другого питання: Це сталося тому, що у вас є IN_GLOBAL_SCOPEглобальна область змінної, і вона має значення 1. Ви можете перевірити існуючі змінні у вашому глобальному масштабі та їх значення, виконавши цю функцію, перш ніж тестувати вище:for(x in this)console.log(x+": "+this[x])
Shaggy

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.