Cetak Nama Variabel [ditutup]

20

Tulis fungsi (bukan program lengkap), sehingga jika fungsi dipanggil dengan variabel global tunggal (atau bahasa terdekat terdekat Anda) sebagai argumennya, ia mengeluarkan (yaitu mencetak atau mengembalikan) nama variabel itu. Jika argumennya bukan variabel, output nilai falsey sebagai gantinya. (Anda tidak harus menangani kasus di mana argumennya adalah variabel, tetapi bukan global.)

Tidak boleh ada koneksi waktu kompilasi antara pemanggilan fungsi dan definisi fungsi (terutama, definisi fungsi tidak boleh makro atau konstruksi serupa yang menerima argumen dalam bentuk fragmen literal dari kode sumber, baik dalam teks maupun pohon sintaksis abstrak bentuk). Yaitu: dalam bahasa yang mendukung kompilasi terpisah, program harus bekerja bahkan jika pemanggilan fungsi dikompilasi terlebih dahulu (tanpa pengetahuan tentang definisi fungsi tetapi mungkin tipe tanda tangan atau setara), maka definisi fungsi dikompilasi setelahnya. Jika bahasa tidak memiliki kompilasi terpisah, Anda harus tetap bertujuan untuk pemisahan yang sama antara panggilan dan definisi.

Jika menggunakan bahasa yang dikompilasi, Anda dapat membaca formulir yang dikompilasi dari program lengkap dari disk, dan dapat berasumsi bahwa program itu dikompilasi dengan informasi debug. (Dengan demikian, solusi yang berfungsi dengan melampirkan debugger dari suatu program ke program itu sendiri diperbolehkan.)

Perhatikan bahwa tugas ini mungkin tidak dapat dilakukan dalam setiap bahasa.

Contoh:

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
Caleb Kleveter
sumber
Komentar bukan untuk diskusi panjang; percakapan ini telah dipindahkan ke obrolan .
Dennis

Jawaban:

31

Jawa

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

Ini saat ini bekerja dengan beberapa gotcha:

  1. Jika Anda menggunakan IDE untuk mengkompilasi ini, itu mungkin tidak berfungsi kecuali dijalankan sebagai Admin (tergantung di mana file kelas sementara disimpan)
  2. Anda harus mengkompilasi menggunakan javacdengan -gbendera. Ini menghasilkan semua informasi debugging termasuk nama variabel lokal di file kelas yang dikompilasi.
  3. Ini menggunakan Java API internal com.sun.tools.javapyang mem-parsing bytecode dari classfile dan menghasilkan hasil yang dapat dibaca manusia. API ini hanya dapat diakses di perpustakaan JDK sehingga Anda harus menggunakan runtime java JDK atau menambahkan tools.jar ke classpath Anda.

Ini sekarang harus bekerja bahkan jika metode ini dipanggil beberapa kali dalam program. Sayangnya itu belum berfungsi jika Anda memiliki banyak pemanggilan dalam satu baris. (Untuk yang melakukannya, lihat di bawah)

Cobalah online!


Penjelasan

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

Bagian pertama ini mendapat beberapa informasi umum tentang kelas apa kita berada dan apa nama fungsinya. Ini dilakukan dengan membuat pengecualian dan mem-parsing 2 entri pertama dari jejak stack.

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

Entri pertama adalah baris di mana pengecualian dilemparkan di mana kita dapat mengambil methodName dari dan entri kedua adalah tempat fungsi dipanggil.

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

Pada baris ini kita mengeksekusi javap executable yang datang dengan JDK. Program ini mem-parsing file kelas (bytecode) dan menyajikan hasil yang dapat dibaca manusia. Kami akan menggunakan ini untuk "parsing" yang belum sempurna.

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

Kami melakukan beberapa hal berbeda di sini. Pertama, kita membaca baris keluaran javap demi baris ke dalam daftar. Kedua kita membuat peta indeks garis bytecode ke indeks garis javap. Ini membantu kita nanti untuk menentukan metode doa mana yang ingin kita analisis. Akhirnya kita menggunakan nomor baris yang diketahui dari jejak tumpukan untuk menentukan indeks garis bytecode yang ingin kita lihat.

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

