5

我正在为 F# 中的 linq 表达式生成器编写简单的 excel 公式。我正在将公式解析为 AST 并使用递归表达式构建器构建表达式。我一直坚持将环境((字符串,表达式)对的映射)传递给该调用中生成的表达式:

Expression.Lambda<System.Func<double>>(eval pexpr).Compile()

其中 pexpr 被解析为 AST 而 eval 是表达式生成器函数。

问题在于定义应该如下所示的类型:

type ExprFunc = Func<ExprFunc map, double>
Expression.Lambda<ExprFunc>(eval pexpr).Compile()

如果 pexpr 包含对 Var("name") 形式的其他表达式的引用,我想注入在环境映射中搜索具有“name”的函数的表达式并调用它,在该调用中传递相同的环境映射。

不幸的是,编译器说不:

此类型定义涉及通过缩写的直接循环引用

有没有办法在.net中定义这种函数类型?

4

1 回答 1

10

如果要编写引用自身的类型声明,则不能使用 F# 类型别名。问题是 F# 类型别名在编译时被删除,因此递归引用会导致无限类型:

Func<Func<Func<Func<... map, double> map, double> map, double> map, double>

在 F# 中,最简单的选择可能是定义一个简单的可区分联合:

type ExprFunc = EF of Func<ExprFunc map, double> 

然后,您可以使用该模式EF f在 F# 函数中获取底层委托。不过,直接这样做是行不通的Expression.Lambda,所以你可能需要类似的东西:

type ExprFunc = Func<ExprFunc map, double> 
and WrappedExprFunc = EF of ExprFunc

调用时Expression.Lambda,您需要使用Func<..>委托作为参数,但您需要修改代码eval以正确处理包装参数(类型为WrappedExprFunc):

Expression.Lambda<ExprFunc>(eval pexpr).Compile()   

顺便说一句,如果您正在生成 C# 表达式树,则将其定义WrappedExprFunc为类可能更容易,因为这样更容易处理。这取决于您的其余代码。

于 2012-09-07T13:22:11.423 回答