F# 需要将C#翻译成F的帮助吗#

F# 需要将C#翻译成F的帮助吗#,f#,c#-to-f#,F#,C# To F#,我需要帮助将IndexOfAny的自定义扩展转换为string,因为现有框架没有匹配字符串值的IndexOfAny。已经翻译了我自己的。然而,我不知道如何通过返回值打破循环。任何想法如何打破循环或更好的解决方案。 下面是我的翻译 C# F# [] 静态成员public indexofanysharp(str:string,anyOff:string[])= 将str null和anyOff null与匹配 |正确-> 设可变索引=-1 对于anyOff do中的值 如果索引=-1,则 索引-1

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

C#

F#

[]
静态成员public indexofanysharp(str:string,anyOff:string[])=
将str null和anyOff null与匹配
|正确->
设可变索引=-1
对于anyOff do中的值
如果索引=-1,则
索引-1

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
    )

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

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

下面是一个关于如何使用尾部递归的示例。下面的代码应该执行与C#循环大致相似的操作。我没有完全实现C的语义,因为我返回了一个
结果
。我在
选项上使用
Result
,因为
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" |]
//如果我们的函数可以从C中调用,我们可以使用活动模式作为保护我们的
//F#空值代码
//只可从F#代码调用的函数,只要我们保护,就不需要保护
//入口点
让内联(| DefaultTo |)dv v=如果System.Object.ReferenceEquals(v,null),则dv else v
让内联(| NotNull |)v=if System.Object.ReferenceEquals(v,null),然后引发(System.NullReferenceException())else v
让清空设置:字符串[]=[| |]
let indexOfSet(DefaultTo“”str)(DefaultTo emptySet set):结果=
//在F#中,尾部递归被用作更强大的循环构造
//F#支持尾部呼叫消除,这是引擎盖下的意思
//作为一个有效的循环实现
//注意:我传递str并设置为参数,以使F#不
//创建新的lambda对象以关闭它们(减少GC负载)
让rec循环(str:string)(set:string[])i=
如果我printfn“%A”另一个选项-使用
Array.tryPick
True,但我认为最终的代码行数总是会比这两阶段方法多…不一定:
|>Seq.tryPick(fun m->match m.IndexOf m with-1->None | I->Some I)
是的,好的,最后这是一个品味问题。我个人不会把它放在一行中。嗨,安东!谢谢你的建议。事实上,我知道在F#中,返回-1不是惯用语。但实际上,我正在一点一点地将现有的C代码改为F代码,并且带有接口的项目仍然使用C作为前端。因此,威尔最终返回了一个选项。我从你的代码中已经理解了如何将“None”转换成-1。你不能在F#中突破for循环。关于这个有一些讨论。嗨!谢谢你的回复。我会尝试一下,因为你提到的可能是对的,因为我还不熟悉F#。在我自己当前的实际函数中,我确实使用了“match”来匹配字符串和数组是否都已传递,如果有则直接返回-1为空。不管怎样,我仍然需要将代码转换为'type'和'member',在C#中可以检测为静态扩展方法。在某种程度上,我可以用它作为“hello world再见”。IndexOfAny(“world”,“yahoo”)
我意识到这一点,这篇文章更多地是为了演示使用尾部递归退出“循环”。
let IndexOfAny (s: string, manyStrings: string seq) = 
    manyStrings
    |> Seq.tryPick (fun m ->
        match s.IndexOf m with
        | -1 -> None
        | i -> Some i
    )
// 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" |]