gpt4 book ai didi

f# - 生成参数化的 F# 引用

转载 作者:行者123 更新时间:2023-12-04 15:04:37 25 4
gpt4 key购买 nike

假设我们有一个简单的 F# 引用:

类型宠物 = { 名称:字符串 }
让 exprNonGeneric = <@@ System.Func(fun (x : Pet) -> x.Name) @@>

得到的报价是这样的:

val exprNonGeneri : Expr =
NewDelegate (System.Func`2[[FSI_0152+Pet, FSI-ASSEMBLY, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken =b77a5c561934e089]],
x,PropertyGet(一些(x),System.String名称,[]))

现在我想概括它,所以我可以使用在其上定义的任意类型和方法/属性,而不是输入“Pet”和属性“Name”。这是我正在尝试做的事情:

让 exprGeneric<'T, 'R> f = <@@ System.Func<'T, 'R>( %f ) @@>
let exprSpecialized = exprGeneric <@ (fun (x : Pet) -> x.Name) @>

结果表达式现在不同了:

val exprSpecialized : Expr =
NewDelegate (System.Func`2[[FSI_0152+Pet, FSI-ASSEMBLY, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken =b77a5c561934e089]],
委托(delegate)Arg,
应用程序( lambda (X,
PropertyGet(一些(x),System.String名称,[])),
委托(delegate)Arg))

如您所见,第一个表达式和第二个表达式之间的区别在于,在第一种情况下,顶级 NewDelegate 表达式包含 PropertyGet,而第二个表达式将 PropertyGet 包装在 Application/Lambda 表达式中。当我将此表达式传递给外部代码时,它不会期望这种表达式结构并失败。

所以我需要一些方法来构建一个通用版本的引用,所以当它被专门化时,得到的引用是 <@@ System.Func(fun (x : Pet) -> x.Name) @@> 的精确匹配。这可能吗?还是只能选择手动将模式匹配应用于生成的报价并将其转换为我需要的?

更新 .作为一种解决方法,我实现了以下适配器:

让 convertExpr (expr : Expr) =
匹配 expr
| NewDelegate(t, darg, appl) ->
匹配 (darg, appl) 与
| (delegateArg, appl) ->
匹配应用程序
|应用程序(l,ldarg)->
匹配 (l, ldarg) 与
| (Lambda(x, f), delegateArg) ->
Expr.NewDelegate(t, [x], f)
| _ -> 表达式
| _ -> 表达式
| _ -> 表达式

它完成了这项工作 - 我现在可以将表达式从第一种形式转换为第二种形式。但我有兴趣找出这是否可以通过简单的方式实现,而无需遍历表达式树。

最佳答案

我认为不可能做到这一点。在第二种情况下,您插入表达式 <@ (fun (x : Pet) -> x.Name) @> ,使用 Lambda 表示节点,进入另一个表达式中的孔。在这个插入过程中编译器不会简化表达式,所以 Lambda无论您做什么,节点都不会被删除。

但是,您的模式匹配解决方法可以大大简化:

let convertExpr = function
| NewDelegate(t, [darg], Application(Lambda(x,f), Var(arg)))
when darg = arg -> Expr.NewDelegate(t, [x], f)
| expr -> expr

事实上,你更复杂的版本是不正确的。这是因为 delegateArg在您最里面的模式中与先前绑定(bind)的 delegateArg 的值不匹配外部模式的标识符;它是一个新的、新绑定(bind)的标识符,也恰好被称为 delegateArg .其实外 delegateArg标识符的类型为 Var list而内部的类型为 Expr !但是,鉴于编译器生成的表达式形式范围有限,您的损坏版本在实践中可能不会有问题。

编辑

关于您的后续问题,如果我对您的理解正确,则可能无法实现您想要的。与 C# 不同,其中 x => x + 1可以解释为类型为 Func<int,int>Expression<Func<int,int>> , 在 F# fun x -> x + 1始终为 int->int 类型.如果你想得到一个 Expr<int->int> 类型的值那么一般需要使用引号运算符 (<@ @>) .

然而,有一种替代方案可能有用。您可以使用 [<ReflectedDefinition>] let 绑定(bind)函数的属性以使其引用也可用。这是一个例子:
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.ExprShape
open Microsoft.FSharp.Quotations.Patterns
open Microsoft.FSharp.Quotations.DerivedPatterns

let rec exprMap (|P|_|) = function
| P(e) -> e
| ShapeVar(v) -> Expr.Var v
| ShapeLambda(v,e) -> Expr.Lambda(v, exprMap (|P|_|) e)
| ShapeCombination(o,l) -> RebuildShapeCombination(o, l |> List.map (exprMap (|P|_|)))


let replaceDefn = function
| Call(None,MethodWithReflectedDefinition(e),args)
-> Some(Expr.Applications(e, [args]))
| _ -> None


(* plugs all definitions into an expression *)
let plugDefs e = exprMap replaceDefn e

[<ReflectedDefinition>]
let f x = x + 1

(* inlines f into the quotation since it uses the [<ReflectedDefinition>] attribute *)
let example = plugDefs <@ fun y z -> (f y) - (f 2) @>

关于f# - 生成参数化的 F# 引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3412332/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com