如何使用 F# 获取作为参数传入函数的变量名称?

发布于 2024-08-31 02:21:07 字数 714 浏览 7 评论 0原文

F# 中有没有办法获取传递给函数的变量的名称?

示例:

let velocity = 5
let fn v = v.ParentName
let name = fn velocity // this would return "velocity" as a string

提前谢谢您

编辑:

为什么这段代码不起作用?它作为值匹配,因此我无法检索“变量”名称。

type Test() =
  let getName (e:Quotations.Expr) =
    match e with
      | Quotations.Patterns.PropertyGet (_, pi, _) -> pi.Name + " property"
      | Quotations.Patterns.Value(a) -> failwith "Value matched"
      | _ -> failwith "other matched"
  member x.plot v = v |> getName |> printfn "%s"

let o = new Test()

let display () =
  let variable = 5.
  o.plot <@ variable @>

let runTheCode fn = fn()

runTheCode display

Is there any way in F# how to get a name of a variable passed into a function?

Example:

let velocity = 5
let fn v = v.ParentName
let name = fn velocity // this would return "velocity" as a string

Thank you in advance

EDIT:

Why this code does not work? It is matched as value, so I can not retrieve the "variable" name.

type Test() =
  let getName (e:Quotations.Expr) =
    match e with
      | Quotations.Patterns.PropertyGet (_, pi, _) -> pi.Name + " property"
      | Quotations.Patterns.Value(a) -> failwith "Value matched"
      | _ -> failwith "other matched"
  member x.plot v = v |> getName |> printfn "%s"

let o = new Test()

let display () =
  let variable = 5.
  o.plot <@ variable @>

let runTheCode fn = fn()

runTheCode display

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

喜爱皱眉﹌ 2024-09-07 02:21:07

为了完成 Marcelo 的回答,是的,您可以使用引号来完成此任务:

open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns

let velocity = 5

let fn (e:Expr) =
  match e with
    | PropertyGet (e, pi, li) -> pi.Name
    | _ -> failwith "not a let-bound value"

let name = fn <@velocity@> 

printfn "%s" name

正如您在代码中看到的,F# let-bound 顶部定义值(函数或变量)被实现为类的属性。

我再也找不到显示如何使用 C# 以函数方式重写一段 F# 代码的链接。看到代码,就很明显为什么需要 PropertyGet 模式。

现在,如果您也想计算表达式,则需要安装 F# powerpack 并在您的项目中引用FSharp.PowerPack.Linq

它在 Expr 类上添加了一个 EvalUntyped 方法。

open Microsoft.FSharp.Linq.QuotationEvaluation

let velocity = 5

let fn (e:Expr) =
  match e with
    | PropertyGet (eo, pi, li) -> pi.Name, e.EvalUntyped
    | _ -> failwith "not a let-bound value"

let name, value = fn <@velocity@> 

printfn "%s %A" name value

此操作,我会这样做:

let velocity = 5

type Foo () =
  member this.Bar (x:int) (y:single) = x * x + int y

let extractCallExprBody expr =
  let rec aux (l, uexpr) =
    match uexpr with
     | Lambda (var, body) -> aux (var::l, body)
     | _ -> uexpr
  aux ([], expr)

let rec fn (e:Expr) =
  match e with
    | PropertyGet (e, pi, li) -> pi.Name
    | Call (e, mi, li) -> mi.Name
    | x -> extractCallExprBody x |> fn
    | _ -> failwith "not a valid pattern"

let name = fn <@velocity@> 
printfn "%s" name

let foo = new Foo()

let methodName = fn <@foo.Bar@>
printfn "%s" methodName

如果您需要对实例的方法执行 显示 EvalUntyped 用法的代码片段,如果需要,您可以为 Expr 添加显式类型参数和向下转型 (:?>)需要保持类型安全:

