변수 이름 인쇄 [닫힘]


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

의견은 긴 토론을위한 것이 아닙니다. 이 대화는 채팅 으로 이동 되었습니다 .
Dennis

답변:


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. 이것은 com.sun.tools.javap클래스 파일의 바이트 코드를 구문 분석하고 사람이 읽을 수있는 결과를 생성 하는 내부 Java API 를 사용합니다 . 이 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)

첫 번째 항목은 methodName을 가져올 수있는 예외가 발생하는 행이며 두 번째 항목은 함수가 호출 된 위치입니다.

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

이 줄에서 JDK와 함께 제공되는 javap 실행 파일을 실행합니다. 이 프로그램은 클래스 파일 (바이트 코드)을 구문 분석하고 사람이 읽을 수있는 결과를 제공합니다. 우리는 이것을 기초적인 "구문 분석"에 사용할 것입니다.

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

이 부분은 실제로 변수 인덱스를 얻기 위해로드 호출을 구문 분석합니다. 함수가 실제로 변수로 호출되지 않으면 예외가 발생하여 여기서 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;

마지막으로 로컬 변수 테이블의 행에서 변수 이름을 구문 분석합니다. 왜 이런 일이 발생했는지 알지 못했지만 null을 찾지 못하면 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 행입니다. LineNumberTable의 17 행은 해당 행의 시작이 바이트 코드 행 인덱스 18임을 나타냅니다. 이것이 System.out로드입니다. 그런 다음 aload_2메소드 호출 직전에 LocalVariableTable의 슬롯 2에서 변수를 찾습니다 str.


재미있게, 다음은 같은 라인에서 여러 함수 호출을 처리하는 것입니다. 이로 인해 함수가 not 등 적이지는 않지만 그 점이 중요합니다. 온라인으로 사용해보십시오!


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")..
Olivier Grégoire

@ OlivierGrégoire 감사하지만 내부 java api를 통해 javap를 호출하도록 전환했기 때문에 더 이상 코드에서 디스크의 정확한 위치를 얻을 필요가 없습니다 (클래스 경로 만)
Poke

@ PunPun1000 그렇습니다. pot 등원 기능을 유지하면서 문제를 해결할 수있는 좋은 방법이 있는지 확실하지 않지만 재미를 위해 함수 외부의 상태를 사용하는 무언가를 함께 넣을 수도 있습니다.
Poke

1
@ PunPun1000 한 회선에서 여러 통화를 처리 할 수있는 버전이 추가되었습니다. 그것은 단순한 기능 이상이므로 답을 업데이트하는 대신 다른 TryItOnline 링크를 추가했습니다.
Poke

14

파이썬 2

이것은 내가 작성한 가장 더러운 코드에 관한 것이지만 작동합니다. ¯ \ _ (ツ) _ / ¯ 존재하지 않는 변수에 오류가 발생합니다. 파이썬이 함수를 호출하면 즉시 사용자가 싫어하기 때문입니다. 변수가 아닌 경우에도 오류가 발생하지만 필요한 경우 try / except로 수정할 수 있습니다.

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 항상 편집으로 설명을 게시하고 답변을 추가합니다. ;)
Arjun

8
@Arjun, 오, 그래서 우리는 EditGolf뿐만 아니라 CodeGolf도 재생해야합니다 : P
Wossname

12

매스 매 티카

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

HoldFirst속성 f은 함수를 호출하기 전에 인수를 평가 하지 못하게 합니다. ValueQ @ x그런 다음 주어진 인수가 값이 주어진 변수인지 확인합니다. 그렇지 않으면 False단락으로 인해 돌아옵니다 . 그렇지 않으면로 변수 이름을 얻습니다 ToString @ HoldForm @ x.


Dang, Mathematica가 처리하는 방식을 악용 And... 올바른 인수가 And부울식이 될 필요가없는 방식이 마음 에 듭니다.
JungHwan Min

나는 그것이 골프 코드가 아니라는 것을 알고 있지만 왜 그렇게하지 f[x_] := ValueQ @ x && HoldForm @ x않습니까? 요구 사항이 입력이 변수인지 확인하지 않으면 HoldForm자체적으로 작동합니다.
ngenisis

HoldAll대신에 HoldFirst?
Greg Martin

1
@ngenisis 반환 HoldForm[a]하지 않기 때문에 "a". 그것들은 Mathematica 노트북에 동일하게 표시되지만 함수는 프론트 엔드와 독립적으로 존재하므로 문자열을 반환하는 솔루션을 원했습니다.
Martin Ender

1
공백 제거 요청 때문에 ... 음 ... 때문에
CalculatorFeline

7

매스 매 티카

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

Mathematica는 모든 것을 평가하는 것을 좋아합니다 . 그래서 멈추기 위해서는 우리는 그것에 맞서 싸워야합니다. 자체 언어로.

방법?

f~SetAttributes~HoldAll;

Mathematica에게 함수 f가 호출 될 때마다 그 인수는 평가되지 않아야한다는 것을 알려줍니다 .


f[_] = False;

f인수와 함께 호출 되면 output False. (?!)


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

f정의 된 기호 (값이있는)와 함께 호출 되면 입력의 기호 이름을 출력하십시오.

이 정의가 더 구체적이기 때문에 이전에 정의 된 내용에도 불구하고 이전 행이 우선하지 않습니다. Wolfram 문서에 나와있는 바와 같이 : Mathematica는 "일반적인 정의보다 먼저 구체적인 정의를 시도합니다."

