Bir Değişkenin Adını Yazdır [kapalı]


20

Bir işlev yazın (tam bir program değil), böylece işlev bağımsız değişken olarak tek bir global değişkenle (veya dilinizin en yakın eşdeğeriyle) çağrılırsa, bu değişkenin adını verir (yani yazdırır veya döndürür). Bağımsız değişken bir değişken değilse, bunun yerine bir falsey değeri girin. (Argümanın değişken olduğu, global olmadığı durumla başa çıkmak zorunda değilsiniz.)

İşlev çağrısı ve işlev tanımı arasında derleme zamanı bağlantısı olmamalıdır (özellikle işlev tanımı, ne metin ne de soyut sözdizimi ağacında kaynak kodun gerçek bir parçası biçiminde argümanlar alan bir makro veya benzer bir yapı olamaz. form). Yani: ayrı derlemeyi destekleyen dillerde, program önce işlev çağrısı derlenmiş olsa bile çalışmalıdır (işlev tanımı hakkında bilgi sahibi değil, muhtemelen bir tür imza veya eşdeğeriyle), daha sonra işlev tanımı derlenir. Dilin ayrı bir derlemesi yoksa, yine de çağrı ve tanım arasında benzer bir ayrım hedeflemelisiniz.

Derlenmiş bir dil kullanıyorsanız, tüm programın derlenmiş biçimini diskten okuyabilir ve programın hata ayıklama bilgileriyle derlendiğini varsayabilirsiniz. (Böylece, bir programdan kendisine bir hata ayıklayıcı ekleyerek çalışan çözümlere izin verilir.)

Bu görevin her dilde mümkün olmayabileceğini unutmayın.

Örnekler:

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

Yorumlar uzun tartışmalar için değildir; bu sohbet sohbete taşındı .
Dennis

Yanıtlar:


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

Bu şu anda birkaç gotcha ile çalışıyor:

  1. Bunu derlemek için bir IDE kullanırsanız, Yönetici olarak çalıştırılmadığı sürece (geçici sınıf dosyalarının nereye kaydedildiğine bağlı olarak) çalışmayabilir.
  2. Sen kullanarak derlemek gerekir javacile -gbayrak. Bu, derlenmiş sınıf dosyasındaki yerel değişken adları da dahil olmak üzere tüm hata ayıklama bilgilerini oluşturur.
  3. Bu com.sun.tools.javap, bir sınıf dosyasının bayt kodunu ayrıştıran ve okunabilir bir sonuç üreten dahili bir Java API kullanır . Bu API'ya yalnızca JDK kitaplıklarından erişilebilir, bu nedenle JDK java çalışma zamanını kullanmalı veya sınıf yolunuza tools.jar eklemelisiniz.

Bu yöntem, yöntem programda birden çok kez çağrılsa bile çalışmalıdır. Tek bir satırda birden fazla çağrınız varsa maalesef henüz çalışmıyor. (Bunu yapan biri için aşağıya bakın)

Çevrimiçi deneyin!


açıklama

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

Bu ilk bölüm hangi sınıfta olduğumuz ve fonksiyonun adı hakkında bazı genel bilgiler alır. Bu, bir istisna oluşturarak ve yığın izlemesinin ilk 2 girdisini ayrıştırarak gerçekleştirilir.

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

İlk giriş, methodName kaynağını alabileceğimiz özel durumun atıldığı, ikinci girdi ise işlevin çağrıldığı yerdir.

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

Bu satırda JDK ile gelen javap yürütülebilir dosyasını yürütüyoruz. Bu program sınıf dosyasını (bayt kodu) ayrıştırır ve insan tarafından okunabilir bir sonuç sunar. Bunu ilkel "ayrıştırma" için kullanacağız.

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

