พิมพ์ชื่อตัวแปร [ปิด]


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

ปัจจุบันนี้ใช้งานได้กับ gotchas สองสาม:

  1. ถ้าคุณใช้ IDE เพื่อคอมไพล์สิ่งนี้มันอาจไม่ทำงานจนกว่าจะถูกเรียกใช้ในฐานะผู้ดูแลระบบ (ขึ้นอยู่กับตำแหน่งที่บันทึกไฟล์คลาสชั่วคราว)
  2. คุณต้องรวบรวมโดยใช้javacกับ-gธง สิ่งนี้สร้างข้อมูลการดีบักทั้งหมดรวมถึงชื่อตัวแปรโลคัลในไฟล์คลาสที่คอมไพล์
  3. สิ่งนี้ใช้ Java API ภายในcom.sun.tools.javapซึ่งแยกวิเคราะห์ bytecode ของ classfile และสร้างผลลัพธ์ที่มนุษย์สามารถอ่านได้ API นี้สามารถเข้าถึงได้ในไลบรารี JDK เท่านั้นดังนั้นคุณต้องใช้ JDK java runtime หรือเพิ่ม tools.jar ใน classpath ของคุณ

ตอนนี้ควรใช้งานได้แม้ว่าวิธีการนั้นจะเรียกว่าหลายครั้งในโปรแกรม น่าเสียดายที่มันยังไม่สามารถใช้งานได้หากคุณมีการเรียกใช้หลายรายการในบรรทัดเดียว (สำหรับสิ่งที่ทำโปรดดูด้านล่าง)

ลองออนไลน์!


คำอธิบาย

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)

รายการแรกคือบรรทัดที่มีข้อยกเว้นเกิดขึ้นซึ่งเราสามารถคว้าเมธอดจากและรายการที่สองคือตำแหน่งที่เรียกใช้ฟังก์ชัน

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

ในบรรทัดนี้เรากำลังเรียกใช้งาน javap ที่มากับ JDK โปรแกรมนี้แยกวิเคราะห์ไฟล์คลาส (bytecode) และนำเสนอผลลัพธ์ที่มนุษย์สามารถอ่านได้ เราจะใช้สิ่งนี้เป็น "การแยกวิเคราะห์" ขั้นพื้นฐาน

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 เอาต์พุตบรรทัดโดยบรรทัดลงในรายการ ประการที่สองเรากำลังสร้างแผนที่ของดัชนีเส้น bytecode เพื่อดัชนี javap line สิ่งนี้จะช่วยเราในการพิจารณาการเรียกใช้เมธอดที่เราต้องการวิเคราะห์ ในที่สุดเรากำลังใช้หมายเลขบรรทัดที่รู้จักจากการติดตามสแต็กเพื่อกำหนดดัชนีบรรทัด bytecode ที่เราต้องการดู

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 line อีกครั้งเพื่อค้นหาจุดที่วิธีการของเราถูกเรียกใช้และจุดเริ่มต้นของ Local Variable Table เราต้องการบรรทัดที่เรียกใช้เมธอดเนื่องจากบรรทัดก่อนที่จะมีการเรียกเพื่อโหลดตัวแปรและระบุว่าตัวแปรใด (โดยดัชนี) ที่จะโหลด Local Variable Table ช่วยให้เราค้นหาชื่อของตัวแปรตามดัชนีที่เราคว้า

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;

ในที่สุดเราก็แยกชื่อของตัวแปรออกจากบรรทัดใน Local Variable Table ส่งคืน 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 แสดงให้เห็นว่าจุดเริ่มต้นของบรรทัดนั้นคือดัชนีบรรทัด bytecode 18 นั่นคือการSystem.outโหลด จากนั้นเรามีaload_2สิทธิ์ก่อนการเรียกเมธอดดังนั้นเราจึงค้นหาตัวแปรในช่องที่ 2 ของ LocalVariableTable ซึ่งอยู่strในกรณีนี้


เพื่อความสนุกนี่คือสายที่รองรับการใช้งานหลายฟังก์ชั่นในสายเดียวกัน สิ่งนี้ทำให้ฟังก์ชันไม่เป็น idempotent แต่เป็นจุดสำคัญ ลองออนไลน์!


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ขอบคุณ แต่ผมเปลี่ยนไปกล่าวอ้าง javap ผ่านใน Java API ภายในดังนั้นผมจึงไม่จำเป็นต้องได้รับตำแหน่งที่แน่นอนบนดิสก์ในรหัสอีกต่อไป (เพียงคลาสพา ธ )
Poke

@ PunPun1000 ใช่ฉันไม่แน่ใจว่ามีวิธีที่ดีในการแก้ไขขณะที่ยังคงฟังก์ชัน idempotent แต่ฉันอาจรวบรวมบางอย่างที่ใช้สถานะนอกฟังก์ชันเพื่อความสนุก
Poke

1
@ PunPun1000 เพิ่มรุ่นที่สามารถรองรับการโทรหลายสายในหนึ่งบรรทัด มันเป็นมากกว่าฟังก์ชั่นดังนั้นฉันเพิ่งเพิ่มลิงค์ TryItOnline อีกลิงก์แทนที่จะอัปเดตคำตอบ
Poke

