需要有关将 C# 转换为 F# 的帮助

Need Help on Translating C# to F#

我需要帮助为字符串翻译 IndexOfAny 的自定义扩展,因为现有框架没有匹配字符串值的 IndexOfAny。已经自己翻译了。但是我不知道如何通过 return 值跳出循环。知道如何跳出循环或更好的解决方案。 下面是我的翻译。

C#

public static int IndexOfAnyCSharp(this string str, string[] anyOff) {
    if (str != null && anyOff != null)
        foreach (string value in anyOff) {
            int index = str.IndexOf(value);
            if (index > -1) return index;
        }
    return -1;
}

F#

[<Extension>]
static member public IndexOfAnyFSharp(str:string, anyOff:string[]) =
    match str <> null && anyOff <> null with
    | true ->
        let mutable index = -1
        for value in anyOff do
            if index = -1 then
                index <- str.IndexOf(value)
        index
    | false -> -1

Seq.tryFind 是你的朋友。一个基本的构建基块是这样的

let IndexOfAny (s: string, manyStrings: string seq) = 
    manyStrings
    |> Seq.map (fun m -> s.IndexOf m)
    |> Seq.tryFind (fun index -> index >= 0)

如果没有匹配项,这将 return None - 这比 returning -1 更符合 F# 的习惯用法:编译器将强制您考虑没有匹配项的情况。

更新:您可能更喜欢:

let IndexOfAny (s: string, manyStrings: string seq) = 
    manyStrings
    |> Seq.tryPick (fun m ->
        match s.IndexOf m with
        | -1 -> None
        | i -> Some i
    )

使用 Seq.tryFindArray.tryFind 是惯用的 F#,但也具有与 C# 循环不同的性能特征。特别是 Seq 模块在性能和内存开销方面存在问题。这有时很重要。

正如其他人在 F# 中指出的那样,您不能提前退出 for 循环。我曾经为此烦恼,但现在不再烦恼了,因为 F# 支持尾调用消除,允许我们将循环实现为尾递归函数。

下面是一个关于如何使用尾递归的例子。下面的代码应该执行大致类似于 C# 循环。我并没有完全实现 C# 的语义,因为我 return 而是 Result<int*int, Unit>。我使用 Result 而不是 option 因为 Result 不会增加 GC 压力,因为它是结构类型。

还包括 IMO 保护 F# 代码免受空值危害的巧妙方法。

// If our function is callable from C# we can use active patterns as a neat way to protect our
//  F# code from null values
//  Functions that are only callable from F# code we don't need to protect as long as we protect
//  the entry points
let inline (|DefaultTo|) dv v = if System.Object.ReferenceEquals (v, null) then dv else v
let inline (|NotNull|) v      = if System.Object.ReferenceEquals (v, null) then raise (System.NullReferenceException ()) else v

let emptySet : string [] = [||]

let indexOfSet (DefaultTo "" str) (DefaultTo emptySet set) : Result<int*int, unit> =
    // In F# tail recursion is used as a more powerful looping construct
    //  F# suppports tail call elimination meaning under the hood this is
    //  implemented as an efficient loop
    //  Note: I pass str and set as argument in order to make F# doesn't
    //  create new lambda object that closes over them (reduces GC load)
    let rec loop (str : string) (set : string []) i =
        if i < set.Length then
            let index = str.IndexOf set.[i]
            if index = -1 then loop str set (i + 1)
            else Ok (i, index)
        else
          Error ()
    loop str set 0

printfn "%A" <| indexOfSet null null
printfn "%A" <| indexOfSet null     [| "abc"; "ab"; "a" |]
printfn "%A" <| indexOfSet ""       [| "abc"; "ab"; "a" |]
printfn "%A" <| indexOfSet "a"      [| "abc"; "ab"; "a" |]
printfn "%A" <| indexOfSet "ab"     [| "abc"; "ab"; "a" |]
printfn "%A" <| indexOfSet "abc"    [| "abc"; "ab"; "a" |]
printfn "%A" <| indexOfSet "da"     [| "abc"; "ab"; "a" |]
printfn "%A" <| indexOfSet "dab"    [| "abc"; "ab"; "a" |]
printfn "%A" <| indexOfSet "dabc"   [| "abc"; "ab"; "a" |]