let fn (e:Expr<'T>) = 
  match e with
    | PropertyGet (eo, pi, li) -> pi.Name, (e.EvalUntyped() :?> 'T)
    | _ -> failwith "not a let-bound value"
    
let name, value = fn <@velocity@> //value has type int here
printfn "%s %d" name value

For completing Marcelo's answer, yes you can use quotations for this task:

open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns

let velocity = 5

let fn (e:Expr) =
  match e with
    | PropertyGet (e, pi, li) -> pi.Name
    | _ -> failwith "not a let-bound value"

let name = fn <@velocity@> 

printfn "%s" name

As you can see in the code, F# let-bound top definition values (functions or variables) are implemented as properties of a class.

I can't find anymore the link that shows how a piece of F# code could be rewritten in a functional way with C#. Seeing the code, it becomes obvious why you need a PropertyGet pattern.

Now if you want to evaluate the expression too, you will need to install F# powerpack and reference FSharp.PowerPack.Linq in your project.

It adds an EvalUntyped method on Expr class..

open Microsoft.FSharp.Linq.QuotationEvaluation

let velocity = 5

let fn (e:Expr) =
  match e with
    | PropertyGet (eo, pi, li) -> pi.Name, e.EvalUntyped
    | _ -> failwith "not a let-bound value"

let name, value = fn <@velocity@> 

printfn "%s %A" name value

If you need to do it for the method of an instance, here's how I would do it:

let velocity = 5

type Foo () =
  member this.Bar (x:int) (y:single) = x * x + int y

let extractCallExprBody expr =
  let rec aux (l, uexpr) =
    match uexpr with
     | Lambda (var, body) -> aux (var::l, body)
     | _ -> uexpr
  aux ([], expr)

let rec fn (e:Expr) =
  match e with
    | PropertyGet (e, pi, li) -> pi.Name
    | Call (e, mi, li) -> mi.Name
    | x -> extractCallExprBody x |> fn
    | _ -> failwith "not a valid pattern"

let name = fn <@velocity@> 
printfn "%s" name

let foo = new Foo()

let methodName = fn <@foo.Bar@>
printfn "%s" methodName

Just to come back on the code snippet showing usage of EvalUntyped, you can add an explicit type parameter for Expr and a downcast (:?>) if you want/need to keep things type-safe:

let fn (e:Expr<'T>) = 
  match e with
    | PropertyGet (eo, pi, li) -> pi.Name, (e.EvalUntyped() :?> 'T)
    | _ -> failwith "not a let-bound value"
    
let name, value = fn <@velocity@> //value has type int here
printfn "%s %d" name value
滥情哥ㄟ 2024-09-07 02:21:07

您也许可以通过代码引用来实现此目的:

let name = fn <@ velocity @>

fn 函数将传递一个 Expr 对象,该对象必须转换为 Quotations.Var (仅当您传递单个变量时才会如此)并提取 Name 实例成员。

You might be able to achieve this with code quotations:

let name = fn <@ velocity @>

The fn function will be passed an Expr object, which it must cast to Quotations.Var (which it will only be if you pass a single variable) and extract the Name instance member.

左岸枫 2024-09-07 02:21:07

基于之前的解决方案,我提出了一个更通用的解决方案,您可以在其中获取函数名称、lambda、值、属性、方法、静态方法、公共字段、联合类型:

open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns

let cout (s:string)= System.Console.WriteLine (s)

let rec getName exprs =
    let fixDeclaringType (dt:string) =
        match dt with
        | fsi  when fsi.StartsWith("FSI_") -> "Fsi"
        | _ -> dt
    let toStr (xDeclType: System.Type) x = sprintf "%s.%s" (fixDeclaringType xDeclType.Name)  x
    match exprs with
    | Patterns.Call(_, mi, _) -> 
        toStr mi.DeclaringType mi.Name
    | Patterns.Lambda(_, expr) -> 
        getName expr
    | Patterns.PropertyGet (e, pi, li) ->  
        toStr pi.DeclaringType pi.Name
    | Patterns.FieldGet (_, fi) -> 
        toStr fi.DeclaringType fi.Name
    | Patterns.NewUnionCase(uci, _) -> 
        toStr uci.DeclaringType uci.Name
    | expresion -> "unknown_name"


let value = ""
let funcky a = a
let lambdy = fun(x) -> x*2
type WithStatic = 
    | A | B
    with static member StaticMethod a = a
let someIP = System.Net.IPAddress.Parse("10.132.0.48")


getName <@ value @> |> cout
getName <@ funcky @> |> cout
getName <@ lambdy @> |> cout
getName <@ WithStatic.A @> |> cout
getName <@ WithStatic.StaticMethod @> |> cout
getName <@ someIP.MapToIPv4 @> |> cout  
getName <@ System.Net.IPAddress.Parse @> |> cout  
getName <@ System.Net.IPAddress.Broadcast @> |> cout  

Based on the previous solutions I came out with a more generic solution where you can get the name of functions, lambdas, values, properties, methods, static methods, public fields, Union types:

open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns

let cout (s:string)= System.Console.WriteLine (s)

let rec getName exprs =
    let fixDeclaringType (dt:string) =
        match dt with
        | fsi  when fsi.StartsWith("FSI_") -> "Fsi"
        | _ -> dt
    let toStr (xDeclType: System.Type) x = sprintf "%s.%s" (fixDeclaringType xDeclType.Name)  x
    match exprs with
    | Patterns.Call(_, mi, _) -> 
        toStr mi.DeclaringType mi.Name
    | Patterns.Lambda(_, expr) -> 
        getName expr
    | Patterns.PropertyGet (e, pi, li) ->  
        toStr pi.DeclaringType pi.Name
    | Patterns.FieldGet (_, fi) -> 
        toStr fi.DeclaringType fi.Name
    | Patterns.NewUnionCase(uci, _) -> 
        toStr uci.DeclaringType uci.Name
    | expresion -> "unknown_name"


let value = ""
let funcky a = a
let lambdy = fun(x) -> x*2
type WithStatic = 
    | A | B
    with static member StaticMethod a = a
let someIP = System.Net.IPAddress.Parse("10.132.0.48")


getName <@ value @> |> cout
getName <@ funcky @> |> cout
getName <@ lambdy @> |> cout
getName <@ WithStatic.A @> |> cout
getName <@ WithStatic.StaticMethod @> |> cout
getName <@ someIP.MapToIPv4 @> |> cout  
getName <@ System.Net.IPAddress.Parse @> |> cout  
getName <@ System.Net.IPAddress.Broadcast @> |> cout  
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文