我一直在Haskell中编写代码,但无法理解实现union函数的想法。 我还发现了一些嵌入在Haskell平台中的函数定义。但问题是我需要一种简洁易懂的方法来使其发挥作用。 任何人都可以帮助我吗?
答案 0 :(得分:8)
假设您正在讨论union :: Eq a => [a] -> [a] -> [a]
,它接受两个输入列表并返回包含每个参数列表的所有元素的第三个列表,那么它在Data.List
中定义,它位于{{ 1}}包。
在源代码中,它分为两个函数,广义函数base
,它采用等式的自定义定义(类型的函数等于unionBy
),然后定义使用{(==) :: a -> a -> Bool
的函数。 1}}类型类通过传入Eq
作为平等的具体实现。
(==)
我们可以将union :: (Eq a) => [a] -> [a] -> [a]
union = unionBy (==)
替换为(==)
代码,因为Haskell允许我们使用等式推理。
unionBy
同样的模式在union = unionBy (==)
-- so...
union :: Eq a => [a] -> [a] -> [a]
union xs ys = xs ++ foldl (flip (deleteBy (==))) (nubBy (==) ys) xs
和unionBy
中deleteBy
的定义中再出现两次,两者都遵循相同的约定。 nubBy
从列表中删除元素,delete
返回唯一元素列表。我们将再次简化定义以消除nub
的所有痕迹,并简单地假设元素(==)
已定义a
。
Eq
现在这个定义可能更具可读性。 union xs ys = xs ++ foldl (flip delete) (nub ys) xs
和union
的{{1}} xs
附加到ys
的唯一(“xs
床”)值已被处理nub
。 ys
的最终结果是逐个尝试foldl (flip delete) _ xs
来自foldl
delete
的每个元素xs
。结果意味着(nub ys)
union xs ys
附加到xs
的每个唯一元素,而不是ys
中的xs
。
顺便说一句,有了这个来源,我们可以注意到union
的一些古怪行为,例如它如何处理第一个参数中的重复项与第二个参数不同
union [1,1,2] [2] == [1,1,2]
union [2] [1,1,2] == [2,1]
这有点令人失望,这是使用[]
来表示Set
的结果 - 就像union
的概念一样。但是,如果我们使用Set.fromList
查看结果,那么我们就可以了。
xs, ys :: Eq a => [a]
Set.fromList (xs `union` ys) == Set.fromList xs `Set.union` Set.fromList ys
这也为我们提供了union
union xs ys = Set.toList (Set.fromList xs `Set.union` Set.fromList ys)
那么foldl
怎么办呢?让我们解开foldl
的定义来看,再次滥用等式推理。
union xs ys = xs ++ (case xs of
[] -> nub ys
(x:xs') -> foldl (flip delete) (delete x (nub ys)) xs'
)
这应该让诀窍更加明显 - 它会绕过xs
的元素,从(nub ys)
逐个删除它们。
虽然希望这有助于使union
中的代码更加清晰,但真正的主要原因应该是等式推理是解析Haskell代码的强大工具。不要害怕直接通过手动内联函数的定义来简化代码。
答案 1 :(得分:0)
我不确定这个union
是否符合您的要求但是相当简单。
我需要自己的功能来删除重复项。
rmdups ls = [d|(z,d)<- zip [0..] ls,notElem d $ take z ls]
它与同一目的的任何递归函数相同。
union l1 l2 = let l = l1 ++ l2 in rmdups l