14

Python 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
@Arjun โอ้เยี่ยมมากเราต้องเล่น EditGolf เช่นเดียวกับ CodeGolf: P
Wossname

12

มาติกา

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

HoldFirstป้องกันแอตทริบิวต์fจากการประเมินผลการโต้แย้งก่อนที่จะเรียกใช้ฟังก์ชั่น ValueQ @ xจากนั้นตรวจสอบว่าอาร์กิวเมนต์ที่กำหนดเป็นตัวแปรที่ได้รับค่าหรือไม่ ถ้าไม่ใช่เราเพิ่งกลับมาFalseเนื่องจากการลัดวงจร ToString @ HoldForm @ xมิฉะนั้นเราจะได้รับชื่อตัวแปรที่มี


แดงการเหยียดหยามว่า 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 False(?!)


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

เมื่อfถูกเรียกพร้อมกับ Symbol ที่กำหนด (ซึ่งมีค่า) ให้เอาท์พุทชื่อสัญลักษณ์ของอินพุต

บรรทัดก่อนหน้าไม่ได้มีความสำคัญแม้จะมีการกำหนดไว้ก่อนหน้านี้เนื่องจากคำจำกัดความนี้มีความเฉพาะเจาะจงมากขึ้น ดังที่ Wolfram Documentation กล่าวว่า: 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

@VisualMelon ฉันไม่สามารถหาวิธีอื่นใน C # ดังนั้นฉันจึงทำเช่นนี้ นอกจากนี้นี่ไม่ใช่รหัสกอล์ฟดังนั้นจึงไม่สำคัญจริงๆ
TheLethalCoder

... ขออภัยไม่ทราบว่าไม่มีการนับไบต์!
VisualMelon

6

MATLAB

varname = @(x) inputname(1);

ฟังก์ชั่นภายในมีตัวแปรที่กำหนดไว้ล่วงหน้าน้อยเช่นvarargin หรือในหมู่คนที่เรายังมีnargininputname

ถูกขโมยจากที่นี่


ฉันไม่เคยรู้เรื่องนี้มาก่อน ฉันเล่นรอบนี้นิดหน่อยและบางทีคุณอาจจะชอบอัญมณีนี้:x=42;y=54; f=@(x)eval(inputname(1));f(x),f(y)
Sanchises

ฮ่าฮ่าeval==evil= D (ใช้งานได้จริงx=42;y=54; f=@(x)eval('inputname(1)');f(x),f(y))
ข้อผิดพลาด

ใช่มันเป็นเพียงแค่ฟังก์ชั่นที่ไม่ระบุชื่อไม่ทราบเกี่ยวกับตัวแปรเวิร์กสเปซใด ๆ ดังนั้น eval สามารถประเมินได้เท่านั้นxและจากนั้นจึงevalใช้อาร์กิวเมนต์ของฟังก์ชันไม่ใช่ตัวแปรเวิร์กสเปซ
Sanchises

6

R

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

substituteส่งคืนต้นไม้การแยกวิเคราะห์สำหรับการแสดงออกที่ไม่ได้ประเมินค่า identicalทำให้แน่ใจว่าเงื่อนไขนี้แสดงออก unevaluated ไม่เหมือนกันกับการแสดงออกของตัวเอง; นั่นคือพารามิเตอร์ที่ส่งผ่านไม่ใช่ตัวอักษร


4

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

สิ่งนี้ใช้การตรวจสอบเพื่อดูขอบเขตรอบทิศทางและค้นหาตำแหน่งที่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']

หลังจากนั้นฉันใช้ regex เพื่อดึงชื่อของอาร์กิวเมนต์

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 ฉันรู้ว่าในขณะนี้ฉันไม่มีความคิดที่ดีขึ้น
JörgHülsermann


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. ทำไมจะว่าเกิดขึ้นได้อย่างไร
Caleb Kleveter

ว้าว! มันลึกเข้าไปใน 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 (โมดูลเหมือนดีบักเกอร์) เพื่อเดิน call stack ค้นหาการเรียกไปยังฟังก์ชันและคืนค่าตัวถูกดำเนินการทั้งหมดภายในอาร์กิวเมนต์ส่งกลับเป็นชื่อตัวแปร หากมีตัวถูกดำเนินการมากกว่าหนึ่งตัว (หรือน้อยกว่า) เราจะไม่เรียกใช้ตัวแปร หากตัวถูกดำเนินการไม่ใช่ตัวแปรมันจะไม่มีชื่อและเราก็สามารถตรวจจับได้เช่นกัน

~$xกรณีที่ยากคือถ้าเราได้รับหนึ่งตัวถูกดำเนินการแสดงออกที่เกี่ยวข้องกับตัวแปรเช่น เราสามารถทราบได้ว่าเกิดกรณีนี้ขึ้นโดยการอ้างอิงตัวแปรโดยตรงจากตารางสัญลักษณ์ (ใช้${…}ไวยากรณ์การอ้างอิงสัญลักษณ์) และเปรียบเทียบที่อยู่หน่วยความจำของมันกับค่าที่เราถูกส่งผ่านเป็นอาร์กิวเมนต์ (ซึ่งสะดวกส่งผ่านการอ้างอิง ) หากพวกเขาแตกต่างกันเรามีการแสดงออกมากกว่าตัวแปรเดียว

