F# 需要将C#翻译成F的帮助吗#
我需要帮助将IndexOfAny的自定义扩展转换为string,因为现有框架没有匹配字符串值的IndexOfAny。已经翻译了我自己的。然而,我不知道如何通过返回值打破循环。任何想法如何打破循环或更好的解决方案。 下面是我的翻译 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
[]
静态成员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" |]