Haskell实现左外连接

时间:2015-05-10 03:17:58

标签: haskell left-join

我正在进行uni赋值,仅使用prelude函数定义在Haskell中实现左外连接。我尝试过使用列表推导:

ljoin :: Eq a => [(a,b)] -> [(a,c)] -> [(a,b, Maybe c)]
ljoin xs ys = [if x1 == y1 then (x1,x2, Just y2) else (x1,x2, Nothing)| (x1, x2) <- xs, (y1,y2) <- ys]

这会为示例生成以下输出:

Main> ljoin [(2,"S"),(1,"J")] [(2,1),(2,2),(3,4)]
[(2,"S",Just 1),(2,"S",Just 2),(2,"S",Nothing),(1,"J",Nothing),(1,"J",Nothing),(1,"J",Nothing)]
(399 reductions, 834 cells)           

是否有人能够给我任何提示来摆脱结果中的重复条目? (2,&#34; S&#34;,Nothing)不应出现在结果中,并且只有1(&#34; J&#34;,Nothing)术语中的1个应存在。

提前致谢。

2 个答案:

答案 0 :(得分:2)

因此,当您执行[f x y | x <- xs, y <- ys]时,您正在执行笛卡尔积内部联接xs中的每个元素都将与每个值配对ys。那不是你想要的。

你想要的是首先遍历xs的所有元素,然后获取ys中匹配的所有值的列表,然后分析后一个列表是否为空,如果需要,请添加Nothing,然后将x1x2与该列表的元素相邻以生成三元组。因此,我们首先撰写&#34;一般策略&#34;:

ljoin xs ys = concat [map (adjoin x1 x2) $ handleNulls $ getMatches x1 ys 
                          | (x1, x2) <- xs] where

&#34; concat&#34;是必要的,因为xs的每个元素都会生成自己的列表,我们希望&#34;展平&#34;将它们全部列入一个重要的结果列表中,这是我们的总体战略中唯一缺失的内容。现在我们填写详细信息:

ljoin xs ys = concat [map (adjoin x1 x2) $ handleNulls $ getMatches x1 ys 
                          | (x1, x2) <- xs] where
    adjoin x1 x2 x3 = (x1, x2, x3)
    handleNulls zs | null zs   = [Nothing]
                   | otherwise = map Just zs
    getMatches x1 ys = [y2 | (y1, y2) <- ys, y1 == x1]

现在有两种方式可能意味着&#34;仅使用前奏函数定义&#34;:这可能意味着您不允许任何import语句,但您可以允许使表达式尽可能复杂(理智!),或者它可能意味着你不允许任何其他定义,包括where(疯狂!) 。在没有为handleNulls步骤作弊的情况下,我没有看到任何方法来做后者,但你可以隐藏&#34;隐藏&#34;欺骗作为一个功能应用程序,因为它不是一个递归绑定:

ljoin xs ys = concat [ zip3 (repeat x1) (repeat x2) . 
                  (\zs -> if null zs then [Nothing] else map Just zs) .
                  map snd . filter ((x1 ==) . fst) $ ys | (x1, x2) <- xs]

但那只是&#34; yuck&#34 ;;以前的版本更清晰。

答案 1 :(得分:0)

如果ys中的密钥是唯一的,那么最简单的选择就是使用Prelude.lookup

ljoin xs ys = [ (kx, x, lookup kx ys) | (kx,x) <- xs ]

否则,归结为这样的事情:

ljoin ((kx,x):xs) ys = (case [ y | (ky, y) <- ys, ky==kx ] of
                           [] -> [(kx, x, Nothing)]
                           ys' -> [(kx, x, Just y') | y' <- ys'])
                       ++ ljoin xs ys
ljoin [] _ = []

或等效地,如果你想要理解:

 ljoin xs ys = [ (kx, x, my) | (kx, x) <- xs, my <- case [ y | (ky, y) <- ys, kx==ky ] of { [] -> [Nothing]; ys' -> fmap Just ys'} ]

当然,您可以使用模式匹配函数替换case

 ljoin xs ys = [ (kx, x, my) | (kx, x) <- xs, my <- listToMaybe [ y | (ky, y) <- ys, kx==ky ] ]
     where listToMaybe []  = [Nothing]
           listToMaybe ys' = map Just ys'