我一直在研究一个关于反身封闭的问题:
关系R的自反闭合是比R更大的最小关系,它是自反的。换句话说,它是R与任何添加的对使R反身。编写一个函数(reflClosure),它接受一对对象列表(代表R)并返回一对对象列表,它们是R的自反闭包。您不必担心对在返回值中出现的顺序。< / p>
我想出了这个解决方案,但看起来很邋and,缺乏整洁。
-- QUESTION 2: Functions and relations
reflClosure :: (Eq a) => [(a,a)] -> [(a,a)]
reflClosure (x:xs) = nub ( (x:xs) ++ [ (x,x) | x <- (heads (x:xs)) ++ (tails
(x:xs)) ])
nub :: Eq a => [a] -> [a]
nub = nubBy (==)
nubBy :: (a -> a -> Bool) -> [a] -> [a]
nubBy eq [] = []
nubBy eq (x:xs) = x : nubBy eq (filter (\y -> not (eq x y)) xs)
heads :: (Eq a) => [(a,a)] -> [a]
heads list = nub [x | (x, _) <- list]
tails :: (Eq a) => [(a,a)] -> [a]
tails list = nub [x | (_,x) <- list]
exists :: (Eq a) => (a,a) -> [(a,a)] -> Bool
exists x xs = length (filter (==x) xs) > 0
-- TEST SET FOR Q2
{-
Your functions should have the following behaviour:
reflClosure [(1,2),(3,2)] = [(1,2),(3,2),(1,1),(2,2),(3,3)]
reflClosure [(1,1),(3,5)] = [(1,1),(3,5),(3,3),(5,5)]
DO NOT WORRY ABOUT THE ORDER IN WHICH PAIRS APPEAR IN YOUR LIST
-}
有更简单的方法吗?从中学习也很有用。
答案 0 :(得分:2)
撰写heads
和tails
的更好方法如下:
heads :: (Eq a) => [(a,a)] -> [a]
heads = nub . map fst
tails :: (Eq a) => [(a,a)] -> [a]
tails = nub . map snd
它没有任何意义,而且它使用了更多&#34;功能性&#34; map
而不是列表理解。
然而,你需要两者意味着更好的方式:
(heads (x:xs), tails (x:xs)) = (\(a,b) -> (nub a) (nub b)) $ unzip (x:xs)
获取fst
和snd
等同于unzip
。
此外,您可以简化exists
的签名:
exists :: (Eq a) => a -> [a] -> Bool
exists x xs = length (filter (==x) xs) > 0
因为没有任何东西取决于输入是对的列表。
Data.List已经定义了nubBy
,所以我不确定你为什么在那里定义它。
不清楚为什么你定义reflClosure
来匹配(x:xs)
,因为你所关心的(显然)是列表非空。也许是这样的:
reflClosure :: (Eq a) => [(a,a)] -> [(a,a)]
reflClosure [] = []
reflClosure xs =
let (as,bs) = unzip xs
in nub $ xs ++ [ (x,x) | x <- (nub as) ++ (nub bs) ]
答案 1 :(得分:2)
关系与对的集合是同构的,而不是对的列表,因此对它们进行建模是有意义的。请注意,下面的所有Ord
约束都在那里,因为Set
的实现需要它。
使用标准库集,因为它们很快。
import Data.Set (Set)
import qualified Data.Set as Set
使代码更易于阅读的类型同义词:
-- A relation with underlying set s
type Relation s = Set (s,s)
现在我们可以编写一个函数来获取基础集的所有成员:
underlyingMembers :: Ord a => Relation a -> Set a
underlyingMembers r = (Set.map fst r) `Set.union` (Set.map snd r)
一旦我们有了这个,找到关系的反身关闭很容易:
reflexiveClosure :: Ord a => Relation a -> Relation a
reflexiveClosure r = r `Set.union` (Set.map (\x -> (x,x)) (underlyingMembers r)
如果你真的需要使用列表,(你真的不应该),你可以fromList
/ toList
:
listVersion :: Ord a => [(a,a)] -> [(a,a)]
listVersion = Set.toList . reflexiveClosure . Set.fromList
如果其中任何一项不清楚,请发表评论,我会详细解释。