gpt4 book ai didi

f# - 如何在 F# 中编写通用的递归扩展方法?

转载 作者:行者123 更新时间:2023-12-02 22:34:32 27 4
gpt4 key购买 nike

我正在努力将一段定义静态、通用扩展、递归扩展方法的 C# 代码转换为 F#。该特定代码段是 Daniel Smith 的 Stackoverflow 社区 Wiki 文章,地址为 Write an Rx "RetryAfter" extension method 。定义如下

public static IObservable<TSource> RetryAfterDelay<TSource, TException>(
this IObservable<TSource> source,
TimeSpan retryDelay,
int retryCount,
IScheduler scheduler) where TException : Exception
{
return source.Catch<TSource, TException>(ex =>
{
if (retryCount <= 0)
{
return Observable.Throw<TSource>(ex);
}

return
source.DelaySubscription(retryDelay, scheduler)
.RetryAfterDelay<TSource, TException>(
retryDelay, --retryCount, scheduler);
});
}

我无法想出一种定义函数的方法,以便我可以在函数内部调用它。我当前的简化版本就像这样,其中编译器告诉 The field, constructor or member 'retryAfterDelay' is not defined

open System
open FSharp.Reactive
open System.Reactive
open System.Reactive.Concurrency
open System.Reactive.Linq
open System.Reactive.Threading.Tasks
open System.Runtime.CompilerServices

//Note that to declare .NET compatible extensions methods "correctly" in F#, one
//needs to also add the assembly level extension attribute. There's a good summary
//by Lincoln Atkinson at http://latkin.org/blog/2014/04/30/f-extension-methods-in-roslyn/.
[<assembly:Extension>]
do ()

[<Extension>]
type ObservableExtensions =

[<Extension>]
static member inline retryAfterDelay((source: IObservable<_>), (retryDelay: TimeSpan), retryCount, (scheduler: IScheduler)): IObservable<_> =
source.Catch(fun ex -> source.DelaySubscription(retryDelay, scheduler).retryAfterDelay(retryDelay, retryCount - 1, scheduler))

[<EntryPoint>]
let main argv =
0

这应该可行吗?我试图举出这个特殊情况的例子,但到目前为止还是徒劳。

<编辑:现在包含了整个程序。金 block 是 Install-Package Rx-MainInstall-Package FSharp.Reactive ,使用 VS 2013、.NET 4.5.1 和 FSharp.Core 4.3.1.0 在 Debug模式下编译。

<编辑2:有一个关于关键字rec的离题注释。在 Record-type recursive member functions and the “rec” keyword 处的递归成员函数中。简而言之,得出了rec递归成员函数中的绑定(bind)不正确,因此编译器将其标记为错误。

<编辑3:也许实现这一目标的潜在方法如下。我还没有检查这是否真的有效,可能需要一些时间,所以我只是在这里添加一个中间注释...

[<Extension>]
type ObservableExtensions =

[<Extension>]
static member inline retryAfterDelay((source: IObservable<_>), (retryDelay: TimeSpan), retryCount, (scheduler: IScheduler)): IObservable<_> =
ObservableExtensions.retryAfterDelay(source.DelaySubscription(retryDelay, scheduler), retryDelay, retryCount - 1, scheduler)

<编辑4:从其他地方和 Gustavo 的答案中获取线索,并使用 type constraints 来尊重原始代码段。 ,我想出了以下内容

//Note that to declare .NET compatible extensions methods "correctly" in F#, one
//needs to also add the assembly level extension attribute. There's a good summary
//by Lincoln Atkinson at http://latkin.org/blog/2014/04/30/f-extension-methods-in-roslyn/.
[<assembly:Extension>]
do ()

[<Extension>]
type ObservableExtensions =

[<Extension>]
[<CompiledName("PascalCase")>]
static member inline retryAfterDelay<'TSource, 'TException when 'TException :> System.Exception>(source: IObservable<'TSource>, retryDelay: int -> TimeSpan, maxRetries, scheduler: IScheduler): IObservable<'TSource> =
let rec go(source: IObservable<'TSource>, retryDelay: int -> TimeSpan, retries, maxRetries, scheduler: IScheduler): IObservable<'TSource> =
source.Catch<'TSource, 'TException>(fun ex ->
if maxRetries <= 0 then
Observable.Throw<'TSource>(ex)
else
go(source.DelaySubscription(retryDelay(retries), scheduler), retryDelay, retries + 1, maxRetries - 1, scheduler))
go(source, retryDelay, 1, maxRetries, scheduler)

一些注意事项

  1. 我不确定 'TSource通配符 _ 有什么区别吗?与以前版本中使用的一样好。尽管如此,我相信这代表了原始代码。
  2. 我修改了界面以包含一个工厂函数来创建延迟。例如,该函数可以是 let dummyRetryStrategy(retryCount: int) = TimeSpan.FromSeconds(1.0)一个示例用例是 let a = Seq.empty<int>.ToObservable().retryAfterDelay(dummyRetryStrategy, 3, Scheduler.Default) .
  3. 接口(interface)至少可以在调度程序方面进行改进,并且该代码基本上未经测试。嗯,也许这应该链接回社区 wiki 答案。
  4. 该接口(interface)是否可以使用其他 .NET 语言(例如 C# 和 VB.NET)的接口(interface)。我实际上有a post pending on a code与 Code Review SO 中的这一点非常相关,所以也许最好在那里处理(我明天将更新它,大约二十小时左右)。

最佳答案

完成类型声明后,您可以将其用作扩展方法。所以你可以这样写方法:

[<Extension>]
type ObservableExtensions =

[<Extension>]
static member retryAfterDelay(source: IObservable<_>, retryDelay: TimeSpan, retryCount, scheduler: IScheduler): IObservable<_> =
ObservableExtensions.retryAfterDelay(source.Catch(fun ex -> source.DelaySubscription(retryDelay, scheduler)),retryDelay, retryCount - 1, scheduler)

之后您就可以立即使用它。如果您在同一类的另一个扩展中需要它,您可以通过再次重新打开类型声明来使用它:

type ObservableExtensions with
[<Extension>]
static member anotherExtension (x: IObservable<_>) = x.retryAfterDelay // now you can use it as an extension method

另一种方法是在内部函数中使用 letrec:

    [<Extension>]
static member retryAfterDelay(source: IObservable<_>, retryDelay: TimeSpan, retryCount, scheduler: IScheduler): IObservable<_> =
let rec go (source: IObservable<_>, retryDelay: TimeSpan, retryCount, scheduler: IScheduler): IObservable<_> =
go (source.Catch(fun ex -> source.DelaySubscription(retryDelay, scheduler)),retryDelay, retryCount - 1, scheduler)
go (source, retryDelay, retryCount, scheduler)

我更喜欢 F#,因为代码中的递归是明确的。

关于f# - 如何在 F# 中编写通用的递归扩展方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23404185/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com