打印变量名称


20

编写一个函数(不是完整的程序),以便如果使用单个全局变量(或您的语言最接近的等效变量)作为参数调用该函数,则该函数将输出(即打印或返回)该变量的名称。如果参数不是变量,则输出false值。(您不必处理参数是变量而不是全局变量的情况。)

函数调用和函数定义之间不得存在编译时连接(值得注意的是,函数定义不能是宏或类似的构造,该构造或构造以文本或抽象语法树的形式接收源代码的文字片段形式的参数形成)。也就是说:在支持单独编译的语言中,即使函数调用先被编译(不了解函数定义,但可能是类型签名或等效形式),程序也必须运行,然后再编译函数定义。如果该语言没有单独的编译,则仍然必须在调用和定义之间进行类似的分离。

如果使用编译语言,则可以从磁盘读取完整程序的编译形式,并且可以假定该程序是使用调试信息编译的。(因此,允许通过将调试器从程序附加到其自身而起作用的解决方案。)

请注意,并非每种语言都可能完成此任务。

例子:

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

评论不作进一步讨论;此对话已转移至聊天
丹尼斯,

Answers:


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

目前,这适用于一些陷阱:

  1. 如果您使用IDE进行编译,除非以Admin身份运行(取决于保存临时类文件的位置),否则它可能无法工作。
  2. 您必须使用javac带有-g标志的编译。这将在编译的类文件中生成所有调试信息,包括本地变量名称。
  3. 它使用内部Java API com.sun.tools.javap来解析类文件的字节码并产生易于阅读的结果。该API仅可在JDK库中访问,因此您必须使用JDK Java运行时或将tools.jar添加到类路径中。

即使该方法在程序中多次调用,现在也应该可以使用。不幸的是,如果您在一行上有多个调用,它还无法正常工作。(有关的内容,请参见下文)

在线尝试!


说明

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,第二个条目是调用函数的位置。

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输出逐行读取到一个列表中。其次,我们正在创建字节码行索引到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;
}

这部分实际上是在解析load调用以获取变量索引。如果实际上未使用变量调用该函数,则可能引发异常,因此我们可以在此处返回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中查找变量。


有趣的是,这里处理在同一行上的多个函数调用。这导致函数不是幂等的,但是这很重要。在线尝试!


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")
奥利维尔·格雷戈尔

@OlivierGrégoire谢谢,但是我切换到通过内部Java api调用javap,所以我不再需要在代码中获取磁盘上的确切位置(仅是类路径)

@ PunPun1000是的,我不确定在保持幂等函数的同时是否有解决此问题的好方法,但是我可能会放在一起,只是使用函数外的状态来取乐。

1
@ PunPun1000添加了可以在一行上处理多个呼叫的版本。它不仅仅是功能,所以我只添加了另一个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
Dead Possum

1
@DeadPossum始终发布并回答,并在以后添加解释以作为编辑。;)
Arjun

8
@Arjun,哦,太好了,我们必须同时玩EditGolf和CodeGolf:P
Wossname

12

Mathematica

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

HoldFirst属性可防止f在调用函数之前评估其参数。ValueQ @ x然后检查给定的参数是否为已赋予值的变量。如果没有,我们只是False由于短路而返回。否则,我们使用获取变量名ToString @ HoldForm @ x


Dang,滥用Mathematica的处理方式And...我喜欢的正确参数And甚至不必是布尔表达式。
JungHwan Min

我知道这不是编码高尔夫球,但为什么不f[x_] := ValueQ @ x && HoldForm @ x呢?如果不需要检查输入是否为变量,则可以单独HoldForm使用。
ngenisis

HoldAll代替HoldFirst
格雷格·马丁

1
@ngenisis因为这返回了HoldForm[a]而不是"a"。当它们在Mathematica笔记本中显示相同时,该函数独立于前端而存在,因此我希望有一个返回字符串的解决方案。
Martin Ender

1
请求删除空格,因为...嗯... 因为
CalculatorFeline

7

Mathematica

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文档指出的那样: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。我相信它也会在编译时评估输入,因此似乎也无法满足该要求。


