.net &引用;“链接”;F中的异步函数#

.net &引用;“链接”;F中的异步函数#,.net,asynchronous,f#,.net,Asynchronous,F#,我在F#中创建了一个函数,用于从Yahoo恢复历史数据(F#的经典异步示例): 现在,我可以通过执行以下操作异步运行此函数: let test= ["MSFT";"YHOO"] |>List.map (fun x -> getCSV x (DateTime.Parse("01.01.2000")) (DateTime.Parse("01.01.2010"))) |> Async.Parallel |> Async.RunSynchronou

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

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

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

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

例如:

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
函数

我想知道的是,是否有可能在下载仍在进行时开始执行第二个函数:一旦MSFT完成,就不需要等到YHOO完成后再计算它的返回


我知道我可以修改
getCSV
,但我想知道是否有一种方法可以“链接”
getReturn
函数,而不必更改以前编写的模块…

如果您像这样定义
getReturns
函数

let getReturns (prices:Async<(DateTime * float) list>) = async {
    let! prices = prices
    return [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)))))
}
您可以通过更改
getCSV
进一步清理它,使
ticker
成为最后一个参数,而不是第一个参数。这允许您部分应用日期参数以生成只需要ticker执行的函数。然后可以使用
getReturns
链接该函数

let test =
    let getRange = getCSV (DateTime(2000, 1, 1)) (DateTime(2010, 1, 1))
    ["MSFT"; "YHOO"]
    |> List.map (getRange >> getReturns)
    |> Async.Parallel
    |> Async.RunSynchronously
编辑

在你的
getReturns
函数中,所有那些
List.nth
调用都让我发痒。我宁愿自己使用模式匹配。我想你可以这样写函数:

let getReturns2 (prices: Async<(DateTime * float) list>) = async {
    let! prices = prices
    let rec loop items output =
        match items with
        | (_, last) :: (time, current) :: rest ->
            loop rest ((time, (last / current)) :: output)
        | [ item ] ->
            List.rev (item :: output)
        | [] ->
            List.rev output
    return loop prices []
}
让getReturns2(价格:Async)=Async{
让我们看看!价格=价格
让rec循环项目输出=
将项目与
|(上次):(时间,当前)::休息->
循环休息((时间,(上次/当前))::输出)
|[项目]->
List.rev(项目::输出)
| [] ->
List.rev输出
返回循环价格[]
}

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

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

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

使用原始的
getReturns
函数,您可以编写:

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 }

好的,基本上如果我没有访问
getReturns
函数的权限,我只需要创建一个包装函数来接受异步输入…@SRKX-如果你不能修改
getReturns
,那么你可能可以很容易地使用
Async.map
(从我的答案中)。本质上,它为您提供的任何函数构建了一个包装器(就像您手工编写的那样)。@Joel:哈哈,我喜欢您的返回实现。我的只是一个例子;)正是我想要的!!如果我理解,我也可以同步编写
getCSV
并使用与异步处理完全相同的代码,对吗?
req.AsyncGetResponse()
需要在异步工作流中,因此,如果您将WebRequest内容移出该函数,则只能使
getCSV
非异步。我认为这是因为async.bind太相似了。下面是示例。如果你想要这个功能,就去投票吧。相关/类似问题:
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 
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
module Async =
  let map f workflow = async {
    let! res = workflow
    return f res }