Di sini kita mengulangi garis javap sekali lagi untuk menemukan tempat di mana metode kita dipanggil dan di mana Tabel Variabel Lokal dimulai. Kita perlu baris tempat metode dipanggil karena baris sebelum itu berisi panggilan untuk memuat variabel dan mengidentifikasi variabel mana (berdasarkan indeks) untuk memuat. Tabel Variabel Lokal membantu kita mencari nama variabel berdasarkan indeks yang kita ambil.

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

Bagian ini sebenarnya mengurai panggilan beban untuk mendapatkan indeks variabel. Ini dapat membuang pengecualian jika fungsi tersebut tidak benar-benar dipanggil dengan variabel sehingga kita dapat mengembalikan nol di sini.

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;

Akhirnya kita menguraikan nama variabel dari baris di Tabel Variabel Lokal. Kembalikan nol jika tidak ditemukan walaupun saya tidak melihat alasan mengapa ini harus terjadi.

Menyatukan semuanya

 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;

Ini pada dasarnya adalah apa yang kita lihat. Dalam kode contoh pemanggilan pertama adalah baris 17. baris 17 di LineNumberTable menunjukkan bahwa awal baris itu adalah bytecode indeks baris 18. Itu adalah System.outbeban. Kemudian kita memiliki aload_2tepat sebelum pemanggilan metode sehingga kita mencari variabel dalam slot 2 dari LocalVariableTable yang strdalam hal ini.


Untuk bersenang-senang, inilah yang menangani beberapa panggilan fungsi pada saluran yang sama. Ini menyebabkan fungsi tidak idempoten tapi itu intinya. Cobalah online!

Menyodok
sumber
1
Sukai jawaban ini. Sedang memikirkan sesuatu yang sama. Namun satu catatan, jika Anda memasukkan beberapa panggilan dalam satu baris program yang sama, maka ia tidak dapat menentukan yang mana yang memanggil metode (tidak yakin bahwa ini dapat diperbaiki dengan pendekatan Anda saat ini), misalnya, coba pindahkan System.out.println(e.getParamName(str));System.out.println(e.getParamName(str2));System.out.println(e.getParamName(str4));ke satu baris di tautan TIO.
PunPun1000
Anda dapat mengambil javaplokasi seperti ini: Paths.get(System.getProperty("java.home"), "bin", "javap").
Olivier Grégoire
@ OlivierGrégoire Terima kasih, tetapi saya beralih untuk memohon javap melalui api java internal jadi saya tidak perlu lagi mendapatkan lokasi yang tepat pada disk dalam kode (hanya classpath)
Poke
@ PunPun1000 ya saya tidak yakin apakah ada cara yang baik untuk memperbaikinya sambil mempertahankan fungsi idempoten tapi saya mungkin menyatukan sesuatu yang hanya menggunakan keadaan di luar fungsi untuk bersenang-senang.
Poke
1
@ PunPun1000 Menambahkan versi yang dapat menangani beberapa panggilan dalam satu saluran. Ini lebih dari sekedar fungsi jadi saya hanya menambahkan tautan TryItOnline lain daripada memperbarui jawabannya.
Poke
14

Python 2

Ini tentang kode paling kotor yang saya tulis tetapi berfungsi. ¯ \ _ (ツ) _ / ¯ Melempar kesalahan pada variabel yang tidak ada karena Python akan segera membenci Anda karena memanggil fungsi dengan satu. Juga melempar kesalahan pada non-variabel tetapi ini bisa diperbaiki dengan coba / kecuali jika diperlukan.

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]

Cobalah online!

Jika kami diizinkan untuk mengambil argumen sebagai string, ini memenuhi persyaratan untuk menghasilkan nilai palsu pada input yang tidak valid.

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

Cobalah online!

benar-benar manusiawi
sumber
Anda memposting jawaban yang hampir sama, ketika saya mengetikkan penjelasan untuk saya: D +1 untuk Anda
Dead Possum
1
@DeadPossum Selalu kirim dan jawab dan tambahkan penjelasan nanti sebagai hasil edit. ;)
Arjun
8
@Arjun, oh bagus jadi kita harus bermain EditGolf dan CodeGolf: P
Wossname
12

