这个Haskell组合算法如何改进?

How can this Haskell combination algorithm be improved?

如何改进下面提出的针对以下问题的解决方案?能不能在时间和space上更有效率?有 space 泄漏吗?

问题: 给定 Astronauts 的输入列表,生成 Astronauts 对的列表,使得这些对没有来自同一国家/地区的两个 Astronauts。假设输入有一个 Astronauts 列表,其中唯一的 identifiers.

data Astronaut = Astronaut { identifier :: Int, country :: String } deriving (Eq)

astronautPairs :: [Astronaut] -> [(Astronaut, Astronaut)]
astronautPairs xs = foldl accumulatePairs [] [(a, b) | a <- xs, b <- xs, country a /= country b]
    where
        accumulatePairs pairs pair = if hasPair pair pairs then pairs else pair:pairs
        hasPair pair@(a,b) ((c,d):xs) = a == d && b == c || hasPair pair xs
        hasPair _ [] = False

让我们从实施细节退一步,想想您要实现的目标:

produce a list of pairs of Astronauts such that the pairs do not have two Astronauts from the same country

你似乎被允许假设每个宇航员在列表中只出现一次。

解决此问题的一种有效方法是首先按国家 划分列表。一个自然的方法是构建一个 HashMap String [Int] 来保存来自每个国家的所有宇航员的列表。

import qualified Data.HashMap.Strict as HMS
import Data.HashMap.Strict (HashMap)

divideAstronauts :: [Astronaut] -> HashMap String [Int]
divideAstronauts = foldl' go mempty where
  go hm (Astronaut ident cntry) = HMS.insertWith (++) cntry [ident] hm

现在您可以将程序的其余部分分为两步:

  1. 选择所有国家对。
  2. 对于每一对国家,选择所有宇航员对,使每个宇航员都来自其中一个国家。

为什么不首先避免生成翻转对,而不是消除翻转对。是我们生产的,对吧?

import Data.List (tails)

astronautPairs :: [Astronaut] -> [(Astronaut, Astronaut)]
astronautPairs xs = [ (y,z) | (y:ys) <- tails xs, z <- .... , country y /= country .... ]

这里假设宇航员的输入列表没有重复项。

因此,我们避免了三角生成的重复。

(我留了一部分代码,你自己补)