Напечатайте имя переменной [закрыто]


20

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

Не должно быть связи во время компиляции между вызовом функции и определением функции (в частности, определение функции не может быть макросом или подобной конструкцией, которая получает аргументы в форме литерального фрагмента исходного кода, ни в текстовом, ни в абстрактном синтаксическом дереве). форма). То есть: в языках, которые поддерживают раздельную компиляцию, программа должна работать, даже если вызов функции компилируется первым (без знания определения функции, но, возможно, с сигнатурой типа или эквивалентом), затем определение функции компилируется позже. Если у языка нет отдельной компиляции, тем не менее вы должны стремиться к аналогичному разделению между вызовом и определением.

Если вы используете скомпилированный язык, вы можете прочитать скомпилированную форму всей программы с диска и предположить, что программа была скомпилирована с отладочной информацией. (Таким образом, решения, которые работают, присоединяя отладчик из программы к себе, разрешены.)

Обратите внимание, что эта задача может быть невозможна на всех языках.

Примеры:

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

Джава

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, поэтому вы должны либо использовать Java-среду JDK, либо добавить 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";

Эта первая часть получает некоторую общую информацию о том, в каком классе мы находимся и как называется функция. Это достигается созданием исключения и анализом первых двух записей трассировки стека.

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

Первая запись - это строка, в которую выдается исключение, из которой мы можем получить метод methodName, а вторая запись - это место, откуда была вызвана функция.

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 построчно в список. Во-вторых, мы создаем карту строковых индексов байт-кода и строковых индексов javap. Это поможет нам позже определить, какой вызов метода мы хотим проанализировать. Наконец, мы используем известный номер строки из трассировки стека, чтобы определить, какой индекс строки байт-кода мы хотим видеть.

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

Эта часть фактически анализирует вызов load, чтобы получить индекс переменной. Это может вызвать исключение, если функция фактически не вызывается с переменной, поэтому мы можем вернуть здесь ноль.

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 вместо обновления ответа.
Poke

14

Python 2

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

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 для вас
Мертвый Опоссум

1
@DeadPossum Всегда публиковать, отвечать и добавлять объяснения позже, как редактирование. ;)
Арджун

8
@ Арджун, о, здорово, поэтому мы должны играть в EditGolf, а также в CodeGolf: P
Wossname

12

Mathematica

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

HoldFirstАтрибут предотвращает fот оценки его аргумента перед вызовом функции. ValueQ @ xзатем проверяет, является ли данный аргумент переменной, которой было присвоено значение. Если нет, мы просто вернемся Falseиз-за короткого замыкания. В противном случае мы получаем имя переменной с помощью ToString @ HoldForm @ x.


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

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

HoldAllвместо HoldFirst?
Грег Мартин

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

1
Просьба удалить пробелы, потому что ... гм ... потому что
CalculatorFeline

7

Mathematica

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

Mathematica любит оценивать все , поэтому, чтобы остановить это, мы должны бороться с этим. На своем родном языке.

Как?

f~SetAttributes~HoldAll;

Сообщает Mathematica, что всякий раз, когда fвызывается функция , ее аргументы не должны оцениваться (т.е. храниться).


f[_] = False;

Когда fвызывается с аргументом, выведите False. (?!)


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

когда f вызывается с определенным символом (который имеет значение), выведите имя символа для ввода.

Предыдущая строка не имеет приоритета, несмотря на то, что она была определена ранее, потому что это определение более конкретное. Как сказано в документации Wolfram: 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 #, поэтому я сделал это таким образом. Кроме того, это не код-гольф, так что это не имеет
большого

... извините, не понял, что не было подсчета байтов!
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

р

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

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


4

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

Он использует inspect для просмотра объема окружения и определения места вызова функции 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

@Okx, я знаю, в данный момент у меня нет лучшей идеи.
Йорг Хюльсерманн


4

JavaScript (ES6)

Для всех имен свойств в window( Object.getOwnPropertyNames(w)) попытайтесь определить метод получения для этого свойства, который возвращает имя свойства.

Затем добавьте запись на карту, 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 :: 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);

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

Самый хитрый случай, если мы получим выражение с одним операндом, включающее переменную, такую ​​как ~$x. Мы можем выяснить, произошло ли это, взяв ссылку на переменную непосредственно из таблицы символов (используя${…} синтаксис символьной ссылки) и сравнив ее адрес в памяти со значением, которое мы передали в качестве аргумента (который, как правило, передается по ссылке). ). Если они разные, у нас есть выражение, а не одиночная переменная.

Обратите внимание, что если мы вызываем эту функцию с выражением preincrement или precrement для одной переменной, как в 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 - это выполнить поиск по всем глобальным переменным и вернуть первое совпадение. ОП говорит, что все в порядке, и вправе судить, действительно ли мое решение действительно.

Обратите внимание, что глобальные переменные начинаются с $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.