Algorithm 删除功能样式中的元素

Algorithm 删除功能样式中的元素,algorithm,f#,Algorithm,F#,我一直在努力解决一些看起来像是简单算法的问题,但到目前为止,还没有找到一种干净的方式来用函数式表达它。下面是问题的概要:假设我有两个数组X和Y X = [| 1; 2; 2; 3; 3 |] Y = [| 5; 4; 4; 3; 2; 2 |] 我想要的是检索匹配的元素和不匹配的元素,如: matched = [| 2; 2; 3 |] unmatched = [| 1; 3 |], [| 4; 4; 5 |] 在伪代码中,我是这样看待这个问题的: let rec match matches

我一直在努力解决一些看起来像是简单算法的问题,但到目前为止,还没有找到一种干净的方式来用函数式表达它。下面是问题的概要:假设我有两个数组X和Y

X = [| 1; 2; 2; 3; 3 |]
Y = [| 5; 4; 4; 3; 2; 2 |]
我想要的是检索匹配的元素和不匹配的元素,如:

matched = [| 2; 2; 3 |]
unmatched = [| 1; 3 |], [| 4; 4; 5 |]
在伪代码中,我是这样看待这个问题的:

let rec match matches x y =
    let m = find first match from x in y
    if no match, (matches, x, y)
    else
        let x' = remove m from x
        let y' = remove m from y
        let matches' = add m to matches
        match matches' x' y'

我遇到的问题是
“从x中删除m”
部分-我找不到一个干净的方法来做到这一点(我有可用的代码,但它非常难看)。有没有一种很好的、惯用的函数式方法来解决这个问题,或者是删除部分,或者是编写算法本身的另一种方法?

您似乎在描述算法及其操作

如果使用适当的数据结构,操作非常容易实现:

// Assume that X, Y are initialized bags
let matches = X.IntersectWith(Y)
let x = X.Difference(Y)
let y = Y.Difference(X)
在.NET framework中没有内置的包集合。您可以使用Power Collection库,包括获取上述函数签名的位置

更新:

您可以通过弱升序列表来表示行李。以下是@kqr答案的改进版本,采用F#语法:


使用正确的数据结构可以很容易地解决这个问题,但如果您想手动完成,我将在Haskell中这样做。我对F#的了解不足以翻译这个,但我希望它足够相似。(半)识字的哈斯克尔

重叠xs-ys= 我首先对这两个序列进行排序,以避免必须知道以前的值的问题

go(排序xs)(排序ys)
哪里
递归的两个基本情况很容易处理——如果其中一个列表为空,则结果包括元素列表中不重叠的另一个列表

go-xs[]=([],(xs,[]))
go[]ys=([],([],ys))
然后我检查每个列表中的第一个元素。如果它们匹配,我可以确保列表在该元素上重叠,因此我将其添加到包含的元素中,并允许排除的元素。我通过在列表的尾部递归来继续搜索列表的其余部分

go(x:xs)(y:ys)
|x==y=let(包括、排除)=go xs ys
in(x:包括,不包括)
接下来是有趣的部分!我主要想知道的是,其中一个列表的第一个元素是否不存在于第二个列表中——在这种情况下,我应该将其添加到排除的列表中,然后继续搜索

|x
事实上就是这样。至少你举的例子是这样的

>让(匹配,不匹配)=重叠x y
>匹配
[2,2,3]
>无与伦比的
([1,3],[4,4,5])

这很有帮助。我最初研究的是集合,它实现了我所关心的操作,只是它们不处理重复项。我怀疑我丢失了正确的数据结构-很高兴知道它有一个名称!当然,现在我的下一个问题是看一个包是如何实现的……看起来你解决了OPs难题,总是移除第一个元素(而不是搜索一个元素放入
匹配的
,你总是选择第一个元素,然后决定它应该去哪里)。我认为如果没有排序,您的算法实际上是有效的,因为如果第一个元素不匹配,您总是查看列表的其余部分。我想说,如果你先排序,速度会更快,因为你可以将
notElem
更改为更有效地利用排序。@roliu没有排序的困难是,在递归的情况下,你会得到第四种情况;如果列表中的
x
y
进一步存在,则需要发生一些事情。我不知道有什么明智的方法来解决这个问题——我能找到的最简单的方法就是将这些元素移到列表的后面,然后将它们向后传播,直到您能够处理它们为止。但是代码变得有点难看。既然你已经对列表进行了排序,我想你可以用
x
@kqr-Ah之类的东西来代替
notElem
,你说得对。最终,如果存在这样的解决方案,我认为排序实际上会更快,因为svick提到了优化(我称之为“改变”
notElem
,以便更有效地利用排序”)。但如何做到这一点是一个有趣的问题。。。我想我应该有一个名为
match
的函数,它返回了删除了匹配项的列表?因此,您需要递归地搜索列表并传递“已处理”部分和“未处理”部分。不太难看,但至少和排序一样难看,排序更有效…@svick绝对!编辑。谢谢
let overlap xs ys =
    let rec loop (matches, ins, outs) xs ys =
        match xs, ys with
        // found a match
        | x::xs', y::ys' when x = y -> loop (x::matches, ins, outs) xs' ys'
        // `x` is smaller than every element in `ys`, put `x` into `ins`
        | x::xs', y::ys' when x < y -> loop (matches, x::ins, outs) xs' ys
        // `y` is smaller than every element in `xs`, put `y` into `outs`
        | x::xs', y::ys' -> loop (matches, ins, y::outs) xs ys'
        // copy remaining elements in `xs` to `ins`
        | x::xs', [] -> loop (matches, x::ins, outs) xs' ys
        // copy remaining elements in `ys` to `outs`
        | [], y::ys' -> loop (matches, ins, y::outs) xs ys'
        | [], [] -> (List.rev matches, List.rev ins, List.rev outs)
    loop ([], [], []) (List.sort xs) (List.sort ys)
type Bag<'T> = Bag of 'T list

module Bag =
    val count : 'T -> Bag<'T> -> int
    val insert : 'T -> Bag<'T> -> Bag<'T>
    val intersect : Bag<'T> -> Bag<'T> -> Bag<'T>
    val union : Bag<'T> -> Bag<'T> -> Bag<'T>
    val difference : Bag<'T> -> Bag<'T> -> Bag<'T>