変数の名前を印刷する[終了]


20

関数(完全なプログラムではない)を作成し、関数が引数として単一のグローバル変数(または言語に最も近いもの)で呼び出された場合、その変数の名前を出力(つまり、出力または返す)します。引数が変数ではない場合、代わりにfalsey値を出力します。(引数が変数であるがグローバルではない場合を処理する必要はありません。)

関数呼び出しと関数定義の間にコンパイル時の接続があってはなりません(特に、関数定義は、テキストまたは抽象構文ツリーではなく、ソースコードのリテラルフラグメントの形式で引数を受け取るマクロまたは同様の構成体であってはなりません。形)。つまり、個別のコンパイルをサポートする言語では、関数呼び出しが最初にコンパイルされても(関数定義の知識はないが型シグネチャまたは同等のものがあれば)プログラムが動作する必要があり、その後関数定義がコンパイルされます。言語に個別のコンパイルがない場合でも、呼び出しと定義を同様に分離する必要があります。

コンパイルされた言語を使用している場合は、ディスクから完全なプログラムのコンパイルされた形式を読み取り、プログラムがデバッグ情報を使用してコンパイルされたと見なすことができます。(したがって、プログラムからデバッガーをそれ自体にアタッチすることで機能するソリューションが許可されます。)

このタスクはすべての言語で実行できるわけではないことに注意してください。

例:

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

コメントは詳細なディスカッション用ではありません。この会話はチャットに移動さました
デニス

回答:


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

これは現在いくつかの落とし穴で動作します:

  1. IDEを使用してこれをコンパイルする場合、管理者として実行しない限り機能しない可能性があります(一時クラスファイルの保存場所によって異なります)
  2. フラグを使用javacしてコンパイルする必要があります-g。これにより、コンパイルされたクラスファイルにローカル変数名を含むすべてのデバッグ情報が生成されます。
  3. これは、com.sun.tools.javapクラスファイルのバイトコードを解析し、人間が読める結果を生成する内部Java API を使用します。このAPIはJDKライブラリでのみアクセスできるため、JDK javaランタイムを使用するか、tools.jarをクラスパスに追加する必要があります。

これは、プログラムでメソッドが複数回呼び出された場合でも機能するはずです。残念ながら、1行に複数の呼び出しがある場合、まだ機能しません。(そうするものについては、以下を参照してください)

オンラインでお試しください!


説明

StackTraceElement[] strace = new Exception().getStackTrace();
String methodName = strace[0].getMethodName();
int lineNum = strace[1].getLineNumber();

String className = strace[1].getClassName().replaceAll(".{5}$", "");
String classPath = Class.forName(className).getProtectionDomain().getCodeSource().getLocation().getPath() + className + ".class";

この最初の部分では、現在のクラスと関数の名前に関する一般的な情報を取得します。これは、例外を作成し、スタックトレースの最初の2つのエントリを解析することで実現されます。

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

最初のエントリはmethodNameを取得できる例外がスローされる行であり、2番目のエントリは関数が呼び出された場所です。

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

この行では、JDKに付属のjavap実行可能ファイルを実行しています。このプログラムは、クラスファイル(バイトコード)を解析し、人間が読み取れる結果を表示します。これを初歩的な「解析」に使用します。

List<String> javapLines = Arrays.asList(javapOut.toString().split("\\r?\\n"));
int byteCodeStart = -1;
Map<Integer, Integer> byteCodePointerToJavaPLine = new HashMap<Integer, Integer>();
Pattern byteCodeIndexPattern = Pattern.compile("^\\s*(\\d+): ");
for (int n = 0;n < javapLines.size();n++) {
    String javapLine = javapLines.get(n);
    if (byteCodeStart > -1 && (javapLine == null || "".equals(javapLine))) {
        break;
    }
    Matcher byteCodeIndexMatcher = byteCodeIndexPattern.matcher(javapLine);
    if (byteCodeIndexMatcher.find()) {
        byteCodePointerToJavaPLine.put(Integer.parseInt(byteCodeIndexMatcher.group(1)), n);
    } else if (javapLine.contains("line " + lineNum + ":")) {
        byteCodeStart = Integer.parseInt(javapLine.substring(javapLine.indexOf(": ") + 2));
    }
}