Mathematica

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

The HoldFirstatribut mencegah fdari mengevaluasi argumen sebelum memanggil fungsi. ValueQ @ xkemudian memeriksa apakah argumen yang diberikan adalah variabel yang telah diberi nilai. Jika tidak, kami hanya kembali Falsekarena hubungan arus pendek. Jika tidak, kami memperoleh nama variabel dengan ToString @ HoldForm @ x.

Martin Ender
sumber
Sial, menyalahgunakan cara menangani Mathematica And... Saya suka bagaimana argumen yang tepat dari Andbahkan tidak harus menjadi ekspresi Boolean.
JungHwan Min
Saya tahu ini bukan kode golf, tapi mengapa tidak adil f[x_] := ValueQ @ x && HoldForm @ x? Jika bukan karena persyaratan untuk memeriksa apakah input adalah variabel, HoldFormdengan sendirinya akan berfungsi.
ngenisis
HoldAllbukan HoldFirst?
Greg Martin
1
@ngenisis Karena itu kembali HoldForm[a]dan tidak "a". Sementara mereka ditampilkan sama di notebook Mathematica, fungsinya ada secara independen dari frontend, jadi saya ingin memiliki solusi yang mengembalikan string.
Martin Ender
1
Permintaan untuk menghapus spasi karena ... um ... karena
CalculatorFeline
7

Mathematica

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

Mathematica suka mengevaluasi semuanya , jadi untuk menghentikannya, kita harus berjuang melawannya. Dalam bahasanya sendiri.

Bagaimana?

f~SetAttributes~HoldAll;

Memberitahu Mathematica bahwa setiap kali fungsi fdipanggil, argumennya tidak harus dievaluasi (yaitu diadakan).


f[_] = False;

Ketika fdipanggil dengan argumen, output False. (?!)


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

Ketika fdipanggil dengan Simbol yang ditentukan (yang memiliki nilai), keluarkan nama simbol input.

Baris sebelumnya tidak diutamakan, meskipun telah didefinisikan sebelumnya, karena definisi ini lebih spesifik. Seperti yang dinyatakan oleh Dokumentasi Wolfram: Mathematica "mencoba untuk menempatkan definisi spesifik sebelum definisi yang lebih umum."

Mathematica sangat keras kepala dan terus berusaha mengevaluasi variabel kapan pun memungkinkan. Ekstra Unevaluatedmenangani itu.

JungHwan Min
sumber
7

C #

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

Versi Lengkap / Terformat:

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

Cara lain untuk melakukan ini adalah dengan merefleksikan tipe seperti ini:

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

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

Namun, Anda harus menyebutnya seperti:

GetParameterName(new { apple });

Kemudian gagal juga 3.14159dengan mengembalikan Lengthdan untuk itu Anda juga harus menyebutnya seperti berikut ini sebagai gantinya:

GetParameterName(new double[]{ 3.14159 });

Dalam C # 6.0 kami juga memiliki:

nameof

Tetapi ini tidak akan dikompilasi jika Anda memberikan sesuatu yang bukan variabel misalnya 3.14159. Saya percaya itu juga mengevaluasi input pada waktu kompilasi jadi sepertinya gagal juga persyaratan itu.

TheLethalCoder
sumber
Ini agak cerdik ... karena hal-hal lambda sangat penting (yaitu Anda tidak bisa hanya lulus dalam variabel, Anda harus melewati sesuatu yang kompilasi akan mengenali harus menjadi sebuah Expression). Anda juga harus menghitung using System.Linq.Expressions;.
VisualMelon
Apakah ini dianggap sebagai "menerima argumen dalam bentuk pohon sintaksis abstrak"?
Matti Virkkunen
@VisualMelon Saya tidak bisa menemukan cara lain untuk melakukan ini di C # jadi saya melakukannya dengan cara ini. Juga ini bukan kode-golf sehingga tidak terlalu penting,
TheLethalCoder
... maaf, tidak menyadari tidak ada hitungan byte!
VisualMelon
6

MATLAB

varname = @(x) inputname(1);

