F# 如何执行Seq.takeWhile+;F中的一项#
我想编写一个函数,它使用谓词过滤序列,但结果还应该包括谓词返回false的第一项 如果F中有break关键字,那么逻辑会是这样的# 我尝试了Seq.takeWhile和Seq.skipWhile的组合,比如:F# 如何执行Seq.takeWhile+;F中的一项#,f#,functional-programming,lazy-sequences,F#,Functional Programming,Lazy Sequences,我想编写一个函数,它使用谓词过滤序列,但结果还应该包括谓词返回false的第一项 如果F中有break关键字,那么逻辑会是这样的# 我尝试了Seq.takeWhile和Seq.skipWhile的组合,比如: Seq.append (Seq.takeWhile predicate s) (Seq.skipWhile predicate s |> Seq.take 1) …但问题是,与谓词匹配的第一项在takeWhile和skipWhile之间丢失 还要注意的是,输入序
Seq.append
(Seq.takeWhile predicate s)
(Seq.skipWhile predicate s |> Seq.take 1)
…但问题是,与谓词匹配的第一项在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
let cmdProcessor state=函数
|“q”->“再见!”
|“h”->“帮助内容”
|c->sprintf“错误命令:“%s”c
让进程退出=
Seq.takeWhile(趣味指令->指令“q”)
让处理器=
processUntilQuit
>>Seq.scan cmdProcessor“欢迎!”
模块io=
让consoleLines=seq{而true则产生System.Console.ReadLine()}
let display:string seq->unit=seq.iter处理器|>io.display
printf“按任意键继续…”
System.Console.ReadKey()|>忽略
此实现存在的问题是,当输入命令q时,不会打印“再见!”
我想做的是实现函数processUntilQuit,这样它可以处理所有命令直到“q”,包括“q”。丑陋的非功能解决方案
let myfilter f s =
let failed = ref false
let newf = fun elem -> match !failed with
|true ->
failed := f elem
true
|false->false
Seq.takeWhile newf s
丑陋的功能解决方案:):
最后是tuple,其中第一个元素包含选项,第一个元素包含源列表中的第一个不匹配元素,第二个元素包含过滤列表
丑陋的功能性懒惰解决方案(对不起,我第一次没有正确阅读您的帖子):
我的F#不够流利,无法让比赛中的终止案例看起来很好,也许一些F#大师会帮上忙
编辑:不那么难看,纯粹的F#解决方案灵感来自托马斯·佩特里切克答案:
let myFilterLazy2 predicate s =
let rec inner ss = seq {
if Seq.isEmpty ss = false then
yield ss |> Seq.head
if ss |> Seq.head |> predicate then
yield! ss |> Seq.skip 1 |> inner
}
inner s
如果您不喜欢此处的计算表达式,请选择duplicateHead
的替代版本:
let duplicateHead' xs =
Seq.append
(Seq.head xs)
xs
这种方法基于构建当前和下一个元素的元组。谓词
正在应用于当前元素,但返回以下元素
注意:当
谓词
在第一个元素上失败时,不安全。为了使其正常工作,您必须重新工作duplicateHead
,方法是添加一个元素,该元素肯定会通过谓词
但您的解决方案并没有真正了解问题所在
两个小的修正:
(1) 使用序列表达式以提高可读性
(2) 如果输入序列为空,则使用Seq.truncate
代替Seq.take
let myFilter predicate s =
seq { yield! Seq.takeWhile predicate s
yield! s |> Seq.skipWhile predicate |> Seq.truncate 1 }
好一点的。:)
这一个需要一个额外的参数n
,它定义了要添加的额外元素的数量
注意:小心使用单行
padWithTrue
(done
关键字)计算表达式中缺少对break
的支持有点烦人。它与F#使用的模型不太吻合(这就是为什么它不受支持的原因),但在这种情况下它将非常有用
如果您只想在序列上使用一次迭代来实现这一点,那么我认为最干净的解决方案是只使用序列的底层结构,并使用
IEnumerator将其作为递归循环来编写,我猜您希望它做些什么,直到:
let takeUntil pred s =
let state = ref true
Seq.takeWhile (fun el ->
let ret= !state
state := not <| pred el
ret
) s
让我们继续=
让state=ref为真
Seq.takeWhile(乐趣el->
让ret=!state
状态:=不这是非常古老的,但我认为我会有所贡献,因为其他解决方案没有提出这一点
使用Seq.scan
来建立谓词结果的两个元素堆栈,并在表示前一个元素的谓词结果的堆栈底部为true
时简单地执行(注意,尚未测试此代码)
我知道这是一个老问题,但有一个更实用的解决方案
尽管,老实说,对于这个问题我更喜欢
在倒数第二行,|>Seq.filter Option.isSome
可以替换为|>Seq.tail
,因为除了初始状态
之外,没有其他状态匹配一些(无,|)另一个迟来的答案,但它是“功能性的”,简单,并且不会读取结果序列中超过最后一个的任何元素
让myFilter谓词=
Seq.collect(乐趣x->[Choice1Of2 x;Choice2Of2(谓词x)])
>>Seq.takeWhile(函数|选择1of2 |->真|选择2of2 p->p)
>>Seq.choose(函数| Choice1Of2 x->Some x | Choice2Of2 |->无)
我知道这个问题提出后的年代,但我不得不处理类似但更一般的问题,我希望有人会发现我的解决方案有用
其思想是在闭包中捕获枚举数,然后返回一个遍历原始序列其余部分的函数
更一般的例子
/// usage example:
let a = seq [1; 2; 3; 4; 5; 6; 7]
use seq = new ContinueSequence<_>(a)
let s1 = seq.Continue(false) |> Seq.takeWhile((>) 3) // take 1 and 2, 3 is current
let s2 = seq.Continue(true) |> Seq.take(2) // take 3 and 4
let s3 = seq.Continue(false) |> Seq.skip(1) // skip 5
let s =
s1
|> Seq.append <| s2
|> Seq.append <| s3
|> Seq.toList
// s = [1; 2; 3; 4; 6; 7]
///用法示例:
设a=seq[1;2;3;4;5;6;7]
使用顺序=新的连续顺序(a)
设s1=seq.Continue(false)|>seq.takeWhile(>)3//取1和2,3是当前值
设s2=seq.Continue(true)|>seq.take(2)//take 3和4
设s3=seq.Continue(false)|>seq.skip(1)//skip 5
让我们=
s1
|>Seq.append Seq.append Seq.toList
//s=[1;2;3;4;6;7]
这个问题的意思是,与谓词匹配的第一项在takeWhile和ski之间丢失了
let duplicateHead' xs =
Seq.append
(Seq.head xs)
xs
let myFilter predicate s =
seq { yield! Seq.takeWhile predicate s
yield! s |> Seq.skipWhile predicate |> Seq.truncate 1 }
let padWithTrue n xs = seq { for _ in 1..n do yield true; done; yield! xs }
let filter predicate n xs =
let ys = xs |> Seq.map predicate |> padWithTrue n
Seq.zip xs ys
|> Seq.takeWhile snd
|> Seq.map fst
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 }
let takeUntil pred s =
let state = ref true
Seq.takeWhile (fun el ->
let ret= !state
state := not <| pred el
ret
) s
Seq.scan (fun (a,b,v) e -> (pred e, a, Some e)) (true, true, None )
>> Seq.takeWhile (fun (_,b,_) -> b)
>> Seq.map (fun (_,_,c) -> c)
let takeWhileAndNext predicate mySequence =
let folder pred state element =
match state with
| Some (_, false) ->
None
| _ ->
Some (Some element, pred element)
let initialState = Some (None, true)
Seq.scan (folder predicate) initialState mySequence |> Seq.takeWhile Option.isSome
|> Seq.map Option.get
|> Seq.map fst
|> Seq.filter Option.isSome
|> Seq.map Option.get
/// allows fetching elements from same sequence
type ContinueSequence<'a> (xs: 'a seq) =
let en = xs.GetEnumerator()
member _.Continue (includeCurrent: bool) =
let s = seq { while en.MoveNext() do yield en.Current }
let c = seq { en.Current }
if includeCurrent then
Seq.append c s
else
s
interface IDisposable with
member _.Dispose() =
en.Dispose()
use seq = new ContinueSequence a
let result = Seq.append
seq.Continue(false) |> Seq.takeWhile(predicate)
seq.Continue(true) |> Seq.take(1) //include element which breaks predicate
/// usage example:
let a = seq [1; 2; 3; 4; 5; 6; 7]
use seq = new ContinueSequence<_>(a)
let s1 = seq.Continue(false) |> Seq.takeWhile((>) 3) // take 1 and 2, 3 is current
let s2 = seq.Continue(true) |> Seq.take(2) // take 3 and 4
let s3 = seq.Continue(false) |> Seq.skip(1) // skip 5
let s =
s1
|> Seq.append <| s2
|> Seq.append <| s3
|> Seq.toList
// s = [1; 2; 3; 4; 6; 7]