3

私の理解では、ワークフロービルダーが行うことは、最初に式を「構築」し、次にそれを実行することです。let!最初に式を作成することを考えると、実際に実行する前にステートメントの数を数えることができるはずですよね? そして、進行状況を監視するログを挿入できるはずですか? ビルダーを作り直して、async進行状況を自動的に報告し、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
 }

ループの場合は、ループ全体が 1 つのステップであると仮定します。ここでは、最上位の式のみがステップとしてカウントされます。

(まったく新しいワークフロービルダーを作成せずにこれを行う方法がある場合は、それも問題ないと思います)。

私はすでに、a) タスクを反復するだけの「タスク」イテレータを作成する (ただし、処理などを失うuseため、不適切になってしまう)、および b) タスク カウンターを作成するというパスを既に通過したことに注意してください。手動でシードして反復するので、もっと良いものを期待しています。

4

2 に答える 2

2

タグ monads で質問にタグを付けたので理論的なニッチピックから始めます。あなたがやりたいことは、実際にはモナドではありません。問題は、モナドには特定の法則が必要なことです (モナドに関する Haskell ページを参照してください)。F# の場合、これは、次の 2 つのスニペットが同じことを意味する必要があることを意味します。

let computation1 = 
  async { let! x = m
          return x }
let computation2 = m

これは、あなたが提案する拡張機能には当てはまりcomputation1ませlet!computation2。現在、これは実際には問題ではないと思います。ログはまだ有用です (場合によっては、予想とは異なる結果が得られる場合があります)。

この機能を F# async に追加するのはそれほど簡単ではありません。問題は、 standard を置き換える (またはラップする) 独自の型を定義する必要があることですAsync<'T>。型には歩数を格納する必要があります。ステップ数を別の場所 (可変カウンターなど) に保存できる場合は、 の計算ビルダーを再定義するだけで済みますasync

これは、このようなことを行う最小限の例です-"step"それぞれに対して出力するだけです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
于 2013-08-17T23:01:47.243 に答える
1

タプル('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
}
于 2013-08-17T18:50:09.487 に答える