In tên biến [đã đóng]


20

Viết một hàm (không phải là một chương trình đầy đủ), để nếu hàm được gọi với một biến toàn cục duy nhất (hoặc tương đương gần nhất với ngôn ngữ của bạn) làm đối số của nó, nó sẽ xuất ra (tức là in hoặc trả về) tên của biến đó. Nếu đối số không phải là biến, thay vào đó hãy xuất giá trị falsey. (Bạn không phải xử lý trường hợp đối số là biến, nhưng không phải là toàn cục.)

Không được có kết nối thời gian biên dịch giữa lệnh gọi hàm và định nghĩa hàm (đáng chú ý, định nghĩa hàm không thể là một cấu trúc vĩ mô hoặc tương tự nhận các đối số dưới dạng một đoạn mã nguồn, không phải trong văn bản cũng như cây cú pháp trừu tượng hình thức). Đó là: trong các ngôn ngữ hỗ trợ biên dịch riêng biệt, chương trình phải hoạt động ngay cả khi lệnh gọi hàm được biên dịch trước (không có kiến ​​thức về định nghĩa hàm nhưng có thể là chữ ký loại hoặc tương đương), sau đó định nghĩa hàm được biên dịch sau đó. Nếu ngôn ngữ không có phần biên dịch riêng biệt, dù sao bạn cũng phải nhắm đến một sự tách biệt tương tự giữa cuộc gọi và định nghĩa.

Nếu sử dụng ngôn ngữ được biên dịch, bạn có thể đọc dạng đã biên dịch của chương trình hoàn chỉnh từ đĩa và có thể cho rằng chương trình được biên dịch với thông tin gỡ lỗi. (Do đó, các giải pháp hoạt động bằng cách đính kèm trình gỡ lỗi từ chương trình vào chính nó được cho phép.)

Lưu ý rằng nhiệm vụ này có thể không thực hiện được trong mọi ngôn ngữ.

Ví dụ:

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

Bình luận không dành cho thảo luận mở rộng; cuộc trò chuyện này đã được chuyển sang trò chuyện .
Dennis

Câu trả lời:


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

Điều này hiện đang hoạt động với một vài vấn đề:

  1. Nếu bạn sử dụng IDE để biên dịch thì nó có thể không hoạt động trừ khi nó được chạy với tư cách Quản trị viên (tùy thuộc vào nơi lưu các tệp lớp tạm thời)
  2. Bạn phải biên dịch sử dụng javacvới -glá cờ. Điều này tạo ra tất cả thông tin gỡ lỗi bao gồm tên biến cục bộ trong tệp lớp đã biên dịch.
  3. Điều này sử dụng API Java nội bộ com.sun.tools.javapđể phân tích mã byte của tệp lớp và tạo ra kết quả có thể đọc được của con người. API này chỉ có thể truy cập được trong các thư viện JDK, do đó bạn phải sử dụng thời gian chạy java JDK hoặc thêm tools.jar vào đường dẫn lớp của bạn.

Điều này bây giờ sẽ hoạt động ngay cả khi phương thức được gọi nhiều lần trong chương trình. Thật không may, nó không hoạt động nếu bạn có nhiều lời mời trên một dòng. (Đối với một trong đó, xem bên dưới)

Hãy thử trực tuyến!


Giải trình

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

Phần đầu tiên này có một số thông tin chung về lớp chúng ta đang ở và tên của hàm là gì. Điều này được thực hiện bằng cách tạo một ngoại lệ và phân tích cú pháp 2 mục đầu tiên của theo dõi ngăn xếp.

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

Mục nhập đầu tiên là dòng mà ngoại lệ được ném vào mà chúng ta có thể lấy tên phương thức từ đó và mục nhập thứ hai là nơi hàm được gọi từ đó.

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

Trong dòng này, chúng tôi đang thực thi javap thực thi đi kèm với JDK. Chương trình này phân tích tệp lớp (mã byte) và trình bày kết quả có thể đọc được của con người. Chúng tôi sẽ sử dụng điều này cho "phân tích cú pháp" thô sơ.

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