Burada birkaç farklı şey yapıyoruz. İlk olarak, javap çıktısını satır satır bir liste halinde okuyoruz. İkincisi, javap hat indekslerine bir bytecode satır indeksleri haritası oluşturuyoruz. Bu, daha sonra hangi yöntem çağrısını analiz etmek istediğimizi belirlememize yardımcı olur. Son olarak hangi bayt kodu satır dizinine bakmak istediğimizi belirlemek için yığın izlemesindeki bilinen satır numarasını kullanıyoruz.

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

Burada, yöntemimizin çağrıldığı yeri ve Yerel Değişken Tablosunun nerede başladığını bulmak için javap çizgileri üzerinde bir kez daha yineliyoruz. Yöntemin çağrıldığı satıra ihtiyacımız var çünkü değişkenin yüklenmesi çağrısını içermeden önceki satır ve hangi değişkenin (dizine göre) yükleneceğini tanımlar. Yerel Değişken Tablosu, yakaladığımız dizine bağlı olarak değişkenin adını aramamıza yardımcı olur.

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

Bu bölüm aslında değişken indeksini almak için yük çağrısını ayrıştırmaktadır. İşlev aslında bir değişkenle çağrılmazsa bu bir istisna oluşturabilir, böylece burada null değerini döndürebiliriz.

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;

Son olarak, değişkenin adını Yerel Değişken Tablosundaki satırdan ayrıştırıyoruz. Bunun olması için hiçbir neden görmedim, ancak bulunamazsa null değerini döndürün.

Hepsini bir araya koy

 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;

Temelde baktığımız şey bu. Örnek kodda, ilk çağrı 17. satırdır. LineNumberTable'daki satır 17, bu satırın başlangıcının bayt kodu satır dizini 18 olduğunu gösterir. Bu, System.outyüktür. Sonra aload_2yöntem çağrısından hemen önce var, bu nedenle LocalVariableTable'ın yuva 2'sinde strbu durumda olan değişkeni ararız .


Eğlenmek için, aynı hatta birden fazla işlev çağrısını işleyen bir örnek. Bu, işlevin idempotent olmamasına neden olur, ancak bu bir nokta. Çevrimiçi deneyin!


1
Bu yanıtı seviyorum. Aynı çizgide bir şey düşünüyordum. Programın aynı çizgide birden çok arama eklerseniz Bir Not olsa, o zaman bir örneğin taşımayı deneyin, (değil emin bu geçerli yaklaşımla çözebilmemiz) yöntemini çağırarak hangi belirleyemez System.out.println(e.getParamName(str));System.out.println(e.getParamName(str2));System.out.println(e.getParamName(str4));üzerine TIO bağlantısında bir satır.
PunPun1000

Sen alabilirsiniz javapböyle konumu: Paths.get(System.getProperty("java.home"), "bin", "javap").
Olivier Grégoire

@ OlivierGrégoire Teşekkürler ama iç java api üzerinden javap çağırmak için geçiş yaptı, bu yüzden artık kodda diskte tam yerini almak zorunda değilsiniz (sadece sınıf yolu)
Poke

@ PunPun1000 evet, idempotent bir işlevi korurken bunu düzeltmenin güzel bir yolu olup olmadığından emin değilim ama sadece eğlenmek için işlev dışında durumu kullanan bir şey koyabilirim.
Poke

1
@ PunPun1000 Bir hatta birden fazla çağrıyı yönetebilecek bir sürüm eklendi. Sadece fonksiyondan daha fazlası, bu yüzden cevabı güncellemek yerine başka bir TryItOnline bağlantısı ekledim.
Poke

14

Python 2

Bu yazdığım en kirli kod hakkında ama işe yarıyor. Yt \ _ (ツ) _ / P Python işlevi bir tanesini çağırmaktan hemen hoşlanmayacağından, var olmayan bir değişkene bir hata atar. Değişken olmayanlara da bir hata atar, ancak gerekirse bir try / / ile düzeltilebilir.

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]

Çevrimiçi deneyin!

Argümanı dize olarak almamıza izin verilirse, bu geçersiz bir girişte bir falsy değeri çıktılama gereksinimlerini karşılar.

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