Dalam suatu fungsi ada beberapa variabel preset seperti varargin atau nargin, di antara yang juga kita miliki inputname.

dicuri dari sini

cacat
sumber
Saya tidak pernah tahu ini ada. Saya bermain-main dengan ini sedikit, dan mungkin Anda akan menyukai permata ini:x=42;y=54; f=@(x)eval(inputname(1));f(x),f(y)
Sanchises
Haha, eval==evil= D (ini benar-benar bekerja dengan x=42;y=54; f=@(x)eval('inputname(1)');f(x),f(y))
flawr
Ya, hanya saja fungsi anonim tidak tahu tentang variabel ruang kerja apa pun, jadi eval hanya bisa mengevaluasi x, dan kemudian eval-menggunakan argumen fungsi, bukan variabel ruang kerja.
Sanchises
6

R

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

substitutemengembalikan pohon parse untuk ekspresi yang tidak dievaluasi. The identicalmerek bersyarat yakin bahwa ekspresi unevaluated ini tidak identik dengan ekspresi itu sendiri; yaitu bahwa parameter yang dikirimkan bukan literal.

mb7744
sumber
4

Python 2

Beberapa penelitian telah dilakukan. Dan sepertinya itu bisa dilakukan dengan python, tapi saya masih berharap beberapa masalah dapat ditemukan.
Solusi tidak sempurna, karena mengasumsikan beberapa struktur kode. Namun saya tidak mematahkannya.

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

Ini menggunakan inspeksi untuk melihat lingkup surround dan menemukan di mana fungsi get_namedipanggil. Misalnya inspect...[1]akan kembali

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

Dan inspect...[1][4]akan menampilkan daftar dengan kode, di mana fungsinya disebut:

['print get_name( a )\n']

Setelah itu saya menggunakan regex untuk mengambil nama argumen

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

Dan .lstrip().rstrip()untuk menghapus semua spasi putih yang dapat ditempatkan ke dalam gelang.

Contoh dengan beberapa input yang rumit

Possum Mati
sumber
4

PowerShell

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

$apple = 'hi'
t $apple
apple

t 3.141

Tapi itu tidak bekerja dengan baik, ini belum sempurna.

TessellatingHeckler
sumber
Ini bukan kode golf.
Okx
@ Ok oh, ya, jadi tidak.
TessellatingHeckler
4

PHP

Perlu bahwa nilai variabel unik dalam array variabel global

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

Cobalah online!

PHP , 96 byte

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

Cobalah online!

Perlu bahwa variabel diinisialisasi langsung ke pemanggilan fungsi.

Cek apakah variabel global terakhir sama dengan variabel penyerahan

Jörg Hülsermann
sumber
Ini bukan kode golf.
Okx
@Okx Saya tahu Saat ini saya tidak punya ide yang lebih baik.
Jörg Hülsermann
4

JavaScript (ES6)

Untuk semua nama properti di window(Object.getOwnPropertyNames(w) ), coba tetapkan pengambil untuk properti yang mengembalikan nama properti.

Kemudian, tambahkan entri ke Peta M mana kuncinya adalah nilai (mungkin diganti) properti, dan nilai tersebut adalah nama properti.

Fungsi fhanya mengambil variabel (yaitu properti dari window) dan mengembalikan entri Muntuk nilai itu.

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)

Ini berfungsi untuk semua variabel global bawaan kecuali windowsendiri, karena tidak ada cara untuk membedakannyatop (kecuali dijalankan dalam bingkai):

L( P.filter(p => p !== f(w[p])) )
// -> ['window']
Darrylyeo
sumber
Untuk beberapa alasan, saya mendapatkan error: L not a function. Mengapa itu terjadi?
Caleb Kleveter
Wow! Itu lebih dalam ke ES6 daripada yang saya lihat dalam beberapa saat.
MD XF
@CalebKleveter D'oh! Saya lupa koma di baris kedua. Itu mungkin atau mungkin tidak bermasalah.
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);

