检索代码引用中的数组项
Retrieving array item within code quotation
作为业余爱好项目的一部分,我想动态创建一个函数,当提供一个浮点数组时,它可以使用数组元素和 return 结果执行一些算术运算。
请注意,函数必须在运行时创建。
我相信这可以通过使用FSharp.Quotations.Expr
模块构造一个lambda表达式来完成。
举个简单的例子:
fun (arr: float array) -> 2.0 * arr.[0]
我可以使用代码引用重新创建它,例如:
<@ fun (arr: float array) -> 2.0 * arr.[0] @>
将此打印到控制台会产生:
Lambda (arr,
Call (None, op_Multiply,
[Value (2.0), Call (None, GetArray, [arr, Value (0)])]))
我可以看到所指的 GetArray
位于 FSharp.Core.LanguagePrimitives.IntrinsicFunctions.GetArray
.
我的问题是... 如何在运行时创建 Call (None, GetArray, [arr, Value (0)])
?
到目前为止我的(令人尴尬的)尝试是:
open System
open FSharp.Quotations
let arr = Var("arr", typeof<float array>, false)
let getArray = FSharp.Core.LanguagePrimitives.IntrinsicFunctions.GetArray.GetType().GetMethods().[0]
Expr.Lambda(arr, Expr.Call(getArray, [Expr.Var(arr); Expr.Value(0)]))
这会产生以下异常:
System.ArgumentException: 'Type mismatch when building 'args': invalid parameter for a method or indexer property. Expected 'System.Object[]', but received type 'System.Double[]'.
鉴于 GetArray
看起来是一个通用函数,我不清楚为什么会这样。
我显然误解了一些相当基本的东西!
感谢您提供的任何建议。
我认为这里的问题是你需要为GetArray
本身获取MethodInfo
,这需要反思。我不确定是否有简单的方法可以做到这一点,但以下方法似乎可行:
let arr = Var("arr", typeof<float array>, false)
let getArray =
let asm = System.Reflection.Assembly.Load("FSharp.Core")
let typ = asm.DefinedTypes |> Seq.find (fun typ -> typ.Name = "IntrinsicFunctions")
let getArrayGeneric = typ.GetMethod("GetArray")
getArrayGeneric.MakeGenericMethod(typeof<double>)
let expr = Expr.Lambda(arr, Expr.Call(getArray, [Expr.Var(arr); Expr.Value(0)]))
printfn "%A" expr // Lambda (arr, Call (None, GetArray, [arr, Value (0)]))
这会在FSharp.Core
程序集内找到泛型GetArray
方法,然后用double
实例化它以获得GetArray<double>
的MethodInfo
,这是你需要的。
现有答案完美解决问题。我可以想到一个稍微简化的方法,即从最小引用中提取 GetArray
方法信息,这样您就不必担心使用反射手动查找方法。
open Microsoft.FSharp.Quotations
let arr = Var("arr", typeof<float array>, false)
let getArray<'T> = match <@ ([||] : 'T[]).[0] @> with Patterns.Call(_, mi, _) -> mi | _ -> failwith "Array access was not Call"
let expr = Expr.Lambda(arr, Expr.Call(getArray<float>, [Expr.Var(arr); Expr.Value(0)]))
printfn "%A" expr
如果方法被移动,这可能是未来的证明,但它仍然只在数组访问作为方法调用公开时才有效。
也许更有趣的方法是使用引号拼接,它可以让您将构造数组访问的位分离到一个函数中,但将其保留为引号:
let arr = Var("arr", typeof<float array>, false)
let getArray (e:Expr<'T[]>) (a:Expr<int>) =
<@ (%e).[%a] @>
let expr =
Expr.Lambda(arr,
getArray (Expr.Cast<float array>(Expr.Var(arr)))
(Expr.Cast<int>(Expr.Value(0))))
printfn "%A" expr
作为业余爱好项目的一部分,我想动态创建一个函数,当提供一个浮点数组时,它可以使用数组元素和 return 结果执行一些算术运算。
请注意,函数必须在运行时创建。
我相信这可以通过使用FSharp.Quotations.Expr
模块构造一个lambda表达式来完成。
举个简单的例子:
fun (arr: float array) -> 2.0 * arr.[0]
我可以使用代码引用重新创建它,例如:
<@ fun (arr: float array) -> 2.0 * arr.[0] @>
将此打印到控制台会产生:
Lambda (arr,
Call (None, op_Multiply,
[Value (2.0), Call (None, GetArray, [arr, Value (0)])]))
我可以看到所指的 GetArray
位于 FSharp.Core.LanguagePrimitives.IntrinsicFunctions.GetArray
.
我的问题是... 如何在运行时创建 Call (None, GetArray, [arr, Value (0)])
?
到目前为止我的(令人尴尬的)尝试是:
open System
open FSharp.Quotations
let arr = Var("arr", typeof<float array>, false)
let getArray = FSharp.Core.LanguagePrimitives.IntrinsicFunctions.GetArray.GetType().GetMethods().[0]
Expr.Lambda(arr, Expr.Call(getArray, [Expr.Var(arr); Expr.Value(0)]))
这会产生以下异常:
System.ArgumentException: 'Type mismatch when building 'args': invalid parameter for a method or indexer property. Expected 'System.Object[]', but received type 'System.Double[]'.
鉴于 GetArray
看起来是一个通用函数,我不清楚为什么会这样。
我显然误解了一些相当基本的东西!
感谢您提供的任何建议。
我认为这里的问题是你需要为GetArray
本身获取MethodInfo
,这需要反思。我不确定是否有简单的方法可以做到这一点,但以下方法似乎可行:
let arr = Var("arr", typeof<float array>, false)
let getArray =
let asm = System.Reflection.Assembly.Load("FSharp.Core")
let typ = asm.DefinedTypes |> Seq.find (fun typ -> typ.Name = "IntrinsicFunctions")
let getArrayGeneric = typ.GetMethod("GetArray")
getArrayGeneric.MakeGenericMethod(typeof<double>)
let expr = Expr.Lambda(arr, Expr.Call(getArray, [Expr.Var(arr); Expr.Value(0)]))
printfn "%A" expr // Lambda (arr, Call (None, GetArray, [arr, Value (0)]))
这会在FSharp.Core
程序集内找到泛型GetArray
方法,然后用double
实例化它以获得GetArray<double>
的MethodInfo
,这是你需要的。
现有答案完美解决问题。我可以想到一个稍微简化的方法,即从最小引用中提取 GetArray
方法信息,这样您就不必担心使用反射手动查找方法。
open Microsoft.FSharp.Quotations
let arr = Var("arr", typeof<float array>, false)
let getArray<'T> = match <@ ([||] : 'T[]).[0] @> with Patterns.Call(_, mi, _) -> mi | _ -> failwith "Array access was not Call"
let expr = Expr.Lambda(arr, Expr.Call(getArray<float>, [Expr.Var(arr); Expr.Value(0)]))
printfn "%A" expr
如果方法被移动,这可能是未来的证明,但它仍然只在数组访问作为方法调用公开时才有效。
也许更有趣的方法是使用引号拼接,它可以让您将构造数组访问的位分离到一个函数中,但将其保留为引号:
let arr = Var("arr", typeof<float array>, false)
let getArray (e:Expr<'T[]>) (a:Expr<int>) =
<@ (%e).[%a] @>
let expr =
Expr.Lambda(arr,
getArray (Expr.Cast<float array>(Expr.Var(arr)))
(Expr.Cast<int>(Expr.Value(0))))
printfn "%A" expr