ここでは、いくつかの異なることを行っています。最初に、javap出力を1行ずつリストに読み込みます。次に、バイトコード行インデックスからjavap行インデックスへのマップを作成しています。これは、後で分析するメソッド呼び出しを決定するのに役立ちます。最後に、スタックトレースの既知の行番号を使用して、見たいバイトコード行インデックスを決定します。

int varLoadIndex = -1;
int varTableIndex = -1;
for (int i = byteCodePointerToJavaPLine.get(byteCodeStart) + 1;i < javapLines.size();i++) {
    if (varLoadIndex < 0 && javapLines.get(i).contains("Method " + methodName + ":")) {
        varLoadIndex = i;
        continue;
    }

    if (varLoadIndex > -1 && javapLines.get(i).contains("LocalVariableTable:")) {
        varTableIndex = i;
        break;
    }
}

ここで、メソッドが呼び出されている場所とローカル変数テーブルが開始されている場所を見つけるために、javap行をもう一度繰り返します。メソッドを呼び出す行が必要なのは、その前の行に変数をロードする呼び出しが含まれており、ロードする変数を(インデックスによって)特定するためです。ローカル変数テーブルは、取得したインデックスに基づいて変数の名前を実際に検索するのに役立ちます。

String loadLine = javapLines.get(varLoadIndex - 1).trim();
int varNumber;
try {
    varNumber = Integer.parseInt(loadLine.substring(loadLine.indexOf("aload") + 6).trim());
} catch (NumberFormatException e) {
    return null;
}

この部分は、実際にはロードコールを解析して変数インデックスを取得しています。関数が実際に変数で呼び出されない場合、これは例外をスローする可能性があるため、ここでnullを返すことができます。

int j = varTableIndex + 2;
while(!"".equals(javapLines.get(j))) {
    Matcher varName = Pattern.compile("\\s*" + varNumber + "\\s*([a-zA-Z_][a-zA-Z0-9_]*)").matcher(javapLines.get(j));  
    if (varName.find()) {
        return varName.group(1);
    }
    j++;
}
return null;

最後に、ローカル変数テーブルの行から変数の名前を解析します。これが発生する理由はわかりませんが、見つからない場合はnullを返します。

すべてを一緒に入れて

 public static void main(java.lang.String[]);
    Code:
...
      18: getstatic     #19                 // Field java/lang/System.out:Ljava/io/PrintStream;
      21: aload_1
      22: aload_2
      23: invokevirtual #25                 // Method getParamName:(Ljava/lang/String;)Ljava/lang/String;
...
    LineNumberTable:
...
      line 17: 18
      line 18: 29
      line 19: 40