Çevrimiçi deneyin!


Benim için neredeyse aynı cevabı gönderdin, benimkine açıklama yazarken: D +1 for you
Dead Possum

1
@DeadPossum Her zaman yayınlayın ve yanıtlayın ve daha sonra düzenleme olarak bir açıklama ekleyin. ;)
Arjun

8
@Arjun, oh harika, bu yüzden CodeGolf'un yanı sıra EditGolf oynamak zorundayız: P
Wossname

12

Mathematica

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

HoldFirstÖzelliği önler ffonksiyonu çağırmadan önce, bağımsız değişkeni değerlendirme ile. ValueQ @ xdaha sonra verilen bağımsız değişkenin değer verilen bir değişken olup olmadığını kontrol eder. Değilse Falsekısa devre nedeniyle geri dönüyoruz. Aksi takdirde, değişken adını ile alırız ToString @ HoldForm @ x.


Dang, Mathematica'nın işleyişini kötüye kullanma And... Doğru argümanının AndBoole ifadesi olması bile gerekmiyor.
JungHwan Min

Kod golf değil biliyorum, ama neden sadece f[x_] := ValueQ @ x && HoldForm @ x? Girişin bir değişken olup olmadığını kontrol etme gereksinimi HoldFormolmasaydı, kendi başına işe yarardı.
ngenisis

HoldAllyerine HoldFirst?
Greg Martin

1
@ngenisis Çünkü bu geri dönüyor HoldForm[a]ve değil "a". Mathematica not defterinde aynı görüntülenirken, işlev ön uçtan bağımsız olarak bulunur, bu yüzden bir dize döndüren bir çözüme sahip olmak istedim.
Martin Ender

1
Boşluğu kaldırma isteği çünkü ... um ... çünkü
CalculatorFeline

7

Mathematica

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

Mathematica her şeyi değerlendirmeyi sever , bu yüzden onu durdurmak için ona karşı savaşmalıyız. Kendi dilinde.

Nasıl?

f~SetAttributes~HoldAll;

Mathematica'ya işlev fher çağrıldığında argümanlarının değerlendirilmemesi (yani tutulması) gerektiğini söyler .


f[_] = False;

Bağımsız fdeğişken ile çağrıldığında çıktı False. (?!)


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

Tüm f(bir değere sahiptir) tanımlanmış bir sembol giriş, çıkış simgesi adıyla adlandırılır.

Daha önce tanımlanmış olmasına rağmen, önceki satır öncelikli değildir, çünkü bu tanım daha spesifiktir. Wolfram Belgelerinde belirtildiği gibi: Mathematica "daha genel tanımların önüne belirli tanımlar koymaya çalışır."

Mathematica çok inatçıdır ve değişkenleri mümkün olduğunca değerlendirmeye çalışmaktadır. Ekstra Unevaluatedbununla ilgilenir.


7

C #

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

Tam / Biçimlendirilmiş sürüm:

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

Bunu yapmanın başka bir yolu da bu tür bir konu üzerinde düşünmektir:

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

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

Ancak, şöyle çağırmalısınız:

GetParameterName(new { apple });

Daha sonra 3.14159geri dönerek başarısız olur Lengthve bunun için de aşağıdaki gibi çağırmanız gerekir:

GetParameterName(new double[]{ 3.14159 });

C # 6.0'da ayrıca:

nameof

Ancak bu değişken olmayan bir şey iletirseniz derlenmez 3.14159. Aynı zamanda girdiyi derleme zamanında değerlendirdiğine inanıyorum, bu nedenle bu gereksinimi de başarısız gibi görünüyor.


Bu biraz tehlikeli ... çünkü lambda şeyleri kesinlikle gerekli (yani sadece değişkeni geçemezsiniz, derlemenin tanıyacağı bir şey geçmesi gerekir Expression). Ayrıca saymalısın using System.Linq.Expressions;.
VisualMelon

Bu "soyut sözdizimi ağacı biçiminde argümanlar almak" olarak sayılıyor mu?
Matti Virkkunen