Chúng tôi đang làm một vài điều khác nhau ở đây. Đầu tiên, chúng tôi đang đọc dòng đầu ra javap theo dòng vào một danh sách. Thứ hai, chúng tôi đang tạo một bản đồ các chỉ mục dòng bytecode thành các chỉ mục dòng javap. Điều này giúp chúng tôi sau này xác định cách gọi phương thức nào chúng tôi muốn phân tích. Cuối cùng, chúng tôi đang sử dụng số dòng đã biết từ theo dõi ngăn xếp để xác định chỉ mục dòng mã byte nào mà chúng tôi muốn xem xét.

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

Ở đây chúng tôi đang lặp lại các dòng javap một lần nữa để tìm vị trí mà phương thức của chúng tôi đang được gọi và nơi Bảng biến cục bộ bắt đầu. Chúng ta cần dòng nơi phương thức được gọi bởi vì dòng trước nó chứa lệnh gọi để tải biến và xác định biến nào (theo chỉ mục) sẽ tải. Bảng biến cục bộ giúp chúng ta thực sự tra cứu tên của biến dựa trên chỉ mục chúng ta đã lấy.

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

Phần này thực sự phân tích cú pháp tải để lấy chỉ số biến. Điều này có thể đưa ra một ngoại lệ nếu hàm không thực sự được gọi với một biến để chúng ta có thể trả về null ở đây.

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;

Cuối cùng, chúng tôi phân tích tên của biến từ dòng trong Bảng biến cục bộ. Trả về null nếu không tìm thấy mặc dù tôi không thấy lý do tại sao điều này sẽ xảy ra.

Để tất cả chúng cùng nhau

 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;

Đây là những gì chúng ta đang xem xét. Trong mã ví dụ, lệnh gọi đầu tiên là dòng 17. dòng 17 trong LineNumberTable cho thấy điểm bắt đầu của dòng đó là chỉ số dòng bytecode 18. Đó là System.outtải. Sau đó, chúng ta có aload_2quyền trước lệnh gọi phương thức để chúng ta tìm biến trong khe 2 của LocalVariableTable strtrong trường hợp này.


Để giải trí, đây là một trong đó xử lý nhiều cuộc gọi chức năng trên cùng một dòng. Điều này làm cho hàm không phải là idempotent nhưng đó là loại điểm. Hãy thử trực tuyến!


1
Yêu câu trả lời này. Đã nghĩ về một cái gì đó dọc theo cùng một dòng. Mặc dù vậy, một lưu ý, nếu bạn bao gồm nhiều cuộc gọi trong cùng một dòng của chương trình, thì nó không thể xác định cuộc gọi nào đang gọi phương thức (không chắc chắn rằng điều này có thể được khắc phục bằng cách tiếp cận hiện tại của bạn), ví dụ, hãy thử di chuyển System.out.println(e.getParamName(str));System.out.println(e.getParamName(str2));System.out.println(e.getParamName(str4));lên một dòng trong liên kết TIO.
PunPun1000

Bạn có thể lấy javapvị trí như thế này : Paths.get(System.getProperty("java.home"), "bin", "javap").
Olivier Grégoire

@ OlivierGrégoire Cảm ơn nhưng tôi đã chuyển sang gọi javap thông qua java api nội bộ vì vậy tôi không cần phải lấy vị trí chính xác trên đĩa trong mã nữa (chỉ là đường dẫn lớp)
Chọc

@ PunPun1000 vâng Tôi không chắc có cách nào tốt để khắc phục điều đó trong khi duy trì chức năng tạm thời nhưng tôi có thể kết hợp một cái gì đó chỉ sử dụng trạng thái bên ngoài chức năng cho vui.
Chọc

1
@ PunPun1000 Đã thêm phiên bản có thể xử lý nhiều cuộc gọi trên một dòng. Đây không chỉ là chức năng nên tôi chỉ thêm một liên kết TryItOnline khác thay vì cập nhật câu trả lời.
Chọc

14

Con trăn 2

Đây là về mã bẩn nhất mà tôi đã viết nhưng nó hoạt động. ¯ \ _ () _ / ¯ Ném lỗi vào một biến không tồn tại vì Python sẽ không thích bạn ngay lập tức khi gọi hàm bằng một. Cũng đưa ra một lỗi trên các biến không nhưng điều này có thể được sửa bằng một lần thử / ngoại trừ nếu cần.

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]

Hãy thử trực tuyến!

Nếu chúng ta được phép lấy đối số làm chuỗi, điều này đáp ứng các yêu cầu xuất ra giá trị sai lệch trên đầu vào không hợp lệ.

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