...
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0      83     0  args   [Ljava/lang/String;
          8      75     1     e   LE;
         11      72     2   str   Ljava/lang/String;
         14      69     3  str2   Ljava/lang/String;
         18      65     4  str4   Ljava/lang/String;
         77       5     5    e1   Ljava/lang/Exception;

これは基本的に私たちが見ているものです。コード例では、最初の呼び出しは17行目です。LineNumberTableの17行目は、その行の先頭がバイトコード行インデックス18であることを示していSystem.outます。これが負荷です。次にaload_2、メソッド呼び出しの直前にあるためstr、この場合はLocalVariableTableのスロット2で変数を探します。


楽しみのために、同じ行で複数の関数呼び出しを処理するものがあります。これにより、関数はi等ではなくなりますが、それは一種のポイントです。オンラインでお試しください!


1
この答えが大好きです。同じ線に沿って何かを考えていた。あなたがプログラムの同じ行に複数の呼び出しを含める場合は、1つのノートものの、それは1つが、たとえば、移動してみてください、(いないことを確認これはあなたの現在のアプローチで固定することができる)メソッドを呼び出しているかを判断することができないSystem.out.println(e.getParamName(str));System.out.println(e.getParamName(str2));System.out.println(e.getParamName(str4));上にTIOリンクの1行。
PunPun1000

次のjavapように場所を取得できますPaths.get(System.getProperty("java.home"), "bin", "javap")
オリビエグレゴワール

OlivierGrégoireのおかげで@が、私はコードで、ディスク上の正確な位置を取得する必要はありませんので、私はもう内部のJava API経由てjavapを呼び出すには(ちょうどクラスパスを)切り替え
ポケ

@ PunPun1000ええ、べき等関数を維持しながらそれを修正する良い方法があるかどうかはわかりませんが、楽しみのために関数の外側の状態を使用するものをまとめるかもしれません。
ポケ

1
@ PunPun1000 1行で複数の呼び出しを処理できるバージョンが追加されました。これは単なる関数ではないため、答えを更新するのではなく、TryItOnlineリンクを追加しました。
ポケ

14

Python 2

これは私が書いた中で最も汚いコードについてですが、動作します。¯\ _(ツ)_ /¯存在しない変数にエラーをスローします。Pythonが変数を使用して関数を呼び出すことをすぐに嫌います。また、非変数に対してエラーをスローしますが、これは必要に応じてtry / exceptで修正できます。

import inspect
import re

def name_of(var):
    for i in inspect.getframeinfo(inspect.currentframe().f_back)[3]:
        return re.search(r'\bname_of\s*\(\s*([A-Za-z_][A-Za-z0-9_]*)\s*\)', i).groups()[0]

オンラインでお試しください!

引数を文字列として使用できる場合、これは無効な入力で偽の値を出力するという要件を満たします。

import inspect
import re

def name_of(var):
    # return var :P

    try:
        eval(var)
    except NameError:
        return False

    for i in inspect.getframeinfo(inspect.currentframe().f_back)[3]:
        try:
            return re.search(r'\bname_of\s*\(\s*[\'"]([A-Za-z_][A-Za-z0-9_]*)[\'"]\s*\)', i).groups()[0]
        except AttributeError:
            return False

オンラインでお試しください!


あなたは私に説明をタイプしている間、ほとんど同じ答えを投稿しました:D +1あなたのために
死んだポッサム

1
@DeadPossum常に投稿して回答し、後で編集として説明を追加します。;)
アルジュン

8
@アルジュン、すごいよ、EditGolfとCodeGolfをプレイしなければならない:P
Wossname

12

Mathematica

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

このHoldFirst属性によりf、関数を呼び出す前に引数を評価できなくなります。ValueQ @ x次に、指定された引数が値が指定された変数であるかどうかを確認します。そうでない場合はFalse、短絡のために戻ります。それ以外の場合は、変数名を取得しますToString @ HoldForm @ x


ダン、Mathematicaの処理方法を乱用していますAnd...の正しい引数がAndブール式である必要さえないのが好きです。
ジョンファンミン

私はそれがゴルフのコードではないことを知っていますが、なぜそれだけではないのf[x_] := ValueQ @ x && HoldForm @ xですか?入力が変数であるかどうかを確認する要件がなければ、HoldFormそれだけで機能します。
-ngenisis

HoldAll代わりにHoldFirst
グレッグマーティン

1
@ngenisis返されるのHoldForm[a]ではなく"a"。Mathematicaノートブックでは同じように表示されますが、関数はフロントエンドとは独立して存在するため、文字列を返すソリューションが必要でした。
マーティンエンダー

1
空白を削除するための要求...... 理由は
CalculatorFeline

7

Mathematica

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

Mathematicaはすべてを評価するのが好きなので、それを止めるためにはそれと戦わなければなりません。独自の言語で。

どうやって?

f~SetAttributes~HoldAll;