@VisualMelon C # bunu yapmak için başka bir yol bulamadım, bu yüzden bu şekilde yaptım. Ayrıca bu kod golf değil, bu yüzden gerçekten önemli değil,
TheLethalCoder

... üzgünüm, bayt sayısı olmadığını fark etmedim!
VisualMelon

6

MATLAB

varname = @(x) inputname(1);

Bir fonksiyon içinde varargin ya narginda bizim gibi önceden ayarlanmış değişkenler vardır inputname.

buradan çalındı


Bunun var olduğunu hiç bilmiyordum. Bu biraz oynadım ve belki de bu gem x=42;y=54; f=@(x)eval(inputname(1));f(x),f(y)
seveceksiniz

Haha, eval==evil= D (gerçekten işe yarıyor x=42;y=54; f=@(x)eval('inputname(1)');f(x),f(y))
flawr

Evet, sadece anonim bir işlev herhangi bir çalışma alanı değişkeni hakkında bilgi sahibi değildir, bu nedenle eval, çalışma alanı değişkenini değil, yalnızca işlev bağımsız değişkenini değerlendirebilir xve sonra evalçalışır.
Sanchises

6

R,

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

substitutedeğerlendirilmemiş bir ifade için ayrıştırma ağacını döndürür. identicalBu unevaluated ifade ifade kendisine denk olmadığından emin koşullu yapar; yani geçirilen parametrenin değişmez bir değeri olmadığı.


4

Python 2

Bazı araştırmalar yapıldı. Ve python'da mümkün gibi görünüyor, ama yine de bazı sıkıntıların bulunmasını bekliyorum.
Çözüm, kodun bazı yapısını varsayacağı için mükemmel değildir. Yine de hala kırmadım.

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

Bu, surround kapsamına bakmak ve işlevin nerede get_nameçağrıldığını bulmak için inspect kullanır . Örneğin inspect...[1]geri dönecek

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

Ve inspect...[1][4]bize fonksiyonun adı verildiği kodlu bir liste gösterecektir:

['print get_name( a )\n']

Bundan sonra argümanın adını almak için regex kullandım

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

Ve .lstrip().rstrip()parantez içine yerleştirilebilecek tüm boşlukları kaldırmak için.

Bazı zor girdilere sahip örnek


4

Güç kalkanı

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

$apple = 'hi'
t $apple
apple

t 3.141

Ancak güvenilir bir şekilde çalışmaz, ilkeldir.


Bu kod golf değil.
Okx

@Okx oh, ayy, yani değil.
TessellatingHeckler

4

PHP

Değişken değerinin, genel değişkenler dizisinde benzersiz olmasını gerektirir

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

Çevrimiçi deneyin!

PHP , 96 bayt

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

Çevrimiçi deneyin!

Değişkenin doğrudan işlev çağrısına başlatılması gerekir.

Son genel değişkenin eşit teslim değişkeninde olup olmadığını kontrol eder


Bu kod golf değil.
Okx

@Okx Biliyorum Şu anda daha iyi bir fikrim yok.
Jörg Hülsermann


4

JavaScript (ES6)

window( Object.getOwnPropertyNames(w)) İçindeki tüm özellik adları için, özellik adını döndüren o özellik için bir alıcı tanımlamaya çalışın.

Ardından, Manahtarın özelliğin (büyük olasılıkla geçersiz kılınan) değeri olduğu ve değerin özelliğin adı olduğu bir Haritaya giriş ekleyin .

İşlev fyalnızca bir değişkeni (yani bir özelliği window) alır ve Mbu değer için girişi döndürür .

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)

Bu window, kendisi dışında tüm yerleşik global değişkenler için geçerlidir , çünkü onu ayırmanın bir yolu yoktur top(bir çerçevede çalıştırılmadığı sürece):

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

Nedense, bir hata alıyorum: L not a function. Neden olur böyle olur?
Caleb Kleveter