这有点狡猾……因为lambda内容绝对必不可少(即,您不能只传递变量,而必须传递编译器将识别为的东西Expression)。您还应该数数using System.Linq.Expressions;
VisualMelon

但是,这是否算作“以抽象语法树形式接收参数”?
Matti Virkkunen

@VisualMelon我在C#中找不到其他方法可以做到这一点,所以我这样做了。同样,这也不是代码问题,所以没关系,
TheLethalCoder

...对不起,没有意识到没有字节数!
VisualMelon

6

的MATLAB

varname = @(x) inputname(1);

在一个函数中,有一些预设变量,例如vararginnargin,我们也有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对函数参数而不是工作区变量进行赋值。
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()

这使用检查来检查环绕范围并找到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()删除所有可能放置在bracets中的空格。

输入一些棘手的示例


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我知道目前我没有更好的主意。
约尔格Hülsermann


4

JavaScript(ES6)

对于windowObject.getOwnPropertyNames(w))中的所有属性名称,尝试为该属性定义一个吸气剂,以返回该属性名称。

然后,将一个条目添加到Map中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。为什么会怎样呢?
卡勒布·克里夫特

哇!这比我一段时间以来对ES6的了解更深。
MD XF

@CalebKleveter D'哦!我在第二行忘记了逗号。那可能有问题也可能没有。
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(类似于调试器的模块)遍历调用堆栈,查找对函数的调用,并返回参数中的所有操作数,并将它们作为变量名返回。如果有多个(或少于一个)操作数,则不会使用变量进行调用。如果操作数不是变量,它将没有名称,我们也可以检测到该名称。

最棘手的情况是,如果我们得到一个包含变量的单操作数表达式,例如~$x。我们可以通过直接从符号表中引用变量来确定是否发生了这种情况(使用${…}符号引用语法)并将其内存地址与我们作为参数传递的值(方便地通过引用传递)进行比较,从而确定是否发生了这种情况)。如果它们不同,则我们有一个表达式而不是一个单独的变量。

请注意,如果我们在单个变量上使用预递增或递减表达式调用此函数,如中所示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";
}

即使全局变量在调用时引用了另一个值,这现在也应该起作用。

在这里测试。


大和非常感谢你对学习的努力
约尔格Hülsermann

@JörgHülsermann甚至找到了改进的方法!
Christoph

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

在第二行,的元素a被推送到流(a())。如果给了函数变量和正常值,则这些元素是引用。

使用下划线循环对元素进行迭代。下划线语法是for循环的语法糖,因此name(_)等效于name(var) for varfor循环中使用的变量名称的形式为<sfvN>N变化。(即“ name(<sfv1>) for <sfv1>”)用户定义的变量包含<或是非法的>,因此生成的名称不会与现有的变量名称冲突。

name()函数返回给定变量的名称。如果a要迭代的元素是引用,则它是给的变量name。否则,如果元素是正常值,则给定的变量name是下划线变量<sfvN>。这是由于Röda中引用的语义所致:如果一个函数接受一个引用,并且该函数被传递了一个引用变量,则传递的值并不指向引用变量,而是指向引用变量所指向的变量。

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

在第三行,我们检查流中的变量名称是否包含<字符。如果是这样,则它是一个下划线变量,并且传递给的值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
我认为如果两个全局变量具有相同的值,则此操作将失败。
Martin Ender


@MartinEnder; 是的,它假定分配给传递的变量的值对该变量是唯一的;我发布时忘了包括在内。
毛茸茸的

@CalebKleveter; 其中一些是由于您传递的变量的值不是该变量唯一的事实,还有一些是由于无效的变量名称(例如hello--)引起的。另外,您需要使用var而不是let
毛茸茸的

1
@CalebKleveter,看到这里的在划定范围之间的区别更多的信息letvar。关于第二个问题:发生这种情况是因为您有一个IN_GLOBAL_SCOPE在全局范围内命名的变量,并且其值是1。您可以在测试上述内容之前运行此命令,以检查全局范围中的现有变量及其值:for(x in this)console.log(x+": "+this[x])
Shaggy

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.