関数fが呼び出されたときはいつでも、その引数を評価(保持)してはならないことをMathematicaに伝えます。


f[_] = False;

ときfの引数、出力と呼ばれていますFalse。(?!)


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

f定義されたSymbol(値を持つ)で呼び出された場合、入力のシンボル名を出力します。

この定義はより具体的であるため、前の行は以前に定義されていても優先されません。Wolframドキュメンテーションが述べているように:Mathematicaは「より一般的な定義の前に特定の定義を置くように試みる」

Mathematicaは非常に頑固で、可能な限り変数を評価しようとし続けます。余分Unevaluatedはそれの世話をします。


7

C#

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

ただし、これは「抽象構文ツリー形式で引数を受け取る」としてカウントされますか?
マティバークネン

@VisualMelon C#でこれを実行する他の方法が見つからなかったので、この方法で実行しました。また、これはコードゴルフではないため、実際には問題ではありません。
TheLethalCoder17年

...申し訳ありませんが、バイトカウントがないことに気付きませんでした!
VisualMelon

6

MATLAB

varname = @(x) inputname(1);

関数内には、varargin またはなどのプリセット変数がいくつかnarginありますinputname

ここから盗まれた


これが存在することは知らなかった。私はこれを少し遊んでみましたが、おそらくあなたはこの宝石が好きになるでしょう: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)
-flawr

ええ、それは無名関数は、任意のワークスペース変数について知らないので、evalが唯一評価できるだけのことだxし、その後されたeval関数の引数ではなく、ワークスペース変数を-ing。
-Sanchises

6

R

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

substitute未評価の式の解析ツリーを返します。identicalこの未評価の式は式自体と同一でないことを確認し、条件付きます。つまり、渡されたパラメーターはリテラルではないということです。


4

Python 2

いくつかの研究が行われました。そして、それはpythonで可能であるようですが、まだいくつかのトラブルが見つかると期待しています。
ソリューションは、コードの構造を前提としているため、完全ではありません。しかし、私はまだそれを破りませんでした。

import inspect
import re

def get_name(var):
    called = inspect.getouterframes(inspect.currentframe())[1][4][0]
    return re.search('(?<=get_name\().*(?=\))', called).group().lstrip().rstrip()

これは、inspectを使用してサラウンドスコープを調べ、関数get_nameが呼び出される場所を見つけます。たとえば、inspect...[1]戻ります

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

そしてinspect...[1][4]、関数が呼び出されるコードのリストを表示します:

['print get_name( a )\n']

その後、引数の名前を取得するために正規表現を使用しました

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

そして.lstrip().rstrip()、ブレースに配置される可能性のあるすべての空白を削除します。

トリッキーな入力の例


4

パワーシェル

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

$apple = 'hi'
t $apple
apple

t 3.141

しかし、それは確実に動作しません、それは初歩的です。


これはコードゴルフではありません。
Okx

@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私は知っています現時点では、これ以上良いアイデアはありません。
ヨルクヒュルサーマン


4

JavaScript(ES6)

windowObject.getOwnPropertyNames(w))内のすべてのプロパティ名について、そのプロパティのプロパティ名を返すゲッターを定義しようとします。

次に、マップにエントリを追加します Mにします。ここで、キーはプロパティの(おそらくオーバーライドされる)値であり、値はプロパティの名前です。

この関数はf単に変数(つまりのプロパティwindow)を受け取り、Mその値のエントリを返します。

let
    w = window,
    M = new Map(),
    L = console.log,
    P = Object.getOwnPropertyNames(w),
    D = Object.defineProperty

for(const p of P){
    try {
        D(w, p, {
            get(){
                return p
            }
        })
    } catch(e){ L(e) }

    try {
        M.set(w[p], p)
    } catch(e){ L(e) }
}

let f = v => M.get(v)

(フレーム内で実行しない限り)windowそれと区別する方法がないため、これはそれ自体を除くすべての組み込みグローバル変数に対して機能しますtop

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

