【问题标题】:Extracting the name of a variable提取变量名
【发布时间】:2020-07-23 08:57:06
【问题描述】:

我们如何在 F# 中构建一个函数来输出传入的变量的名称?例如:

let someVar1 = "x"
getVarname someVar1 //output would be "someVar1"

let someVar2 = "y"
getVarname someVar2 //output would be "someVar2"

let f toString = fun a -> printfn "%s: %d" (toString a) a
let x = 1
f getVarname x      //output would be: "x: 1"

我在这里 (get name of a variable or parameter) 在 C# 中发现了一个类似的问题,但我无法在 F# 中使其工作。

【问题讨论】:

  • 我听说过这个函数,但我不知道如何在脚本中使用它。尽管我打开了 Microsoft.FSharp.Core.Operators,但它一直显示错误(“未定义值或构造函数 'nameof'”)。我正在使用 Visual Studio Community 2019 16.5.3。

标签: functional-programming f#


【解决方案1】:

nameof 实现在 F# 核心中具有运算符,但 F# 5 编译器位尚未发布。 当它出现时,您可以使用它来获取符号的名称。

let someVar1 = None
let name = nameof someVar1 // name = "someVar1"

目前,我们可能会滥用动态运算符为我们提供一个 shim,您最终可以将其替换为 nameof

let name = ()
let (?) _ name = string name

用法:

let someVar1 = None
let name = name?someVar1

它读起来还不错,而且你会得到某种程度的自动完成。

如果您真的希望能够在调用站点检索本地名称和值,可以使用引号。

let printVar = function
|  ValueWithName(value, _type, name) -> printfn "%s = %A" name value 
| _ -> ()

不过使用起来有点吵。

let someVar1 = 12
printVar <@ someVar1 @> //prints someVar1 = 12

【讨论】:

  • 非常感谢,阿斯蒂!但是假设我们有一个函数 f getName = (fun a -> printfn "%s: %d" (getName a) a) 并且用户将其称为 f getName x (其中 x 是在某处定义的变量,等于 1) .我想得到“x:1”作为输出,而不是“a:1”。在这种情况下,我该如何使用您的解决方案?
  • nameof 也无法做到这一点。 nameof 将绑定到本地符号。
  • 谢谢!我编辑了我的问题并添加了我上面提供的示例。
  • 我添加了一个替代解决方案。
【解决方案2】:

如果您使用引号和静态方法,您已经可以使用ReflectedDefinition 属性在 F# 4 中捕获变量的名称。以下示例中的Demo.GetVarName 静态方法返回用作参数的变量的名称和值:

open Microsoft.FSharp.Quotations

type Demo = 
  static member GetVarName([<ReflectedDefinition(true)>] x:Expr<int>) = 
    match x with
    | Patterns.WithValue(_, _, Patterns.ValueWithName(value, _, name)) ->
        name, value :?> int
    | _ -> failwithf "Argument was not a variable: %A" x

let test ()= 
  let yadda = 123
  Demo.GetVarName(yadda)

test()

这适用于上面的test() 函数中的局部变量。对于顶级变量(实际上编译为属性),您还需要为PropertyGet 添加一个案例:

match x with
| Patterns.WithValue(_, _, Patterns.ValueWithName(value, _, name)) -> 
    name, value :?> int
| Patterns.WithValue(value, _, Patterns.PropertyGet(_, pi, _)) -> 
    pi.Name, value :?> int
| _ -> failwithf "Argument was not a variable: %A" x

【讨论】:

  • 嗨,托马斯。谢谢你的解释!我使用以下函数尝试了您的完整解决方案(包括 PropertyGet): let f = fun a -> sprintf "%s: %d" (a |> Demo.GetVarName |> fst) a;让 x = 1; fx。输出为“arg00: 1”。
  • 问题在于 |&gt; 运算符是内联的,因此编译器可能会看到类似 (fun arg00 -&gt; Demo.GetVarName(arg00)) a 的内容。这仅适用于直接调用,即Demo.GetVarName(a)
  • 在进行了您现在建议的更正后,我得到“a: 1”作为输出,而不是“x: 1”。是否有可能得到“x:1”?
  • @FRocha 不,这不适用于封装在函数中的代码 - 它会为您提供直接作为参数传递给 Demo.GetVarName 的变量名称。换句话说,如果你想定义一个方法,比如 Chart.Plot(whatever) 来获取参数的变量名,你需要按照我的答案中显示的方式定义它。
猜你喜欢
  • 2011-11-21
  • 2019-01-21
  • 2016-06-30
  • 1970-01-01
  • 2022-11-28
  • 1970-01-01
  • 2014-08-16
  • 2018-06-04
  • 2012-01-08
相关资源
最近更新 更多