โปรดทราบว่าถ้าเราเรียกใช้ฟังก์ชันนี้โดยมีนิพจน์ preincrement หรือ predecrement ในตัวแปรเดียวดังที่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

@ JörgHülsermannแม้แต่ค้นพบวิธีปรับปรุงมัน!
Christoph

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 แตกต่างกันไป (Ie. " name(<sfv1>) for <sfv1>") มันผิดกฎหมายที่ตัวแปรที่ผู้ใช้กำหนดจะต้องมี<หรือ>ดังนั้นชื่อที่สร้างขึ้นจะไม่ขัดแย้งกับชื่อตัวแปรที่มีอยู่

name()ฟังก์ชันส่งกลับชื่อของตัวแปรที่กำหนด ถ้าองค์ประกอบในการถูกซ้ำคือการอ้างอิงแล้วมันเป็นตัวแปรที่กำหนดให้a nameมิฉะนั้นถ้าองค์ประกอบเป็นค่าปกติตัวแปรที่กำหนดให้เป็นตัวแปรขีดname <sfvN>นี่คือความหมายของการอ้างอิงในRöda: ถ้าฟังก์ชั่นยอมรับการอ้างอิงและฟังก์ชั่นจะถูกส่งผ่านตัวแปรอ้างอิงค่าส่งผ่านไม่ได้ชี้ไปที่ตัวแปรอ้างอิง แต่ตัวแปรตัวแปรที่อ้างอิงชี้ไปที่

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

ที่บรรทัดที่สามเราตรวจสอบว่าชื่อตัวแปรในสตรีมมี<อักขระหรือไม่ ถ้าเป็นเช่นนั้นมันเป็นตัวแปรขีดล่างและค่าที่ส่งไปfยังไม่ใช่การอ้างอิง มิฉะนั้นเราจะเอาท์พุทชื่อของการอ้างอิง

วิธีนี้ไม่ทำงานหากตัวแปรที่กำหนดให้กับฟังก์ชั่นเป็นการอ้างอิงหรือตัวแปรขีดล่าง อย่างไรก็ตามคำถามระบุว่าจะต้องจัดการกับตัวแปรโกลบอลเท่านั้นและพวกเขาไม่สามารถอ้างอิงหรือขีดเส้นใต้ตัวแปรในRödaได้


1

Ruby , 46 ไบต์

รู้สึกเหมือนรหัสที่สกปรกที่สุดที่ฉันเคยเขียนให้ Ruby

ต้องการให้ตัวแปรโกลบอลที่คุณเรียกใช้มีค่าเฉพาะที่ไม่ได้อยู่ในตัวแปรโกลบอลอื่น ๆ เนื่องจากวิธีเดียวที่จะทำสิ่งนี้ใน 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 ไม่สามารถตรวจสอบได้ว่ามีการให้พารามิเตอร์โดยการอ้างอิงหรือตามค่า ฟังก์ชันจะส่งคืนชื่อแรกโดยที่ค่านั้นตรงกับค่าพารามิเตอร์ของฟังก์ชัน

ทดสอบออนไลน์


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
ฉันคิดว่าสิ่งนี้จะล้มเหลวถ้าสองกลมมีค่าเท่ากัน
Martin Ender

1
ฉันได้รับบางส่วนเอาท์พุทที่แปลกมาก
Caleb Kleveter

@MartinEnder; ใช่มันจะถือว่าค่าที่กำหนดให้กับตัวแปรที่ส่งนั้นเป็นค่าเฉพาะสำหรับตัวแปรนั้น ลืมที่จะรวมว่าเมื่อฉันโพสต์
Shaggy

@CalebKleveter; บางส่วนเกิดจากข้อเท็จจริงที่ว่าค่าของตัวแปรที่คุณส่งผ่านนั้นไม่เหมือนกันกับตัวแปรนั้นและบางส่วนเกิดจากชื่อตัวแปรที่ไม่ถูกต้อง (เช่นhello--) นอกจากนี้คุณยังจะต้องใช้มากกว่าvar let
Shaggy

1
@CalebKleveter ดูที่นี่สำหรับข้อมูลเพิ่มเติมเกี่ยวกับความแตกต่างในการกำหนดขอบเขตระหว่างและlet varคำถามที่สองของคุณ: ที่เกิดขึ้นเพราะคุณมีตัวแปรที่มีชื่ออยู่ในขอบเขตทั่วโลกของคุณและมันมีค่าIN_GLOBAL_SCOPE 1คุณสามารถตรวจสอบตัวแปรที่มีอยู่ในขอบเขตทั่วโลกและค่าของพวกเขาโดยการเรียกใช้นี้ก่อนการทดสอบข้างต้น:for(x in this)console.log(x+": "+this[x])
Shaggy

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.