我正在尝试将以下命令式代码转换为Haskell中的功能性解决方案。我想比较集s
的成员与集s'
的成员,并根据比较更新集t
和t'
。这是命令式伪代码:
-- s, s', t, t' are of type Data.Set a
-- foo x y returns a Just a or Nothing
foo :: a -> a -> Just a
-- Initialize t and t' to s and s'
t = s
t = s'
foreach x in s
foreach y in s'
if (foo x y) == Just z
insert z into t
delete x from t
delete y from t'
return (t, t')
我希望的Haskell函数的类型可能是,
mergeSets :: S.Set -> S.Set -> (S.Set, S.Set)
mergeSets s s' = ...
其中S
的类型为Data.Set
,函数的结果将与新集合t
和t'
成对(或以其他方式返回两个集合t
和t'
)。
答案 0 :(得分:4)
这是一种可能性:
bar s s' =
foldl (\ (t,t') (z,(x,y)) ->
( delete x (insert z t) , delete y t' ))
(s,s')
[(z,(x,y)) | x <- toList s, y <- toList s', Just z <- [foo x y]]
这里的主要问题是您是否打算进行插入和删除以干扰foreach
机制。以上假设你没有。
如果你的套装很大,你可能需要增加严格性,以避免暴击:
bar s s' = foldl (\ (t,t') (z,(x,y)) ->
let a=insert z t; b=delete x a; c=delete y t'
in a `seq` b `seq` c `seq` (b,c) )
....
答案 1 :(得分:2)
如果您正在使用像Set
这样的集合数据类型,那么通常不会在元素上编写循环。这对列表更合适。因此,如果您的算法需要以某种嵌套方式枚举所有元素,请将Set转换为a list.
因此,我会尽量避免在集合上使用嵌套循环算法,而是在集合操作方面寻找一些声明性规范:交集,并集,差异等。
如果那是不可能的,那么列表的天真翻译肯定是可能的:
import qualified Data.Set as S
mergeSets :: Ord a => S.Set a -> S.Set a -> (S.Set a, S.Set a)
mergeSets s t = go1 (S.toList s) s t
where
go1 [] t t' = (t,t')
go1 (x:xs) t t' = go1 xs u u'
where
(u, u') = go2 x (S.toList t) t t'
go2 x [] t t' = (t,t')
go2 x (y:ys) t t'
| Just z <- foo x y = go2 x ys (S.delete x (S.insert z t)) (S.delete y t')
| otherwise = go2 x ys t t'
-- for example
foo x y = if x == y then Just x else Nothing
简单的一点就是对嵌套循环进行建模。我们可以使用例如列表理解。但是,您的算法使用输出集的变异,因此我们需要将其作为累积参数传递。
为了真正很好地融入Haskell,我们需要放弃逐个元素的命令式风格,并按照自然的Set api工作。这将是您通往一个不错的解决方案的途径。