我在F#中有一个函数,比如:
let MyFunction x =
let workingVariable1 = x + 1
let workingVariable2 = workingVariable1 + 1
let y = workingVariable2 + 1
y基本上,MyFunction接受一个输入x并返回y。然而,在计算过程中,有一些工作变量(中间变量),由于我的工作性质(土木工程),我需要报告所有的中间结果。如何存储函数的所有工作变量?
发布于 2015-04-01 04:49:37
如果我对你的理解是正确的,那么有几种选择:
let MyFunction1 x =
let workingVariable1 = x + 1
let workingVariable2 = workingVariable1 + 1
let y = workingVariable2 + 1
y,workingVariable1,workingVariable2
MyFunction1 2 |> printfn "%A"
type OneType()=
member val Y = 0 with get,set
member val WV1 = 0 with get,set
member val WV2 = 0 with get,set
override this.ToString() =
sprintf "Y: %d; WV1: %d; WV2: %d\n" this.Y this.WV1 this.WV2
let MyFunction2 x =
let workingVariable1 = x + 1
let workingVariable2 = workingVariable1 + 1
let y = workingVariable2 + 1
new OneType(Y=y,WV1=workingVariable1,WV2=workingVariable2)
MyFunction2 2 |> printfn "%A"退出:
(5, 3, 4)
Y: 5; WV1: 3; WV2: 4http://ideone.com/eYNwYm
在第一个函数中,使用tuple:https://msdn.microsoft.com/en-us/library/dd233200.aspx
第二种原生数据类型。https://msdn.microsoft.com/en-us/library/dd233205.aspx
发布于 2015-04-01 14:25:10
我不知道你在期待什么样的“报告”。这是一个中间值日志吗?这个日志应该保存多长时间?等等,从我的理解来看,这是我的尝试。这不是理想的解决方案,因为它允许报告中间步骤的值,但不知道哪个表达式生成了中间值(我认为您希望知道值n是workingVariable1 = x + 1表达式的输出)。
所以我的解决方案是基于计算表达式的。计算表达式是一种F#“单子”。
首先,您需要定义一个计算表达式:
type LoggingBuilder() =
let log p = printfn "intermediate result %A" p
member this.Bind(x, f) =
log x
f x
member this.Return(x) =
x接下来,我们创建一个计算表达式生成器实例:
let logIntermediate = new LoggingBuilder()现在您可以像这样重写您的原始函数:
let loggedWorkflow x =
logIntermediate
{
let! workingVariable1 = x + 1
let! workingVariable2 = workingVariable1 + 1
let! y = workingVariable2 + 1
return y,workingVariable1,workingVariable2
}如果运行传入10的loggedWorkflow函数,就会得到以下结果:
> loggedWorkflow 10;;
intermediate result 11
intermediate result 12
intermediate result 13
val it : int * int * int = (13, 11, 12)正如我所说的,您的中间值将被记录下来,但是您不确定哪一行代码是负责的。
但是,我们可以有一点机会用相应的代码行获得类型的FullName。我们必须稍微修改计算表达式生成器:
member this.Bind(x, f) =
log x (f.GetType().FullName)
f x和一个日志函数,用于:
let log p f = printfn "intermediate result %A %A" p f如果再次运行loggedWorkflow函数,传入10个函数,就会得到这个结果(这来自我在FSI中运行的脚本):
intermediate result 11 "FSI_0003+loggedWorkflow@34"
intermediate result 12 "FSI_0003+loggedWorkflow@35-1"
intermediate result 13 "FSI_0003+loggedWorkflow@36-2"这是一个黑客,但是我们得到了一些额外的信息,比如workingVariable1 = x + 1表达式是在哪里定义的(在我的例子中是"FSI_"),以及在哪一行代码上(@34,@35-1)。如果您的代码更改并且很可能发生这种情况,那么如果记录了很长一段时间,您的中间结果将是错误的。请注意,我还没有在FSI之外测试它,也不知道是否在每种情况下都包含了代码行。
我不确定是否可以从计算表达式中获得表达式名称(如workingVariable1 = x + 1)。我觉得这不可能。
注意:与日志函数不同,您可以定义一些其他函数,这些函数将这个中间步骤持久化到持久存储或其他任何地方。
更新
我试着想出一个不同的解决方案,但这并不容易。不过,我可能会妥协。让我解释一下。您无法获得值的名称绑定到计算表达式中。因此,我们无法记录例如表达式workingVariable1 = x + 1和"'workingVariable1' result is 2"的日志。假设我们在计算表达式中传递了一个中间结果的额外名称,如下所示:
let loggedWorkflow x =
logIntermediate
{
let! workingVariable1 = "wk1" @@ x + 1
let! workingVariable2 = "wk2" @@ workingVariable1 + 1
let! y = "y" @@ workingVariable2 + 1
return y,workingVariable1,workingVariable2
}正如您在@@签名之前所看到的,我们给出了中间结果的名称,这样let! workingVariable1 = "wk1" @@ x + 1行将被记录为"wk1“。
然后,我们需要一个额外的类型来存储表达式的名称和值:
type NamedExpression<'T> = {Value:'T ; Name: string}然后,我们必须重新定义一个infix操作符@@,我们使用une计算表达式:
let (@@) name v = {Value = v; Name = name}这个操作符只获取表达式的左和右部分,并将其包装在NamedExpression<'T>类型中。
我们还没完成呢。我们必须修改计算表达式生成器的绑定部分:
member this.Bind(x, f) =
let {Name = n; Value = v} = x
log v n
f v首先,我们将NamedExpression<'T>值解构为名称并提取值。我们对其进行日志记录,并将函数f应用于未包装的值v。Log函数如下所示:
let log p n = printfn "'%s' has intermediate result of : %A" n p现在,当您运行工作流loggedWorkflow 10;;时,您将得到以下结果:
'wk1' has intermediate result of : 11
'wk2' has intermediate result of : 12
'y' has intermediate result of : 13也许有更好的方法来做到这一点,比如使用编译器服务之类的,但这是迄今为止我所能做的最好的尝试。
发布于 2015-04-01 13:13:22
这不是一种非常“功能”的方法,但是您可以使用可变变量来存储中间结果:
let mutable workingVariable1 = 0
let mutable workingVariable2 = 0
let MyFunction x =
workingVariable1 <- x + 1
workingVariable2 <- workingVariable1 + 1
let y = workingVariable2 + 1
yhttps://stackoverflow.com/questions/29382900
复制相似问题