Hãy thử trực tuyến!


Bạn đã đăng câu trả lời gần như giống nhau, trong khi tôi đang gõ lời giải thích cho tôi: D +1 cho bạn
Dead Possum

1
@DeadPossum Luôn đăng và trả lời và thêm phần giải thích sau này dưới dạng chỉnh sửa. ;)
Arjun

8
@Arjun, ồ thật tuyệt vì vậy chúng tôi phải chơi EditGolf cũng như CodeGolf: P
Wossname

12

Toán học

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

Các HoldFirstthuộc tính ngăn chặn ftừ việc đánh giá đối số của nó trước khi gọi hàm. ValueQ @ xsau đó kiểm tra xem đối số đã cho có phải là biến đã được đưa ra một giá trị hay không. Nếu không chúng tôi chỉ trở về Falsedo ngắn mạch. Nếu không, chúng tôi có được tên biến với ToString @ HoldForm @ x.


Dang, lạm dụng cách Mathicala xử lý And... Tôi thích cách lập luận đúng của Andthậm chí không phải là biểu thức Boolean.
JungHwan Min

Tôi biết đó không phải là mã golf, nhưng tại sao không f[x_] := ValueQ @ x && HoldForm @ x? Nếu không có yêu cầu kiểm tra xem đầu vào có phải là biến hay không, thì HoldFormchính nó sẽ hoạt động.
ngenisis

HoldAllthay vì HoldFirst?
Greg Martin

1
@ngenisis Vì trả lại mà HoldForm[a]không được "a". Mặc dù chúng được hiển thị giống nhau trong sổ ghi chép Mathicala, hàm tồn tại độc lập với frontend, vì vậy tôi muốn có một giải pháp trả về một chuỗi.
Martin Ender

1
Yêu cầu xóa khoảng trắng vì ... ừm ... bởi vì
Máy

7

Toán học

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

Mathematica thích đánh giá mọi thứ , vì vậy để làm cho nó dừng lại, chúng ta phải chiến đấu chống lại nó. Trong ngôn ngữ riêng của mình.

Làm sao?

f~SetAttributes~HoldAll;

Nói với Mathicala rằng bất cứ khi nào hàm fđược gọi, các đối số của nó không nên được đánh giá (nghĩa là được giữ).


f[_] = False;

Khi fđược gọi với một đối số, đầu ra False. (?!)


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

Khi fđược gọi với một Biểu tượng được xác định (có giá trị), hãy xuất tên biểu tượng của đầu vào.

Dòng trước không được ưu tiên, mặc dù đã được xác định trước đó, vì định nghĩa này cụ thể hơn. Như Tài liệu Wolfram nêu: Mathematica "cố gắng đặt các định nghĩa cụ thể trước các định nghĩa chung hơn."

Mathematica rất bướng bỉnh và luôn cố gắng đánh giá các biến bất cứ khi nào có thể. Việc bổ sung Unevaluatedsẽ chăm sóc điều đó.


7

C #

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

Phiên bản đầy đủ / được định dạng:

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

Một cách khác để làm điều này là bằng cách phản ánh kiểu như vậy:

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

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

Tuy nhiên, bạn phải gọi nó như sau:

GetParameterName(new { apple });

Sau đó, nó cũng thất bại 3.14159khi quay trở lại Lengthvà thay vào đó, bạn cũng phải gọi nó như sau:

GetParameterName(new double[]{ 3.14159 });

Trong C # 6.0 chúng ta cũng có:

nameof

Nhưng điều này sẽ không được biên dịch nếu bạn vượt qua nó một cái gì đó không phải là một biến, ví dụ 3.14159. Tôi tin rằng nó cũng đánh giá đầu vào tại thời gian biên dịch nên có vẻ như nó cũng không đạt yêu cầu đó.


Đây là một chút tinh ranh ... bởi vì công cụ lambda là rất cần thiết (nghĩa là bạn không thể chuyển vào biến, bạn phải vượt qua một cái gì đó mà trình biên dịch sẽ nhận ra sẽ trở thành một Expression). Bạn cũng nên được tính using System.Linq.Expressions;.
VisualMelon

Điều này có được tính là "nhận đối số ở dạng cây cú pháp trừu tượng" không?
Matti Virkkunen

