这可能是微不足道的,我确实有一个解决方案,但我对此并不满意。不知何故,(更多)更简单的表单似乎不起作用,并且它在拐角情况下变得混乱(连续的第一个或最后一个匹配对)。
为了简单起见,我们将匹配规则定义为任意两个或多个差异为2的数字。例如:
> filterTwins [1; 2; 4; 6; 8; 10; 15; 17]
val it : int list = [2; 4; 6; 8; 10; 15; 17]
我目前使用的代码是这个,只是感觉草率和超重:
let filterTwins list =
let func item acc =
let prevItem, resultList = acc
match prevItem, resultList with
| 0, []
-> item, []
| var, [] when var - 2 = item
-> item, item::var::resultList
| var, hd::tl when var - 2 = item && hd <> var
-> item, item::var::resultList
| var, _ when var - 2 = item
-> item, item::resultList
| _
-> item, resultList
List.foldBack func list (0, [])
|> snd
我打算用我自己的原创练习来试验List.foldBack
,大型列表和并行编程(进展顺利)但最终弄乱了“简单”部分......
有更多答案,但我相信以上是最明显的答案。希望我接受丹尼尔的答案作为解决方案并没有伤害任何人的感受:每一个解决方案都应该是选定的答案(!)。
*使用函数名称作为一个字符进行计数
答案 0 :(得分:2)
这会做你想要的吗?
let filterTwins l =
let rec filter l acc flag =
match l with
| [] -> List.rev acc
| a :: b :: rest when b - 2 = a ->
filter (b::rest) (if flag then b::acc else b::a::acc) true
| _ :: t -> filter t acc false
filter l [] false
这非常低效,但这是使用更多内置函数的另一种方法:
let filterTwinsSimple l =
l
|> Seq.pairwise
|> Seq.filter (fun (a, b) -> b - 2 = a)
|> Seq.collect (fun (a, b) -> [a; b])
|> Seq.distinct
|> Seq.toList
也许稍好一点:
let filterTwinsSimple l =
seq {
for (a, b) in Seq.pairwise l do
if b - 2 = a then
yield a
yield b
}
|> Seq.distinct
|> Seq.toList
答案 1 :(得分:1)
这个怎么样?
let filterPairs f =
let rec filter keepHead = function
| x::(y::_ as xs) when f x y -> x::(filter true xs)
| x::xs ->
let rest = filter false xs
if keepHead then x::rest else rest
| _ -> []
filter false
let test = filterPairs (fun x y -> y - x = 2) [1; 2; 4; 6; 8; 10; 15; 17]
或者如果您的所有列表项都是唯一的,您可以这样做:
let rec filterPairs f s =
s
|> Seq.windowed 2
|> Seq.filter (fun [|a;b|] -> f a b)
|> Seq.concat
|> Seq.distinct
let test = filterPairs (fun x y -> y - x = 2) [1; 2; 4; 6; 8; 10; 15; 17]
修改强>
或者这是我觉得优雅的另一种选择。首先定义一个函数,用于将列表分解为满足谓词的连续项的组列表:
let rec groupConsec f = function
| [] -> []
| x::(y::_ as xs) when f x y ->
let (gp::gps) = groupConsec f xs
(x::gp)::gps
| x::xs -> [x]::(groupConsec f xs)
然后,通过将所有结果重新收集起来,丢弃任何单身人士来构建你的功能:
let filterPairs f =
groupConsec f
>> List.collect (function | [_] -> [] | l -> l)
let test = filterPairs (fun x y -> y - x = 2) [1; 2; 4; 6; 8; 10; 15; 17]
答案 2 :(得分:1)
以下解决方案符合您自己的精神,但我使用一个区别联盟来封装算法的各个方面并稍微控制一下疯狂:
type status =
| Keep of int
| Skip of int
| Tail
let filterTwins xl =
(Tail, [])
|> List.foldBack
(fun cur (prev, acc) ->
match prev with
| Skip(prev) when prev - cur = 2 -> (Keep(cur), cur::prev::acc)
| Keep(prev) when prev - cur = 2 -> (Keep(cur), cur::acc)
| _ -> (Skip(cur), acc))
xl
|> snd
答案 3 :(得分:1)
这是另一个解决方案,它使用类似的判别联合策略作为我的另一个答案,但它可以懒惰地处理序列,所以你可以看到那些双胞胎(素数?)在它们出现时滚动:
type status =
| KeepTwo of int * int
| KeepOne of int
| SkipOne of int
| Head
let filterTwins xl =
let xl' =
Seq.scan
(fun prev cur ->
match prev with
| KeepTwo(_,prev) | KeepOne prev when cur - prev = 2 ->
KeepOne cur
| SkipOne prev when cur - prev = 2 ->
KeepTwo(prev,cur)
| _ ->
SkipOne cur)
Head
xl
seq {
for x in xl' do
match x with
| KeepTwo(a,b) -> yield a; yield b
| KeepOne b -> yield b
| _ -> ()
}
答案 4 :(得分:0)
为了完整起见,我将基于此主题中的友好建议,用我最终提出的内容回答这个问题。
这种方法的好处是它不需要Seq.distinct
,我认为这是一种改进,因为它允许重复。但是,它仍然需要List.rev
,这并不是最快的。它也不是最简洁的代码(参见问题解决方案的比较)。
let filterTwins l =
l
|> Seq.pairwise
|> Seq.fold (fun a (x, y) ->
if y - x = 2 then (if List.head a = x then y::a else y::x::a)
else a) [0]
|> List.rev
|> List.tail