我仍然是Haskell的新手并正在尝试一些事情。我有一张满是毕达哥拉斯三重奏的清单。现在我想删除所有相同的三元组。例如(6,8,10)与(3,4,5)的方式相同。所以我编写了一个函数来检查两个3元组是否相同,如果相同则返回True
,如果不是False
。但现在我有一个3元组的列表(其中有相同的三元组),我想过滤掉所有相同的(3元组)。我一直在StackOverflow上搜索相同的问题,但遗憾的是找不到任何我可以使用的东西。
我目前的代码如下。 (不要介意效率,因为我确信目前我的效率非常低。)
生成三元组列表:
pyth :: Int -> [(Int, Int, Int)]
pyth i = [(a, b, c) | a <- [1..i], b <- [1..a], c <- [1..i], (a^2) + (b^2) == (c^2)]
一些帮助处理3元组的函数:
first :: (Int, Int, Int) -> Int
first (a,_,_) = a
second :: (Int, Int, Int) -> Int
second (_,a,_) = a
third :: (Int, Int, Int) -> Int
third (_,_,a) = a
检查两个三元组是否相同的功能:
doubleCheck :: (Int, Int, Int) -> (Int, Int, Int) -> Bool
doubleCheck a b
| (((first b) `div` (first a)) == ((second b) `div` (second a))) &&
((first b) `mod` (first a) == 0) &&
((second b) `mod` (second a) == 0) = True
| otherwise = False
现在,通过我听到和读到的有关Haskell和高阶函数的强大功能,我认为解决方案就像两行代码或其他东西。但是我坚持了这一天,并且无法弄清楚如何解决这个问题。
提前致谢!
答案 0 :(得分:3)
我可以建议一些简化。
首先,布尔值是普通值。他们不必被降级为if
警卫或| ...
警卫。特别是,
f x | condition = True
| otherwise = False
可以简化为
f x = condition
在您的具体案例中:
doubleCheck :: (Int, Int, Int) -> (Int, Int, Int) -> Bool
doubleCheck a b =
(((first b) `div` (first a)) == ((second b) `div` (second a)))
&& ((first b) `mod` (first a) == 0)
&& ((second b) `mod` (second a) == 0)
其次,让我们删除一些冗余的括号,那里有太多的噪音。
doubleCheck :: (Int, Int, Int) -> (Int, Int, Int) -> Bool
doubleCheck a b =
(first b `div` first a) == (second b `div` second a)
&& (first b `mod` first) == 0
&& (second b `mod` second a) == 0
实际上,我们可以删除最后三行中的所有括号,但是让我们留下其中的一些。
第三,你的助手功能根本没用。忘掉这些访问器,并在需要时利用模式匹配。
doubleCheck :: (Int, Int, Int) -> (Int, Int, Int) -> Bool
doubleCheck (a1,a2,a3) (b1,b2,b3) =
(b1 `div` a1) == (b2 `div` a2)
&& (b1 `mod` a1) == 0
&& (b2 `mod` a2) == 0
顺便说一句,上面现在看起来不对,因为你根本没有处理a3,b3
。我想你应该解决这个问题吗?
无论如何,要过滤列表并仅保留每个三元组的第一个副本,我们可以使用Data.List.nubBy
nubBy doubleCheck myListOfTriples
这具有O(n ^ 2)复杂度,这不是很好,但应该这样做。目前,我无法找到针对此特定问题的更好的复杂性解决方案。
也许,人们可以通过三重格的gcd潜水每个组件来减少每个三重奏。在这个规范化过程之后,我们的等同性是明显的同一性。因此,我们可以在O(n log n)中排序,然后删除O(n)中的重复项。