我正在为F#中的linq表达式构建器编写简单的excel公式.我正在使用递归表达式构建器将公式解析为AST并构建表达式.我一直坚持将环境((字符串,表达式)映射对)传递给该调用中生成的表达式:
Expression.Lambda<System.Func<double>>(eval pexpr).Compile()
Run Code Online (Sandbox Code Playgroud)
解析pexpr的位置AST和eval是表达式构建函数.
问题是定义类型应该如下所示:
type ExprFunc = Func<ExprFunc map, double>
Expression.Lambda<ExprFunc>(eval pexpr).Compile()
Run Code Online (Sandbox Code Playgroud)
如果pexpr包含对Var("name")形式的其他表达式的引用,我想注入在环境映射中使用"name"搜索函数的表达式并调用它,在该调用中传递相同的环境映射.
不幸的是,编译器说不:
此类型定义涉及通过缩写的立即循环引用
有没有办法在.net中定义这样的函数类型?
如果要编写引用自身的类型声明,则不能使用F#类型别名.问题是F#类型别名在编译时被擦除,因此递归引用将导致无限类型:
Func<Func<Func<Func<... map, double> map, double> map, double> map, double>
Run Code Online (Sandbox Code Playgroud)
在F#中,最简单的替代方法可能是定义一个简单的区分联合:
type ExprFunc = EF of Func<ExprFunc map, double>
Run Code Online (Sandbox Code Playgroud)
然后,您可以使用该模式EF f在F#函数中获取基础委托.直接这样做不会起作用Expression.Lambda,所以你可能需要这样的东西:
type ExprFunc = Func<ExprFunc map, double>
and WrappedExprFunc = EF of ExprFunc
Run Code Online (Sandbox Code Playgroud)
在调用时Expression.Lambda,您需要使用Func<..>委托作为参数,但是您需要修改代码eval以正确处理包装的参数(这将是类型WrappedExprFunc):
Expression.Lambda<ExprFunc>(eval pexpr).Compile()
Run Code Online (Sandbox Code Playgroud)
顺便说WrappedExprFunc一句,如果您正在生成C#表达式树,那么将其定义为类可能更容易,因为这样更容易处理.这取决于你的其余代码.