需要有关将 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.tryFind
或 Array.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" |]
我需要帮助为字符串翻译 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.tryFind
或 Array.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" |]