@VisualMelon Tôi không thể tìm thấy bất kỳ cách nào khác để làm điều này trong C # vì vậy tôi đã làm theo cách này. Ngoài ra, đây không phải là môn đánh gôn nên nó không thực sự quan trọng,
TheLethalCoder

... xin lỗi, không nhận ra rằng không có số byte!
VisualMelon

6

MATLAB

varname = @(x) inputname(1);

Trong một hàm có một vài biến đặt trước như varargin hoặc nargin, trong số các biến chúng ta cũng có inputname.

bị đánh cắp từ đây


Tôi không bao giờ biết điều này tồn tại. Tôi đã chơi xung quanh với điều này một chút và có lẽ bạn sẽ thích viên ngọc này:x=42;y=54; f=@(x)eval(inputname(1));f(x),f(y)
Sanchise

Haha, eval==evil= D (nó thực sự hoạt động với x=42;y=54; f=@(x)eval('inputname(1)');f(x),f(y))
flawr

Vâng, chỉ là một hàm ẩn danh không biết về bất kỳ biến không gian làm việc nào, vì vậy eval chỉ có thể đánh giá xvà sau đó là evalđối số của hàm chứ không phải biến không gian làm việc.
Sanchise

6

R

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

substitutetrả về cây phân tích cho một biểu thức không được đánh giá. Điều identicalkiện đảm bảo rằng biểu thức không được đánh giá này không giống với chính biểu thức đó; tức là thông số được truyền không phải là chữ.


4

Con trăn 2

Một số nghiên cứu đã được thực hiện. Và nó dường như có thể ở trăn, nhưng tôi vẫn mong đợi một số rắc rối được tìm thấy.
Giải pháp không hoàn hảo, vì nó giả định một số cấu trúc của mã. Tuy nhiên, tôi đã không phá vỡ nó vẫn còn.

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

Điều này sử dụng kiểm tra để xem xét phạm vi bao quanh và tìm nơi chức năng get_nameđược gọi. Ví dụ inspect...[1]sẽ trở lại

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

inspect...[1][4]sẽ hiển thị cho chúng tôi danh sách với mã, trong đó chức năng được gọi là:

['print get_name( a )\n']

Sau đó, tôi đã sử dụng regex để lấy tên của đối số

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

.lstrip().rstrip()để loại bỏ tất cả các khoảng trắng có thể được đặt vào nẹp.

Ví dụ với một số đầu vào khó khăn


4

PowerShell

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

$apple = 'hi'
t $apple
apple

t 3.141

Nhưng nó không hoạt động đáng tin cậy, nó thô sơ.


Đây không phải là mã golf.
Okx

@Okx oh, ôi, vậy thì không.
TessellatingHeckler

4

PHP

Cần rằng giá trị biến là duy nhất trong mảng các biến toàn cục

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

Hãy thử trực tuyến!

PHP , 96 byte

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

Hãy thử trực tuyến!

Cần rằng biến được khởi tạo trực tiếp cho lệnh gọi hàm.

Kiểm tra xem biến toàn cục cuối cùng có phải là biến đầu hàng không


Đây không phải là mã golf.
Okx

@Okx Tôi biết Hiện tại tôi không có ý tưởng nào tốt hơn.
Jörg Hülsermann


4

JavaScript (ES6)

Đối với tất cả các tên thuộc tính trong window( Object.getOwnPropertyNames(w)), hãy cố gắng xác định một getter cho thuộc tính đó trả về tên thuộc tính.

Sau đó, thêm một mục vào Bản đồ M trong đó khóa là giá trị (có thể bị ghi đè) của thuộc tính và giá trị là tên của thuộc tính.

Hàm fchỉ cần lấy một biến (tức là thuộc tính của window) và trả về mục trongM cho giá trị đó.

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)

Điều này hoạt động cho tất cả các biến toàn cục tích hợp ngoại trừ windowchính nó, vì không có cách nào để phân biệt nó với top(trừ khi chạy trong một khung):

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

Vì một số lý do, tôi nhận được một lỗi : L not a function. Tại sao điều đó sẽ xảy ra?
Caleb Kleveter

Ồ Điều đó sâu hơn về ES6 mà tôi đã thấy trong một thời gian.
MD XF

