首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在F#中从函数中提取工作变量

如何在F#中从函数中提取工作变量
EN

Stack Overflow用户
提问于 2015-04-01 04:32:34
回答 3查看 1.1K关注 0票数 2

我在F#中有一个函数,比如:

代码语言:javascript
复制
let MyFunction x = 
    let workingVariable1 = x + 1
    let workingVariable2 = workingVariable1 + 1
    let y = workingVariable2 + 1
    y

基本上,MyFunction接受一个输入x并返回y。然而,在计算过程中,有一些工作变量(中间变量),由于我的工作性质(土木工程),我需要报告所有的中间结果。如何存储函数的所有工作变量?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-04-01 04:49:37

如果我对你的理解是正确的,那么有几种选择:

代码语言:javascript
复制
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"

退出:

代码语言:javascript
复制
(5, 3, 4)
Y: 5; WV1: 3; WV2: 4

http://ideone.com/eYNwYm

在第一个函数中,使用tuple:https://msdn.microsoft.com/en-us/library/dd233200.aspx

第二种原生数据类型。https://msdn.microsoft.com/en-us/library/dd233205.aspx

票数 1
EN

Stack Overflow用户

发布于 2015-04-01 14:25:10

我不知道你在期待什么样的“报告”。这是一个中间值日志吗?这个日志应该保存多长时间?等等,从我的理解来看,这是我的尝试。这不是理想的解决方案,因为它允许报告中间步骤的,但不知道哪个表达式生成了中间值(我认为您希望知道值nworkingVariable1 = x + 1表达式的输出)。

所以我的解决方案是基于计算表达式的。计算表达式是一种F#“单子”。

首先,您需要定义一个计算表达式:

代码语言:javascript
复制
type LoggingBuilder() =
    let log p = printfn "intermediate result %A" p

    member this.Bind(x, f) = 
        log x
        f x

    member this.Return(x) = 
        x

接下来,我们创建一个计算表达式生成器实例:

代码语言:javascript
复制
let logIntermediate = new LoggingBuilder()

现在您可以像这样重写您的原始函数:

代码语言:javascript
复制
let loggedWorkflow x = 
    logIntermediate
        {
            let! workingVariable1 = x + 1
            let! workingVariable2 = workingVariable1 + 1
            let! y = workingVariable2 + 1
            return y,workingVariable1,workingVariable2
        }

如果运行传入10的loggedWorkflow函数,就会得到以下结果:

代码语言:javascript
复制
> loggedWorkflow 10;;
intermediate result 11
intermediate result 12
intermediate result 13
val it : int * int * int = (13, 11, 12)

正如我所说的,您的中间值将被记录下来,但是您不确定哪一行代码是负责的。

但是,我们可以有一点机会用相应的代码行获得类型的FullName。我们必须稍微修改计算表达式生成器:

代码语言:javascript
复制
member this.Bind(x, f) = 
        log x (f.GetType().FullName)
        f x

和一个日志函数,用于:

代码语言:javascript
复制
let log p f = printfn "intermediate result %A %A" p f

如果再次运行loggedWorkflow函数,传入10个函数,就会得到这个结果(这来自我在FSI中运行的脚本):

代码语言:javascript
复制
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"的日志。假设我们在计算表达式中传递了一个中间结果的额外名称,如下所示:

代码语言:javascript
复制
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“。

然后,我们需要一个额外的类型来存储表达式的名称和值:

代码语言:javascript
复制
type NamedExpression<'T> = {Value:'T ; Name: string}

然后,我们必须重新定义一个infix操作符@@,我们使用une计算表达式:

代码语言:javascript
复制
let (@@) name v = {Value = v; Name = name}

这个操作符只获取表达式的左和右部分,并将其包装在NamedExpression<'T>类型中。

我们还没完成呢。我们必须修改计算表达式生成器的绑定部分:

代码语言:javascript
复制
member this.Bind(x, f) =
        let {Name = n; Value = v} = x
        log v n
        f v

首先,我们将NamedExpression<'T>值解构为名称并提取值。我们对其进行日志记录,并将函数f应用于未包装的值v。Log函数如下所示:

代码语言:javascript
复制
let log p n = printfn "'%s' has intermediate result of : %A" n p

现在,当您运行工作流loggedWorkflow 10;;时,您将得到以下结果:

代码语言:javascript
复制
'wk1' has intermediate result of : 11
'wk2' has intermediate result of : 12
'y' has intermediate result of : 13

也许有更好的方法来做到这一点,比如使用编译器服务之类的,但这是迄今为止我所能做的最好的尝试。

票数 2
EN

Stack Overflow用户

发布于 2015-04-01 13:13:22

这不是一种非常“功能”的方法,但是您可以使用可变变量来存储中间结果:

代码语言:javascript
复制
let mutable workingVariable1 = 0
let mutable workingVariable2 = 0

let MyFunction x = 
    workingVariable1 <- x + 1
    workingVariable2 <- workingVariable1 + 1
    let y = workingVariable2 + 1
    y
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/29382900

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档