没有突变的 F# 解决方案
F# solution without mutation
只是为了好玩,我阅读了 these 面试问题并尝试在 C# 和 F# 中找到解决方案,我努力在不改变布尔值或使用正则表达式的情况下在惯用的 F# 中进行以下操作:
给你一个包含一个或多个 $ 符号的字符串,例如:
"foo bar foo $ bar $ foo bar $ "
问题:如何从给定字符串中删除第二次和第三次出现的 $?
我的带有突变的命令式 F# 解决方案:
let input = "foo bar foo $ bar $ foo bar $ "
let sb = new StringBuilder()
let mutable first = true
let f c=
if c='$' && first then first<-false
else sb.Append(c) |> ignore
input |> Seq.iter f
(还有一个 C#):
var input = "foo bar foo $ bar $ foo bar $ ";
var sb = new StringBuilder();
bool first = true;
input.ForEach(c => {
switch (c)
{
case '$' when first: first = false; break;
default: sb.Append(c);break;
};
});
let f (s:string) =
s.Split('$')
|> Array.toList
|> function
| [] -> ""
| [ a ] -> a
| [ a; b ] -> a + "$" + b
| a :: b :: c :: rest -> a + "$" + b + c + (rest |> String.concat "$")
f "foo bar foo $ bar $ foo bar $ "
// "foo bar foo $ bar foo bar "
f "1 $ 2 $ 3 $ 4 $ 5 $"
//"1 $ 2 3 4 $ 5 $"
请注意,此解决方案仅删除 $
的第二个和第三个实例。如果要删除除第一个以外的所有内容,请将 String.concat "$"
替换为 String.concat ""
let f (s:string) =
s.Split('$')
|> Seq.mapi (fun i t -> (if i > 3 || i = 1 then "$" else "") + t)
|> String.concat ""
这是另一个使用 tail 递归和 seq
计算表达式扫描每个 char
的方法:
let f (s:string) =
let rec chars n input = seq {
match Seq.tryHead input with
| Some '$' -> if not(n = 1 || n = 2) then yield '$'
yield! Seq.tail input |> chars (n+1)
| Some c -> yield c
yield! Seq.tail input |> chars n
| None -> ()
}
chars 0 s
|> fun cs -> new string(Seq.toArray cs)
它可能更长但可能比第一个更有效。
编辑: 不,它不是更有效,也不是尾递归,可能是因为它发生在计算表达式中。
只是为了好玩,我阅读了 these 面试问题并尝试在 C# 和 F# 中找到解决方案,我努力在不改变布尔值或使用正则表达式的情况下在惯用的 F# 中进行以下操作:
给你一个包含一个或多个 $ 符号的字符串,例如: "foo bar foo $ bar $ foo bar $ " 问题:如何从给定字符串中删除第二次和第三次出现的 $?
我的带有突变的命令式 F# 解决方案:
let input = "foo bar foo $ bar $ foo bar $ "
let sb = new StringBuilder()
let mutable first = true
let f c=
if c='$' && first then first<-false
else sb.Append(c) |> ignore
input |> Seq.iter f
(还有一个 C#):
var input = "foo bar foo $ bar $ foo bar $ ";
var sb = new StringBuilder();
bool first = true;
input.ForEach(c => {
switch (c)
{
case '$' when first: first = false; break;
default: sb.Append(c);break;
};
});
let f (s:string) =
s.Split('$')
|> Array.toList
|> function
| [] -> ""
| [ a ] -> a
| [ a; b ] -> a + "$" + b
| a :: b :: c :: rest -> a + "$" + b + c + (rest |> String.concat "$")
f "foo bar foo $ bar $ foo bar $ "
// "foo bar foo $ bar foo bar "
f "1 $ 2 $ 3 $ 4 $ 5 $"
//"1 $ 2 3 4 $ 5 $"
请注意,此解决方案仅删除 $
的第二个和第三个实例。如果要删除除第一个以外的所有内容,请将 String.concat "$"
替换为 String.concat ""
let f (s:string) =
s.Split('$')
|> Seq.mapi (fun i t -> (if i > 3 || i = 1 then "$" else "") + t)
|> String.concat ""
这是另一个使用 tail 递归和 seq
计算表达式扫描每个 char
的方法:
let f (s:string) =
let rec chars n input = seq {
match Seq.tryHead input with
| Some '$' -> if not(n = 1 || n = 2) then yield '$'
yield! Seq.tail input |> chars (n+1)
| Some c -> yield c
yield! Seq.tail input |> chars n
| None -> ()
}
chars 0 s
|> fun cs -> new string(Seq.toArray cs)
它可能更长但可能比第一个更有效。
编辑: 不,它不是更有效,也不是尾递归,可能是因为它发生在计算表达式中。