@CalebKleveter! Tôi quên một dấu phẩy trên dòng thứ hai. Điều đó có thể hoặc không thể có vấn đề.
darrylyeo

3

Perl 5 + Devel :: Người gọi

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

Chúng tôi sử dụng Devel :: Caller (một mô-đun giống như trình gỡ lỗi) để đi bộ ngăn xếp cuộc gọi, tìm kiếm cuộc gọi đến hàm và trả về tất cả các toán hạng trong đối số, trả về chúng dưới dạng tên biến. Nếu có nhiều (hoặc ít hơn) một toán hạng, chúng ta sẽ không được gọi với một biến. Nếu toán hạng không phải là một biến, nó sẽ không có tên và chúng ta cũng có thể phát hiện ra điều đó.

Trường hợp khó nhất là nếu chúng ta có một biểu thức một toán hạng liên quan đến một biến, chẳng hạn như ~$x . Chúng ta có thể tìm hiểu xem điều này có xảy ra hay không bằng cách lấy tham chiếu đến biến trực tiếp từ bảng ký hiệu (sử dụng ${…}cú pháp tham chiếu ký hiệu) và so sánh địa chỉ bộ nhớ của nó với giá trị mà chúng ta đã truyền qua như một đối số (một cách thuận tiện, được truyền bởi tham chiếu ). Nếu chúng khác nhau, chúng ta có một biểu thức chứ không phải là một biến đơn độc.

Lưu ý rằng nếu chúng ta gọi hàm này với một biểu thức preincrement hoặc preecrement trên một biến duy nhất, như trong v(--$x), chúng ta sẽ được $xtrả về. Điều này là do thực sự nó là biến được truyền cho hàm trong trường hợp này; nó chỉ được tăng hoặc giảm trước. Tôi hy vọng điều này không đủ tiêu chuẩn cho câu trả lời. (Theo một cách nào đó, nó làm cho nó tốt hơn, bởi vì nó cho thấy rằng chúng ta đang kiểm tra chính đối số thay vì chỉ đọc mã nguồn.)


3

PHP

Mặc dù các đệ trình PHP khác chỉ kiểm tra xem giá trị đã cho có khớp với giá trị toàn cầu hay không, phiên bản này hoạt động bằng cách tham chiếu đến giá trị:

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

Điều này bây giờ sẽ hoạt động ngay cả khi biến toàn cục là tham chiếu đến giá trị khác tại thời điểm gọi.

Kiểm tra nó ở đây .


Tuyệt vời và cảm ơn bạn rất nhiều vì nỗ lực học tập
Jörg Hülsermann

@ JörgHülsermann Thậm chí tìm thấy một cách để cải thiện nó!
Christoph

3

Röda

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

Hãy thử trực tuyến!

Röda có chức năng dựng sẵn cho việc này - name . Thật không may, mặc dù, nó không trả về giá trị giả khi không được cung cấp một biến.

Mã này lạm dụng một số tính năng của ngữ nghĩa tham chiếu. Dòng giải thích theo từng dòng:

