我试图在Haskell中编写一个函数,它接受一个整数列表和一个整数n,并找到所有等于n的元组。到目前为止,我有一个可行的实现
tuplesum :: (Eq b, Num b) => [b] -> b -> [(b, b)]
tuplesum xs n = [(x1,x2) | x1 <- xs, x2 <- xs, x1 + x2 == n, x1 <= x2]
所以如果我给这个函数输入
tuplesum [5,1,4,0,5,6,9] 10
输出是
[(5,5),(5,5),(1,9),(4,6),(5,5),(5,5)]
但是,我有(5,5)解决方案的4个重复。我希望函数输出[(5,5),(1,9),(4,6)]
,但是我无法弄清楚如何约束具有相同整数的元组而不将其完全删除。
答案 0 :(得分:6)
我的印象是,您正在寻找一种从列表中选择两个元素的方法,以便x1
始终位于 x2
之前。
始终让x2
遍历列表的其余部分的常用方法是使用tails :: [a] -> [[a]]
。对于列表,tails
将生成列表的所有尾部列表,从列表本身开始。例如:
Prelude Data.List> tails [1, 4, 2, 5]
[[1,4,2,5],[4,2,5],[2,5],[5],[]]
我们可以使用它与模式匹配来选择一个元素,并获得对剩余元素的引用。例如:
import Data.List(tails)
tuplesum :: (Eq b, Num b) => [b] -> b -> [(b, b)]
tuplesum xs n = [(x1,x2) | (x1:x2s) <- tails xs, x2 <- x2s, x1 + x2 == n]
请注意,此处仍然可以获取重复项,例如,如果5
在列表中出现三次,那么x1
可以选择第一个5
,并且然后x2
可以选择第二个5
以及最后一个import Data.List(nub, tails)
tuplesum :: (Eq b, Num b) => [b] -> b -> [(b, b)]
tuplesum xs n = nub [(x1,x2) | (x1:x2s) <- tails xs, x2 <- x2s, x1 + x2 == n]
。我们可以使用像nub :: Eq a => [a] -> [a]
这样的uniqness过滤器:
tails
请注意,在这里使用HashSet
仍然更好,因为它会提高性能,因为我们首先会生成较少量的重复项。
上述算法仍然是 O(n 2 ),并且速度不是很快。然而,我们可以反过来解决问题:我们可以先构造一个x1
元素,并为每个元素n - x1
,检查import Data.Hashable(Hashable)
import Data.HashSet(fromList, member)
tuplesum :: (Ord b, Hashable b, Num b) => [b] -> b -> [(b, b)]
tuplesum xs n = nub [(x1,x2) | x1 <- xs, let x2 = n-x1, x1 <= x2, member x2 hs]
where hs = fromList xs
是否是成员,如:< / p>
nub
但由于hashNub :: (Eq a, Hashable a) => [a] -> [a]
hashNub = go HashSet.empty
where
go _ [] = []
go s (x:xs) =
if x `HashSet.member` s
then go s xs
else x : go (HashSet.insert x s) xs
,运行时仍然是 O(n 2 ),我们可以在这里使用hashNub :: (Eq a, Hashable a) => [a] -> [a]
:
import Data.Hashable(Hashable) import Data.HashSet(fromList, member) tuplesum :: (Ord b, Hashable b, Num b) => [b] -> b -> [(b, b)] tuplesum xs n = hashNub [(x1,x2) | x1 <- xs, let x2 = n-x1, x1 <= x2, member x2 hs] where hs = fromList xs
然后让它使用:
_registerServiceWorker() {
if ('serviceWorker' in navigator) {
window.addEventListener('load', function () {
navigator.serviceWorker.register("../../content/js/app/service-worker.js", { scope: "/" }).then(function (registration) {
console.log('Service worker registration succeeded:', registration);
}).catch(function (error) {
console.log('Service worker registration failed:', error);
});;
});
}
}
现在它适用于 O(n log n)。
答案 1 :(得分:0)
我真的很喜欢你的函数tuplesum xs n = [(x1,x2) | x1 <- xs, x2 <- xs, x1 + x2 == n, x1 <= x2]
因为它是笛卡尔积,并且它消除了大多数对称的对,否则它们将构成大约一半。它很好地得到了谓词匹配。剩下的唯一问题是重复元素。直到最近我才忘记了这件事。 86. Graham Hutton &#39; &#34; Haskell编程&#34; 和他的rmdups
函数。我喜欢他的rmdups
是因为它既不依赖于进口。
rmdups :: Eq a => [a] -> [a]
rmdups [] = []
rmdups (x:xs) = x : filter (/= x) (rmdups xs)
Hutton的解决方案非常通用且经典递归。我不想在没有添加原始内容的情况下发布他的解决方案,所以这里是一个列表理解,以消除包括元组在内的任何数据类型的重复。
rmdups ls = [d|(z,d)<- zip [0..] ls, notElem d $ take z ls]
您可以将rmdups
功能放在功能rmdups.tuplesum
前面
你的函数消除了大多数对称对,因此rmdups
没有。
rmdups [(5,5),(5,5),(1,9),(4,6),(5,5),(5,5)]
[(5,5),(1,9),(4,6)]
或者
rmdups "abcabcdefdef" OR "abcdefabcdef"
&#34; ABCDEF&#34;