Mathematica는 매우 완고하며 가능할 때마다 변수를 평가하려고 계속 노력합니다. 여분 Unevaluated은 그것을 처리합니다.


7

기음#

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

그러나 이것이 "추상 구문 트리 형식으로 인수를받는"것으로 간주됩니까?
Matti Virkkunen 2016 년

@ VisualMelon C # 에서이 작업을 수행하는 다른 방법을 찾을 수 없으므로이 방법으로 수행했습니다. 또한 이것은 코드 골프 가 아니므로 실제로 중요하지 않습니다.
TheLethalCoder

... 죄송합니다. 바이트 수가 없다는 것을 몰랐습니다!
VisualMelon

6

MATLAB

varname = @(x) inputname(1);

함수 안에는 varargin or 와 같은 몇 가지 사전 설정 변수 nargin가 있습니다 inputname.

여기서 도난 당했어


나는 이것이 존재한다는 것을 결코 알지 못했다. 나는 이것으로 조금 놀았고 아마도 당신은이 보석을 좋아할 것입니다 :x=42;y=54; f=@(x)eval(inputname(1));f(x),f(y)
Sanchises

Haha, eval==evil= D (실제로 작동 x=42;y=54; f=@(x)eval('inputname(1)');f(x),f(y))
flawr

네, 익명 함수는 평가 만 평가할 수 있도록 모든 작업 공간 변수에 대해 알고하지 않습니다 단지의 x다음되는 eval함수의 인수가 아닌 작업 공간 변수를 -ing.
Sanchises 2016 년

6

아르 자형

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

substitute평가되지 않은 표현식에 대한 구문 분석 트리를 리턴합니다. identical이 평가되지 않은 표현이 표현 자체가 동일하지 있는지 조건부 만든다; 즉, 전달 된 매개 변수는 리터럴이 아닙니다.


4

파이썬 2

일부 연구가 수행되었습니다. 그리고 그것은 파이썬에서 가능해 보이지만 여전히 몇 가지 문제가 발견 될 것으로 기대합니다.
솔루션은 코드의 일부 구조를 가정하므로 완벽하지 않습니다. 그러나 나는 아직도 그것을 깨지 않았다.

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

@Okx 아, 죄송합니다.
TessellatingHeckler 2016 년

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 내가 아는 순간 더 좋은 아이디어는 없습니다.
Jörg Hülsermann 2016 년


4

자바 스크립트 (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합니다. 왜 그런 일어날까요?
Caleb Kleveter 2016 년

와우! 그것은 내가 본 것보다 ES6에 더 깊습니다.
MD XF

트윗 담아 가기 두 번째 줄에 쉼표를 잊었습니다. 문제가되었을 수도 있고 아닐 수도 있습니다.
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 (디버거와 유사한 모듈)를 사용하여 호출 스택을 걷고 함수에 대한 호출을 찾고 인수 내의 모든 피연산자를 반환하고 변수 이름으로 반환합니다. 하나의 피연산자보다 많거나 적은 피연산자가 있으면 변수로 호출되지 않았습니다. 피연산자가 변수가 아닌 경우 이름이 없으므로 해당 변수도 감지 할 수 있습니다.

가장 까다로운 경우는 다음과 같은 변수가 포함 된 1- 오퍼랜드 식을 얻는 경우입니다 ~$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örg Hülsermann 2016 년

@ JörgHülsermann 심지어 그것을 향상시킬 수있는 방법을 찾았습니다!
Christoph

3

로다

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에서이 문제를 해결하는 유일한 방법은 모든 전역 변수를 검색하고 첫 번째 일치 항목을 반환하는 것이므로 호출하는 전역 변수에는 다른 전역 변수에없는 고유 한 값이 있어야합니다. OP는 괜찮으며 내 솔루션이 유효한지 판단 할 수 있습니다.

$추가 테스트 사례를 추가하려는 경우 전역 변수 는 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 함수는 매개 변수가 참조 또는 값으로 제공되었는지를 감지 할 수 없습니다. 함수는 값이 함수 매개 변수 값과 일치하는 이름 만 반환합니다.

온라인 테스트




0

자바 스크립트 (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
두 글로벌이 동일한 값을 가지면 이것이 실패한다고 생각합니다.
Martin Ender 2016 년


@ 마틴 엔더; 예, 전달 된 변수에 지정된 값이 해당 변수에 고유 한 것으로 가정합니다. 내가 게시 할 때 포함하는 것을 잊어 버렸습니다.
얽히고 설킨

@CalebKleveter; 그중 일부는 전달중인 변수의 값이 해당 변수에 고유하지 않으며 일부는 유효하지 않은 변수 이름 (예 :) 때문 hello--입니다. 또한 var대신 에 사용해야 let합니다.
얽히고 설킨

1
@CalebKleveter, 및 사이의 범위 차이에 대한 자세한 내용은 여기 를 참조 하십시오 . 두 번째 질문 : 전역 범위 내에 이름이 지정된 변수가 있고 값이 있기 때문에 발생 했습니다 . 위를 테스트하기 전에이를 실행하여 전역 범위의 기존 변수와 값을 확인할 수 있습니다.letvarIN_GLOBAL_SCOPE1for(x in this)console.log(x+": "+this[x])
Shaggy

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.