何らかの理由で、エラーが発生しています:L not a function。なぜだろうそれが起こるの?
カレブKleveter

うわー!これは、私がしばらく見てきたよりもES6の奥深くにあります。
MD XF

@CalebKleveter D'oh!2行目のコンマを忘れました。それは問題があるかもしれないし、そうでないかもしれない。
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);

Devel :: Caller(デバッガーに似たモジュール)を使用して呼び出しスタックを調べ、関数の呼び出しを探し、引数内のすべてのオペランドを返し、変数名として返します。オペランドが1つより多い(または少ない)場合、変数を使用して呼び出されませんでした。オペランドが変数ではなかった場合、その名前には名前がなく、それも検出できます。

最も難しいケースは、などの変数を含む1オペランド式を取得する場合です~$x。これが発生したかどうかは、シンボルテーブルから直接変数への参照を取得することで把握できます(${…}シンボリック参照構文)、そのメモリアドレスを引数として渡された値と比較することでわかります(便利なのは参照で渡されます) )。それらが異なる場合、孤立した変数ではなく式があります。

この関数を、次のように、1つの変数で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もそれを改善する方法を見つけました!
クリストフ

3

ロダ

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

オンラインでお試しください!

Rödaにはこのための組み込み関数があります– name。ただし、残念ながら、変数を指定しないと偽の値を返しません。

このコードは、参照セマンティクスのいくつかの機能を乱用しています。行ごとの説明:

f(&a...) {

この関数は、可変数の参照引数(&a...)を取ります。これは、参照のリストを作成するRödaの唯一の方法です。

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

2行目でaは、の要素がストリームにプッシュされます(a())。これらの要素は、関数に変数が与えられた場合の参照であり、そうでない場合は通常の値です。

要素は、アンダースコアループを使用して繰り返されます。アンダースコア構文はforループの構文シュガーなので、name(_)と同等name(var) for varです。forループで使用される変数の名前は、<sfvN>Nが変化する形式です。(つまり、「name(<sfv1>) for <sfv1>」)ユーザー定義変数に<or を含めることは違法である>ため、生成された名前は既存の変数名と衝突しません。

このname()関数は、指定された変数の名前を返します。a反復される要素が参照である場合、それはに与えられた変数ですname。それ以外の場合、要素が通常の値だった場合、与えられるname変数はアンダースコア変数です<sfvN>。これは、ローダの参照のセマンティクスによるものです。関数が参照を受け入れ、関数に参照変数が渡された場合、渡された値は参照変数ではなく、参照変数が指す変数を指します。

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

3行目では、ストリーム内の変数名に <文字。その場合、それはアンダースコア変数であり、渡された値fは参照ではありませんでした。それ以外の場合は、参照の名前を出力します。

このソリューションは、関数に指定された変数が参照変数またはアンダースコア変数である場合は機能しません。ただし、この質問では、グローバル変数のみを処理する必要があり、Rödaで参照またはアンダースコア変数にすることはできません。


1

ルビー、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 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
2つのグローバルの値が同じ場合、これは失敗すると思います。
マーティンエンダー


@MartinEnder; はい、渡された変数に割り当てられた値はその変数に固有であると仮定します。私が投稿したときにそれを含めるのを忘れていました。
シャギー

@CalebKleveter; それらのいくつかは、渡した変数の値がその変数に固有ではないという事実によるものであり、その一部は無効な変数名(例えばhello--)によるものです。また、varではなくを使用する必要がありますlet
シャギー

1
@CalebKleveter、との間のスコープの違いに関する詳細については、こちらを参照してください。2番目の質問:グローバルスコープ内で名前が付けられた変数があり、その値がであるために発生しました。あなたは上記のテストする前にこれを実行して、既存のあなたのグローバルスコープ内の変数とその値を確認することができます:letvarIN_GLOBAL_SCOPE1for(x in this)console.log(x+": "+this[x])
シャギー

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.