f(&a...) {

Hàm lấy một số lượng đối số tham chiếu ( &a...). Đây là cách duy nhất trong Röda để tạo một danh sách các tài liệu tham khảo.

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

Ở dòng thứ hai, các phần tử ađược đẩy vào luồng (a() ). Các phần tử này là các tham chiếu nếu hàm được đưa ra các biến và các giá trị bình thường khác.

Các phần tử được lặp lại bằng cách sử dụng một vòng lặp gạch dưới. Cú pháp gạch dưới là cú pháp đường cho forcác vòng lặp, do đó name(_)tương đương với name(var) for var. Tên của biến được sử dụng trong forvòng lặp có dạng <sfvN>N thay đổi. (Tức là. " name(<sfv1>) for <sfv1>") Việc chứa biến do người dùng xác định là bất hợp pháp< hoặc> do đó, các tên được tạo không xung đột với các tên biến hiện có.

Các name()chức năng trả về tên của biến nhất định. Nếu phần tử ađược lặp đi lặp lại là một tham chiếu, thì đó là biến được đưa ra choname . Mặt khác, nếu phần tử là một giá trị bình thường, biến được đưa ra namelà biến gạch dưới <sfvN>. Điều này là do ngữ nghĩa của các tham chiếu trong Röda: nếu một hàm chấp nhận một tham chiếu và hàm được truyền một biến tham chiếu, giá trị được truyền không chỉ đến biến tham chiếu mà là biến của biến tham chiếu trỏ tới.

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

Ở dòng thứ ba, chúng tôi kiểm tra xem tên biến trong luồng có chứa <ký tự không. Nếu vậy, nó là một biến gạch dưới và giá trị được truyền chof không phải là một tham chiếu. Nếu không, chúng tôi xuất tên của tài liệu tham khảo.

Giải pháp này không hoạt động nếu biến được cung cấp cho hàm là tham chiếu hoặc biến gạch dưới. Tuy nhiên, câu hỏi chỉ rõ rằng chỉ các biến toàn cục phải được xử lý và chúng không thể là tham chiếu hoặc biến gạch dưới trong Röda.


1

Hồng ngọc , 46 byte

Cảm thấy giống như mã bẩn nhất mà tôi từng viết cho Ruby.

Yêu cầu các biến toàn cục mà bạn gọi có một giá trị duy nhất không có trên bất kỳ biến toàn cục nào khác, bởi vì cách duy nhất để thực hiện thử thách này trong Ruby là tìm kiếm trên tất cả các biến toàn cục và trả về kết quả khớp đầu tiên. OP nói rằng nó ổn, và được tự do đánh giá nếu giải pháp của tôi là hợp lệ.

Lưu ý rằng các biến toàn cục bắt đầu bằng $Ruby, nếu bạn muốn thêm công cụ kiểm tra trường hợp bổ sung.

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

Hãy thử trực tuyến!


1

PHP

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

nếu giá trị được tìm thấy, in tên biến và thoát. không in gì và không thoát ra.

61 byte để trả về tên biến hoặc NULL:

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

Nó sẽ không tìm thấy các hàm được đặt tên, chỉ những hàm được gán cho biến.
Và một hàm PHP không thể phát hiện ra một tham số được cung cấp bởi tham chiếu hoặc theo giá trị. Hàm sẽ chỉ trả về tên đầu tiên trong đó giá trị khớp với giá trị tham số hàm.

Kiểm tra trực tuyến


1

PowerShell

Phiên bản mới nhưng hoạt động bắt đầu từ PowerShell 3.0

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

Dùng thử trực tuyến!

Phiên bản trước

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

Dùng thử trực tuyến!



0

JavaScript (ES6)

Yêu cầu giá trị của biến được truyền cho hàm là duy nhất cho biến đó. Trả về undefinednếu một biến không được thông qua.

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

Thử nó

Vì một số lý do, nó đưa ra lỗi xuất phát chéo trong Đoạn trích khi "nghĩa đen" được thông qua.

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


Giải trình

Lặp lại tất cả các mục trong đối tượng toàn cầu ( this), kiểm tra xem giá trị của mỗi mục có hoàn toàn bằng với giá trị của đối số được truyền cho hàm không. Nếu một mục phù hợp được tìm thấy thì khóa (tên) của nó được trả về, thoát khỏi chức năng.


Thay thế

Với các yêu cầu tương tự như trên

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

2
Tôi nghĩ điều này thất bại nếu hai quả cầu có cùng giá trị.
Martin Ender

1
Tôi nhận được một số đầu ra rất kỳ lạ .
Caleb Kleveter

@MartinEnder; có, nó giả định rằng giá trị được gán cho biến được truyền là duy nhất cho biến đó; quên bao gồm điều đó khi tôi đăng.
Xù xì

@CalebKleveter; một số trong số đó là do giá trị của biến bạn chuyển không phải là duy nhất cho biến đó và một số là do tên biến không hợp lệ (ví dụ hello--:). Ngoài ra, bạn cần sử dụng varhơn là let.
Xù xì

1
@CalebKleveter, xem tại đây để biết thêm thông tin về sự khác biệt trong phạm vi giữa letvar. Đối với câu hỏi thứ hai của bạn: Điều đó xảy ra bởi vì bạn có một biến được đặt tên IN_GLOBAL_SCOPEtrong phạm vi toàn cầu của bạn và nó có giá trị là 1. Bạn có thể kiểm tra các biến hiện có trong phạm vi toàn cầu của mình và các giá trị của chúng bằng cách chạy biến này trước khi kiểm tra ở trên:for(x in this)console.log(x+": "+this[x])
Shaggy

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.