Kami menggunakan Devel :: Caller (modul mirip debugger) untuk menjalankan tumpukan panggilan, mencari panggilan ke fungsi, dan mengembalikan semua operan dalam argumen, mengembalikannya sebagai nama variabel. Jika ada lebih (atau kurang) dari satu operan, kami tidak dipanggil dengan variabel. Jika operan bukan variabel, itu tidak akan memiliki nama dan kami dapat mendeteksi itu juga.

Kasus paling sulit adalah jika kita mendapatkan ekspresi satu operan yang melibatkan variabel, seperti ~$x . Kita dapat mencari tahu apakah ini telah terjadi dengan mengambil referensi ke variabel langsung dari tabel simbol (menggunakan${…} sintaks referensi simbolik) dan membandingkan alamat memorinya dengan nilai yang kami berikan sebagai argumen (yang, mudah, dilewatkan dengan referensi ). Jika mereka berbeda, kami memiliki ekspresi daripada variabel tunggal.

Perhatikan bahwa jika kita memanggil fungsi ini dengan ekspresi preincrement atau predecrement pada variabel tunggal, seperti pada v(--$x), kita akan $xkembali. Ini karena sebenarnya variabel itu sendiri yang diteruskan ke fungsi dalam kasus ini; itu hanya akan bertambah atau dikurangi sebelumnya. Saya harap ini tidak mendiskualifikasi jawabannya. (Di satu sisi, itu membuatnya lebih baik, karena itu menunjukkan bahwa kita memeriksa argumen itu sendiri daripada hanya membaca kode sumber.)


sumber
3

PHP

Sementara pengiriman PHP lainnya hanya menguji jika nilai yang diberikan cocok dengan nilai global, versi ini berfungsi dengan mengambil referensi ke nilai:

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

Ini sekarang harus bekerja bahkan jika variabel global adalah referensi ke nilai lain pada saat menelepon.

Uji di sini .

Christoph
sumber
Hebat dan Terima kasih banyak atas upaya pembelajarannya
Jörg Hülsermann
@ JörgHülsermann Bahkan menemukan cara untuk memperbaikinya!
Christoph
3

Rosda

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

Cobalah online!

Rosda memiliki fungsi bawaan untuk ini - name . Sayangnya, itu tidak mengembalikan nilai palsu ketika tidak diberi variabel.

Kode ini menyalahgunakan beberapa fitur semantik referensi. Penjelasan baris demi baris:

f(&a...) {

Fungsi mengambil sejumlah variabel argumen referensi ( &a...). Ini adalah satu-satunya cara di Raeda untuk membuat daftar referensi.

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

Pada baris kedua elemen-elemen adidorong ke aliran (a() ). Elemen-elemen ini adalah referensi jika fungsi diberi variabel dan nilai normal sebaliknya.

Elemen-elemen diulang menggunakan loop garis bawah. Sintaks garis bawah adalah sintaksis gula untuk forloop, jadi name(_)sama dengan name(var) for var. Nama variabel yang digunakan dalam forloop adalah bentuk di <sfvN>mana N bervariasi. (Yaitu. " name(<sfv1>) for <sfv1>") Adalah ilegal untuk mengandung variabel yang ditentukan pengguna <atau >, jadi nama yang dihasilkan tidak bertabrakan dengan nama variabel yang ada.

The name()fungsi mengembalikan nama variabel yang diberikan. Jika elemen dalama iterasi adalah referensi, maka itu adalah variabel yang diberikan kepada name. Jika tidak, jika elemen adalah nilai normal, variabel yang diberikan nameadalah variabel garis bawah <sfvN>. Hal ini disebabkan oleh semantik referensi dalam Röda: jika suatu fungsi menerima referensi dan fungsi tersebut meneruskan variabel referensi, nilai yang diteruskan tidak menunjuk ke variabel referensi tetapi ke variabel yang ditunjuk oleh variabel referensi.

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

Pada baris ketiga kami memeriksa apakah nama variabel dalam aliran berisi <karakter. Jika demikian, itu adalah variabel garis bawah dan nilai yang diteruskan kef bukan referensi. Kalau tidak, kami menampilkan nama referensi.

Solusi ini tidak berfungsi jika variabel yang diberikan ke fungsi adalah referensi atau variabel garis bawah. Namun, pertanyaannya menentukan bahwa hanya variabel global yang harus ditangani, dan mereka tidak dapat menjadi referensi atau menggarisbawahi variabel dalam Rda.

fergusq
sumber
1

Rubi , 46 byte

Terasa seperti kode paling kotor yang pernah saya tulis untuk Ruby.

Mengharuskan bahwa variabel global yang Anda panggil memiliki nilai unik yang tidak ada pada variabel global lainnya, karena satu-satunya cara untuk melakukan tantangan ini di Ruby adalah dengan melakukan pencarian pada semua variabel global dan mengembalikan kecocokan pertama. OP mengatakan tidak apa-apa, dan bebas menilai apakah solusi saya valid.

Perhatikan bahwa variabel global dimulai dengan $di Ruby, karena jika Anda ingin menambahkan hal-hal kasus tes tambahan.

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

Cobalah online!

Nilai Tinta
sumber
1

PHP

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

jika ditemukan nilai, cetak nama variabel dan keluar. cetak apa-apa dan jangan keluar lagi.

61 byte untuk mengembalikan nama variabel atau NULL:

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

Itu tidak akan menemukan fungsi bernama, hanya yang ditugaskan ke variabel.
Dan fungsi PHP tidak dapat mendeteksi apakah parameter diberikan oleh referensi atau nilai. Fungsi hanya akan mengembalikan nama depan di mana nilainya sesuai dengan nilai parameter fungsi.

Uji secara online

Titus
sumber
1

PowerShell

Versi baru tetapi bekerja mulai dari PowerShell 3.0

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

Cobalah secara Online!

Versi sebelumnya

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

Cobalah secara Online!

Andrei Odegov
sumber
0

tcl

proc p a {puts [subst $a]}

demo

sergiol
sumber
0

JavaScript (ES6)

Mengharuskan nilai variabel yang diteruskan ke fungsi unik untuk variabel itu. Mengembalikan undefinedjika variabel tidak lulus.

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

Cobalah

Untuk beberapa alasan, ia melempar kesalahan asal-silang ke dalam Snippet ketika "literal" dilewatkan.

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


Penjelasan

Loop melalui semua entri dalam objek global ( this), memeriksa apakah nilai masing-masing secara ketat sama dengan nilai argumen yang diteruskan ke fungsi. Jika entri yang cocok ditemukan maka kuncinya (nama) dikembalikan, keluar dari fungsinya.


Alternatif

Dengan persyaratan yang sama seperti di atas

arg=>
    [...Object.keys(this)].filter(key=>
        this[key]==arg
    ).pop()
Shaggy
sumber
2
Saya pikir ini gagal jika dua global memiliki nilai yang sama.
Martin Ender
1
Saya mendapatkan beberapa hasil yang sangat aneh .
Caleb Kleveter
@MartinEnder; ya, itu mengasumsikan bahwa nilai yang diberikan ke variabel yang dikirimkan adalah unik untuk variabel itu; lupa untuk memasukkan itu ketika saya memposting.
Shaggy
@CalebKleveter; beberapa di antaranya disebabkan oleh fakta bahwa nilai variabel yang Anda lewati tidak unik untuk variabel itu dan beberapa di antaranya karena nama variabel yang tidak valid (mis., hello--). Selain itu, Anda harus menggunakan vardaripada let.
Shaggy
1
@ CalebKleveter, lihat di sini untuk info lebih lanjut tentang perbedaan dalam pelingkupan antara letdan var. Untuk pertanyaan kedua Anda: Itu terjadi karena Anda memiliki variabel bernama IN_GLOBAL_SCOPEdalam lingkup global Anda dan memiliki nilai 1. Anda dapat memeriksa variabel yang ada dalam lingkup global Anda dan nilainya dengan menjalankan ini sebelum menguji yang di atas:for(x in this)console.log(x+": "+this[x])
Shaggy
0

Swift, 45 byte

var a = 3
print(Mirror(reflecting: a).label!)
Dimitrie-Toma Furdui
sumber
Saya tidak bisa menjalankan ini. Saya mendapatkan:value of type 'Mirror' has no member 'label'
Caleb Kleveter