“链式"F#中的异步函数 [英] "Chaining" asynchronous functions in F#

查看:19
本文介绍了“链式"F#中的异步函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 F# 中创建了一个函数来从 Yahoo(F# 的经典异步示例)恢复历史数据:

I have created a function in F# to recover historical data from Yahoo (the classic asynchronous example for F#):

let getCSV ticker dStart dEnd =
async   {
        let query = getFileUrl ticker dStart dEnd
        let req = WebRequest.Create(query)
        use! resp = req.AsyncGetResponse()
        use stream= resp.GetResponseStream()
        use reader = new StreamReader(stream)
        let content = reader.ReadToEnd()
        let ts = parseData content
        return ts
        }

现在,我可以通过执行以下操作异步运行此函数:

Now, I can run this function asynchronously by doing the following:

let test=
    ["MSFT";"YHOO"]
    |>List.map (fun x -> getCSV x (DateTime.Parse("01.01.2000")) (DateTime.Parse("01.01.2010")))
    |> Async.Parallel
    |> Async.RunSynchronously

好吧,这很酷.

现在,我想知道的是如何对价格历史应用一些函数:

Now, what I would like to know is how to apply some function to this which is the history of prices:

例如:

let getReturns (prices:(DateTime *float)list) =
    [for i in 1..(prices.Length-1) -> i]
    |> List.map (fun i ->(fst (List.nth prices i), (snd (List.nth prices i))/(snd (List.nth prices (i-1) )) - 1.0))

所以简单的做法是:

let test2=
    ["MSFT";"YHOO"]
    |>List.map (fun x -> getCSV x (DateTime.Parse("01.01.2000")) (DateTime.Parse("01.01.2010")))
    |> Async.Parallel
    |> Async.RunSynchronously
    |> Array.map getReturns;;

但是,getReturns 函数会在每个文件下载和解析后执行.

However, the getReturns function is executed once every file is downloaded and parsed.

我想知道的是,是否可以在下载仍在进行时开始执行第二个函数:一旦 MSFT 完成,无需等到 YHOO 完成计算其回报...

What I would like to know, is if it is possible to start execution the second function while the downloads are still taking place: once MSFT is done, no need to wait until YHOO is done to compute its return...

我知道我可以修改 getCSV 但我想知道是否有一种方法可以链接"getReturn 函数而无需更改以前编写的模块...

I know that I could modify getCSV but I would like to know if there is a way to "chain" the getReturn function without having to change a previously written module...

推荐答案

我通常会直接在异步工作流中编写对函数的调用.这主要是风格或偏好的问题 - 我认为使用异步工作流编写的代码通常更明确,并且不会经常使用高阶函数(尽管它们有时仍然有用):

I would typically write the call to the function directly inside an asynchronous workflow. This is mostly a matter of style or preference - I think that code written using asynchronous workflows is generally more explicit and doesn't use higher-order functions as often (though they're still sometimes useful):

let test=
    [ for stock in ["MSFT";"YHOO"] ->
        async { let! data = getCSV stock (DateTime(2000, 1, 1)) (DateTime(2010, 1, 1))
                return getReturns data } ]
    |> Async.Parallel
    |> Async.RunSynchronously 

这意味着并行执行的工作流首先获取数据,然后调用getRteurns 提取数据.然后将整个操作并行化.

This means that the workflows executed in parallel first get the data and then call getRteurns to extract the data. The entire operation is then parallelized.

或者,您可以使用 Joel 的解决方案(修改 getReturns 函数,使其采用异步工作流并返回异步工作流)或定义函数 Async.map它采用异步工作流并构造一个新的工作流,将某些函数应用于结果.

Alternatively, you could either use Joel's solution (modify the getReturns function so that it takes an asynchronous workflow and returns an asynchronous workflow) or define a function Async.map that takes an asynchronous workflow and constructs a new one that applies some function to the result.

使用你原来的 getReturns 函数,你可以这样写:

Using your original getReturns function, you can then write:

let test=
    ["MSFT";"YHOO"]
    // For every stock name, generate an asynchronous workflow
    |> List.map (fun x -> getCSV x (DateTime(2000, 1, 1)) (DateTime(2010, 1, 1)))
    // For every workflow, transform it into a workflow that 
    // applies 'getReturns' to the result of the original workflow
    |> List.map (Async.map getReturns)
    // Run them all in parallel
    |> Async.Parallel
    |> Async.RunSynchronously

Async.map 的定义很简单:

module Async =
  let map f workflow = async {
    let! res = workflow
    return f res }

这篇关于“链式"F#中的异步函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