需要有关将C#翻译为F#的帮助

时间:2017-08-24 10:42:35

标签: f# c#-to-f#

我需要帮助翻译IndexOfAny for string的自定义扩展,因为现有框架没有匹配字符串值的IndexOfAny。已经翻译了我自己的。但是我不知道如何通过返回值突破循环。任何想法如何摆脱循环或更好的解决方案。 以下是我的翻译。

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

2 个答案:

答案 0 :(得分:7)

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

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

如果没有匹配,这将返回None - 这比返回-1更加惯用F#:编译器将强制您考虑没有匹配的情况。

更新:您可能更喜欢:

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

答案 1 :(得分:1)

使用Array.tryFindSeq是惯用的F#,但也具有与C#循环不同的性能特征。特别是Result<int*int, Unit>模块在​​性能和内存开销方面存在问题。这有时很重要。

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

下面是一个关于如何使用尾递归的示例。下面的代码应该与C#循环大致相似。我并没有完全实现C#的语义,而是返回Result。我使用option而不是Result因为// 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" |] 没有添加GC压力,因为它是结构类型。

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

{{1}}