Vaov! Bu, ES6'da bir süredir gördüğümden daha derin.
MD XF

@CalebKleveter D'oh! İkinci hatta bir virgül unuttum. Bu sorunlu olabilir veya olmayabilir.
darrylyeo

3

Perl 5 + Devel :: Arayan

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

Çağrı yığınını yürümek, işleve çağrı aramak ve değişken içindeki tüm işlenenleri döndürmek için Devel :: Caller (hata ayıklayıcı benzeri bir modül) kullanırız. Birden fazla işlenen varsa, bir değişkenle çağrılmadık. Eğer işlenen bir değişken değilse, bir adı olmayacaktır ve bunu da tespit edebiliriz.

En zor durum, gibi bir değişkeni içeren tek işlenen bir ifade almamızdır ~$x. Bunun, doğrudan sembol tablosundan ( ${…}sembolik referans sözdizimini kullanarak) değişkene bir referans alarak ve bellek adresini bir argüman olarak geçirdiğimiz değerle (uygun olarak referansla geçirilen) karşılaştırarak meydana gelip gelmediğini anlayabiliriz ). Eğer farklılarsa, yalnız bir değişken yerine bir ifademiz var.

Bu işlevi, olduğu gibi tek bir değişken üzerinde bir ön düzenleme veya tahmin ifadesi ile çağırırsak v(--$x), $xdöndürüldüğümüzü unutmayın. Bunun nedeni, aslında bu durumda işleve iletilen değişkenin kendisidir; sadece önceden artırılır veya azaltılır. Umarım bu cevabı diskalifiye etmez. (Bir bakıma daha iyi yapar, çünkü sadece kaynak kodunu okumak yerine argümanın kendisini kontrol ettiğimizi gösterir.)


3

PHP

Diğer PHP gönderimleri yalnızca verilen değerin bir global değerle eşleşip eşleşmediğini test ederken, bu sürüm şu değere referans alarak çalışır:

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

Genel değişken, çağrı sırasında başka bir değere referans olsa bile, bu işlem artık geçerli olmalıdır.

Burada test edin .


Harika ve öğrenme çabası için çok teşekkür ederim
Jörg Hülsermann

@ JörgHülsermann Daha da geliştirmenin bir yolunu buldum!
Christoph

3

Röda

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

Çevrimiçi deneyin!

Röda bunun için yerleşik bir işleve sahiptir name. Ne yazık ki, bir değişken verilmediği zaman bir falsy değeri döndürmez.

Bu kod, referans anlambilimin çeşitli özelliklerini kötüye kullanır. Satır satır açıklama:

f(&a...) {

İşlev, değişken sayıda başvuru bağımsız değişkeni ( &a...) alır. Röda'da referans listesi oluşturmanın tek yolu budur.

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

İkinci satırdaki elemanları aakışa ( a()) itilir . Fonksiyona değişkenler ve normal değerler verildiğinde bu öğeler referanslardır.

Öğeler bir alt çizgi döngüsü kullanılarak yinelenir. Alt çizgi sözdizimi, fordöngüler için sözdizimi şekeri name(_)olduğundan eşdeğerdir name(var) for var. Döngüde kullanılan değişkenin adı, N'nin değiştiği forbiçimdedir <sfvN>. (Ie. " name(<sfv1>) for <sfv1>") Bir kullanıcı tarafından tanımlanan değişken içermesi de yasadışı <veya >oluşturulan isimleri mevcut olan değişken isimleri ile değil çarpışır yapmak, böylece.

name()Fonksiyon verilen değişkenin adını döndürür. aYinelenen öğe bir referanssa, verilen değişkendir name. Aksi takdirde, eleman normal bir değerdeyse, verilen değişken namealt çizgi değişkenidir <sfvN>. Bunun nedeni, Röda'daki referansların anlambilimidir: bir işlev bir başvuruyu kabul ederse ve işlev bir başvuru değişkeninden geçirilirse, iletilen değer referans değişkenine değil, başvuru değişkeninin işaret ettiği değişkene işaret eder.

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

Üçüncü satırda, akıştaki değişken adının bir <karakter içerip içermediğini inceliyoruz . Öyleyse, bir alt çizgi değişkenidir ve iletilen değer fbir referans değildir. Aksi takdirde referansın adını veririz.

Bu çözüm, işleve verilen değişken bir başvuru veya alt çizgi değişkeniyse çalışmaz. Ancak soru, yalnızca global değişkenlerin ele alınması gerektiğini ve bunların Röda'da referans veya alt çizgi değişkenleri olamayacağını belirtir.


1

Yakut , 46 bayt

Ruby için yazdığım en kirli kod gibi geliyor.

Aradığınız küresel değişkenlerin başka bir global değişken üzerinde olmayan benzersiz bir değere sahip olması gerekir, çünkü Ruby'de bu zorluğu yapmanın tek yolu tüm global değişkenlerde bir arama yapmak ve ilk eşleşmeyi döndürmektir. OP, sorun olmadığını söylüyor ve çözümümün geçerli olup olmadığını yargılamakta özgür.

$Fazladan test senaryosu öğeleri eklemek istiyorsanız global değişkenlerin Ruby'de başladığını unutmayın .

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

Çevrimiçi deneyin!


1

PHP

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

değer bulunursa, değişken adını yazdırın ve çıkın. hiçbir şey yazdırmayın ve başka çıkış yapmayın.

Değişken adı döndürmek için 61 bayt veya NULL:

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

Adlandırılmış işlevleri değil, yalnızca değişkenlere atanan işlevleri bulamaz.
Ve bir PHP işlevi, bir parametrenin başvuru veya değere göre sağlanmış olup olmadığını algılayamaz. İşlev, değerin işlev parametre değeriyle eşleştiği ilk adı döndürür.

Çevrimiçi test edin


1

Güç kalkanı

Yeni sürüm ancak PowerShell 3.0'dan başlayarak çalışıyor

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

Çevrimiçi Deneyin!

Önceki versiyon

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

Çevrimiçi Deneyin!



0

JavaScript (ES6)

İşleve geçirilen değişkenin değerinin bu değişkene özgü olmasını gerektirir. undefinedBir değişken geçirilmemişse döndürür .

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

Dene

Bazı nedenlerden dolayı, bir "değişmez" iletildiğinde Snippet'e çapraz kökenli bir hata atar.

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


açıklama

Genel nesnede ( this) yer alan tüm girdileri gözden geçirin ve her birinin değerinin işleve iletilen bağımsız değişkenin değerine kesinlikle eşit olup olmadığını kontrol edin. Eşleşen bir giriş bulunursa, işlevden çıkarak anahtarı (adı) döndürülür.


Alternatif

Yukarıdaki şartlarla aynı

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

2
Bence iki küresel aynı değere sahipse bu başarısız olur.
Martin Ender


@MartinEnder; evet, iletilen değişkene atanan değerin o değişkene özgü olduğunu varsayar; Ben gönderdiğim zaman dahil etmeyi unuttum.
Shaggy

@CalebKleveter; bunlardan bazıları geçmekte olduğunuz değişkenin değerinin o değişkene özgü olmaması ve bir kısmının geçersiz değişken isimleri (örn hello--.) nedeniyle olmasından kaynaklanmaktadır . Ayrıca, kullanmak varyerine kullanmanız gerekir let.
Shaggy

1
@CalebKleveter, bkz burada arasındaki kapsam içinde fark hakkında daha fazla bilgi için letve var. İkinci sorunuza: Bu IN_GLOBAL_SCOPE, küresel kapsamınızda bir değişkene sahip olduğunuz ve bunun değeri olduğu için oldu 1. Yukarıdakileri test etmeden önce bunu çalıştırarak küresel kapsamınızdaki değişkenleri ve değerlerini kontrol edebilirsiniz:for(x in this)console.log(x+": "+this[x])
Shaggy

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.