"链接"在F#异步功能 [英] "Chaining" asynchronous functions in F#

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

问题描述

我已经创建了F#的函数从雅虎(经典的异步例子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

确定这很酷。

Ok that's cool.

现在,我想知道的是如何应用的一些功能,这是价格的历史:

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))

所以做的平凡的方式是:

So the trivial way of doing it is:

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 函数执行一次。

我想知道的是,如果有可能开始执行第二功能,而下载仍在进行: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 ,而无需改变previously写入模块的功能...

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...

推荐答案

我通常直接写内部的异步工作调用的函数。这主要是款式还是preference的问题 - 我认为,code编写使用异步工作流一般比较明确,并且不使用高阶函数经常(虽然他们有时仍然会很有用):

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.

另外,你既可以使用乔尔的解决方案(修改 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天全站免登陆