Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
F# 是否将异步工作流生成器更改为计数步骤?_F#_Monads - Fatal编程技术网

F# 是否将异步工作流生成器更改为计数步骤?

F# 是否将异步工作流生成器更改为计数步骤?,f#,monads,F#,Monads,我的理解是,工作流生成器所做的是首先“构建”表达式,然后执行它。因此,考虑到它首先构建表达式,它应该能够计算let语句,对吗?然后它应该能够注入一些日志来监控进度?那么,是否可以重新编写asyncbuilder以自动报告进度并消除下面的printfn冗余 async { let! a = doSomething1 () printfn "%d/%d" 1 4 let! b = doSomething2 a printfn "%d/%d" 2 4 let! c =

我的理解是,工作流生成器所做的是首先“构建”表达式,然后执行它。因此,考虑到它首先构建表达式,它应该能够计算
let语句,对吗?然后它应该能够注入一些日志来监控进度?那么,是否可以重新编写
async
builder以自动报告进度并消除下面的
printfn
冗余

 async {
   let! a = doSomething1 ()
   printfn "%d/%d" 1 4
   let! b = doSomething2 a
   printfn "%d/%d" 2 4
   let! c = doSomething3 b
   printfn "%d/%d" 3 4
   let! d = doSomething4 c
   printfn "%d/%d" 4 4
   return d
 }
对于循环,我想假设整个循环是一个步骤。在这里,只有顶级表达式算作步骤

(请注意,如果有一种方法可以做到这一点,而不必制作一个全新的工作流生成器,我想这也很好)


注意,我已经经历了a)制作一个只迭代任务的“任务”迭代器的过程(但随后你失去了例如
使用
处理,因此它最终不充分),和b)制作一个任务计数器,但这总是需要手动播种和迭代,所以我希望有更好的方法。

你可以使用元组
('a,int,int)
跟踪当前结果、步骤总数和到目前为止执行的步骤数。然后,您可以编写一个函数以获取当前状态,以及下一个要执行的异步函数,例如

//create the initial state
let startCount steps = ((), 0, steps)

let withCount af (a, c, steps) = async {
    let nc = c + 1
    let! res = af a
    do printfn "%d %d" nc steps
    return (res, nc, steps)
}
withCount
使用一个函数返回下一个异步操作和当前状态。它创建下一个工作流,增加执行步骤的数量,并在返回新状态之前打印状态

然后,您可以像这样使用它:

async {
    let init = startCount 4
    let! t = withCount doSomething init
    let! t2 = withCount doSomething2 t
    let! (r, _, _) = withCount doSomething3 t2
    return r
}

当你用标记单子来标记这个问题时,我将从一个理论上的挑剔开始。你想要做的实际上不是单子。问题是单子需要某些定律(请参阅)。对于F#,这意味着以下两个片段的意思应该是相同的:

let computation1 = 
  async { let! x = m
          return x }
let computation2 = m
对于您建议的扩展,情况并非如此,因为
computation1
computation2
多了一个
let!
。现在,我不认为这实际上是一个问题-日志记录可能仍然有用(即使它在某些情况下给出的结果可能与您预期的不同)


将此功能添加到F#async并不容易-问题是您需要定义自己的类型来替换(或包装)标准
AsyncYeah这基本上是我已经想到的最好的选择。它在C语言中也能工作。我希望能有一些东西,真的只是作为一个借口,在那里我可以告诉老板我们需要F,在那里我可以使用工作流或一些F-特定的功能来消除完全考虑它的需要。是的,它在写了一个问题后,我突然想到它实际上与异步没有任何特别的关系——我真正想要的只是一个步骤计数器;将它与异步步骤混为一谈仅仅是一个域细节。然而,我问题的关键更多的是我的构建器能否识别出有N个let!(/do!/for/where)我的工作流程中的顶级语句,而我不必明确指定,并且它当前正在执行N的步骤i?这是可能的吗?(最好不要将
use
作为顶级构造包含在内;这样
use
中的任何
let!
都将独立计算)
// A custom computation builder that redirects all operations to
// the standard 'async' builder, but prints "step" in the Bind method
type LogAsyncBuilder() = 
  member x.Bind(c1, f) = async { 
    let! arg = c1
    printfn "step!" 
    return! f arg }
  member x.Return(v) = async.Return(v)
  member x.ReturnFrom(c) = async.ReturnFrom(c)

// An instance of our custom computation builder
let logAsync = LogAsyncBuilder()

// Example that prints 'step' 4 times (for every Bind - let!)
let doSomething n = logAsync {
  return n + 10 }

logAsync {
  let! a = doSomething 0
  let! b = doSomething a
  let! c = doSomething b
  let! d = doSomething c
  return d }
|> Async.RunSynchronously