Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
生成参数化F#报价单_F#_Quotations - Fatal编程技术网

生成参数化F#报价单

生成参数化F#报价单,f#,quotations,F#,Quotations,假设我们有一个简单的F#报价: type Pet = { Name : string } let exprNonGeneric = <@@ System.Func(fun (x : Pet) -> x.Name) @@> 类型Pet={Name:string} 让exprNonGeneric=x.Name)@> 由此产生的报价如下所示: val exprNonGeneri : Expr = NewDelegate (System.Func`2[[FSI_0152+Pet, FS

假设我们有一个简单的F#报价:

type Pet = { Name : string } let exprNonGeneric = <@@ System.Func(fun (x : Pet) -> x.Name) @@> 类型Pet={Name:string} 让exprNonGeneric=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 (Some (x), System.String Name, [])) val exprNonGeneri:Expr= NewDelegate(System.Func`2[[FSI_0152+Pet,FSI-ASSEMBLY,版本=0.0.0,区域性=中性,PublicKeyToken=null],[System.String,mscorlib,版本=4.0.0.0,区域性=中性,PublicKeyToken=b77a5c561934e089], x、 PropertyGet(一些(x),System.String名称,[])) 现在我想对它进行泛化,所以我可以使用定义在它上面的任意类型和方法/属性,而不是类型“Pet”和属性“Name”。以下是我试图做的:

let exprGeneric<'T, 'R> f = <@@ System.Func<'T, 'R>( %f ) @@> let exprSpecialized = exprGeneric<Pet, string> <@ (fun (x : Pet) -> x.Name) @> 让排出管f= 让exprsspecialized=exprGeneric 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]], delegateArg, Application (Lambda (x, PropertyGet (Some (x), System.String Name, [])), delegateArg)) val exprSpecialized:Expr= NewDelegate(System.Func`2[[FSI_0152+Pet,FSI-ASSEMBLY,版本=0.0.0,区域性=中性,PublicKeyToken=null],[System.String,mscorlib,版本=4.0.0.0,区域性=中性,PublicKeyToken=b77a5c561934e089], 委托人, 应用程序(λ(x, PropertyGet(一些(x),System.String名称,[]),它们, 委托人(arg) 如您所见,第一个表达式和第二个表达式之间的区别在于,在第一种情况下,顶级NewDelegate表达式包含PropertyGet,而第二个表达式在Application/Lambda表达式中包装PropertyGet。当我将这个表达式传递给外部代码时,它并不期望这样的表达式结构,因此失败了

因此,我需要某种方法来构建Quotence的通用版本,因此当它变得专门化时,得到的Quotence与x.Name)@>完全匹配。这可能吗?还是只有手动将模式匹配应用于生成的报价并将其转换为我需要的

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

let convertExpr (expr : Expr) = match expr with | NewDelegate(t, darg, appl) -> match (darg, appl) with | (delegateArg, appl) -> match appl with | Application(l, ldarg) -> match (l, ldarg) with | (Lambda(x, f), delegateArg) -> Expr.NewDelegate(t, [x], f) | _ -> expr | _ -> expr | _ -> expr 让convertExpr(expr:expr)= 匹配表达式 |新代表(t、darg、appl)-> 匹配 |(委托人参数,应用)-> 匹配应用程序 |应用程序(l,ldarg)-> 将(l,ldarg)与 |(Lambda(x,f),delegateArg)-> Expr.NewDelegate(t[x],f) |_uu->expr |_uu->expr |_uu->expr
它完成了工作-我现在可以将表达式从第一种形式转换为第二种形式。但是我很想知道这是否可以通过一种简单的方式实现,而不需要遍历表达式树。

我认为这是不可能的;在第二种情况下,将表达式
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
与外部模式中先前绑定的
delegateArg
标识符的值不匹配;它是一个新的、新绑定的标识符,也被称为
delegateArg
。事实上,外部的
delegateArg
标识符具有类型
Var list
,而内部标识符具有类型
Expr
!然而,考虑到编译器生成的表达式形式的有限范围,您的错误版本在实践中可能不会有问题

编辑

关于你的后续问题,如果我理解正确,可能无法实现你想要的。与C不同,
x=>x+1
可以被解释为具有
Func
Expression
类型,在F#
fun x->x+1
中,类型总是
int->int
。如果要获取类型为
Exprint>
的值,通常需要使用引号运算符
()

然而,还有一种可能有用的替代方案。您可以在let绑定函数上使用
[]
属性,使其引用也可用。下面是一个例子:

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) @>
打开Microsoft.FSharp.quotes
打开Microsoft.FSharp.quotes.ExprShape
打开Microsoft.FSharp.quotes.Patterns
打开Microsoft.FSharp.Quotences.DerivedPatterns
设rec exprMap(| P | | |)=函数
|P(e)->e
|形状变量(v)->表达式变量v
|ShapeLambda(v,e)->表达式Lambda(v,exprMap(| P | | | |)e)
|形状组合(o,l)->重建形状组合(o,l |>List.map(exprMap(| P | | |)))
设replaceDefn=函数
|调用(无,带有反射定义(e)的方法,参数)
->一些(示例应用程序(例如,[args]))
|无
(*将所有定义插入表达式*)
让plugDefs e=exprMap replaceDefn e
[]
设fx=x+1
(*将f内联到引号中,因为它使用[]属性*)
例如:plugDefs(fy)-(f2)@>

感谢您的回答和建议。然而,我仍然被相关问题所困扰:是否有可能将一个简单的F#委托(例如,fun x->x.Name)插入到一个独立于实际类型的通用引号中,如上面的引号#2。这类似于模拟框架所做的:它们在不知道具体接口的情况下定义一些表达式,并注入具体类型。在F#@Vagif中,我似乎无法做到这一点——我不确定我是否理解你的问题。你能详细介绍一下你想做什么吗?我需要发送F和C之间的互操作。C#需要某种LINQ表达式。我可以用硬编码的方式用F#编写它,但我想为此构建一个通用的F#包装器。这个包装器应该能够将lambda作为输入,比如“funx->x.Name”,并将它们转换成引号。我开始怀疑这在一般情况下是不可能的。@Vagif-我补充了一些想法