vid*_*idi 14 f# functional-programming lazy-sequences
我想编写一个使用谓词过滤序列的函数,但结果也应该包含谓词返回false的第一个项目.
如果F#中有break关键字,那么逻辑将是这样的
let myFilter predicate s =
seq {
for item in s do
yield item
if predicate item then
break
}
Run Code Online (Sandbox Code Playgroud)
我尝试了Seq.takeWhile和Seq.skipWhile的组合,如下所示:
Seq.append
(Seq.takeWhile predicate s)
(Seq.skipWhile predicate s |> Seq.take 1)
Run Code Online (Sandbox Code Playgroud)
...但问题是在takeWhile和skipWhile之间丢失了与谓词匹配的第一个项目
还要注意输入序列是惰性的,因此任何消耗序列并在之后做出决定的解决方案都是不可行的.
有任何想法吗?
谢谢!
编辑:非常感谢所有的答案!我没想到会有如此之快的反应.我很快就会看看他们每个人.现在我只想提供更多背景信息.考虑以下实现shell的编码kata:
let cmdProcessor state = function
| "q" -> "Good bye!"
| "h" -> "Help content"
| c -> sprintf "Bad command: '%s'" c
let processUntilQuit =
Seq.takeWhile (fun cmd -> cmd <> "q")
let processor =
processUntilQuit
>> Seq.scan cmdProcessor "Welcome!"
module io =
let consoleLines = seq { while true do yield System.Console.ReadLine () }
let display : string seq -> unit = Seq.iter <| printfn "%s"
io.consoleLines |> processor|> io.display
printf "Press any key to continue..."
System.Console.ReadKey ()|> ignore
Run Code Online (Sandbox Code Playgroud)
这个实现有一个麻烦,它不打印"再见!" 何时输入命令q.
我想要做的是实现函数processUntilQuit,使其处理所有命令,直到"q",包括 "q".
Tom*_*cek 15
break计算表达式缺乏支持有点烦人.它不适合F#使用的模型(这就是为什么它不受支持),但在这种情况下它会非常有用.
如果你想在序列上只使用一次迭代来实现它,那么我认为最干净的解决方案是使用序列的底层结构并将其作为递归循环使用 IEnumerator<'T>
这相当短(与此处的其他解决方案相比),代码也非常清晰:
let myFilter predicate (s:seq<_>) =
/// Iterates over the enumerator, yielding elements and
/// stops after an element for which the predicate does not hold
let rec loop (en:IEnumerator<_>) = seq {
if en.MoveNext() then
// Always yield the current, stop if predicate does not hold
yield en.Current
if predicate en.Current then
yield! loop en }
// Get enumerator of the sequence and yield all results
// (making sure that the enumerator gets disposed)
seq { use en = s.GetEnumerator()
yield! loop en }
Run Code Online (Sandbox Code Playgroud)
不太明白你的解决方案有什么问题。
两个小修正:
(1) 使用序列表达式以提高可读性。
(2)如果输入序列为空,则使用Seq.truncatenot 。Seq.take
let myFilter predicate s =
seq { yield! Seq.takeWhile predicate s
yield! s |> Seq.skipWhile predicate |> Seq.truncate 1 }
Run Code Online (Sandbox Code Playgroud)