Haskell中“列表列表”的组成

时间:2017-09-14 01:05:52

标签: haskell tuples discrete-mathematics

我正在尝试制作composition of relations。 这里的关系用列表表示。

funComp :: Ord a => [[a,a]] -> [[a,a]] -> [[a,a]]
funComp [[a,b]] [[c,d]]
 | b == c = [[a,d]]

例如,给定:

[[1,1],[1,2],[2,2],[2,3],[3,3],[3,4],[4,4]] 

[[1,4],[1,4],[2,3],[2,3],[3,2],[3,1],[4,1]]

应该返回:

[[1,4],[1,4],[1,3],[2,2],[2,1],[3,2],[3,1],[4,1]]

2 个答案:

答案 0 :(得分:4)

我在下面提供了完整的答案,希望您会发现它有用。我同意@erisco的意见,如果你刚刚开始,这个问题可能会有点进步。您可能希望从一些仅涉及一个列表/关系而不是合并两个的问题开始。 (例如,如何通过删除 x R x 形式的所有元素来建立反自反关系?如何生成所有值 y 的集合,使得 > x R y 对于固定值 x ?你能用内置的Haskell函数和从头开始解决这些问题吗?)

无论如何,首先,你可能会发现它很有帮助,只是从易于阅读的角度来看,使用元组来表示关系元素,所以你的两个示例关系将是:

r1 = [(1,1),(1,2),(2,2),(2,3),(3,3),(3,4),(4,4)]
r2 = [(1,4),(1,4),(2,3),(2,3),(3,2),(3,1),(4,1)]

您要做的是构造是一种关系,由从r1r2的任何有序元素对创建的所有复合元素组成。这是列表理解有用的东西:

result = [ combine x y | x <- r1, y <- r2 ]

此表达式从两个关系中创建每对combine x yx上运行y的结果列表。 combine应该是什么?好吧,它需要两个元素并生成它们的组合,所以你可能首先尝试类似的东西:

combine (a,b) (c,d) | b == c = (a,d)

这将进行类型检查,但combine只是一个部分功能 - 对于那些没有组合的对没有价值 - 所以这不会让你太过分。你需要一种编写combine的方法,它允许你返回一个组合,如果存在一个组合,否则什么也不返回。

在Haskell中执行此操作的标准方法是使用Maybe类型:

combine (a,b) (c,d) | b == c = Just (a,d)
                    | otherwise = Nothing

此类型检查并运行,但result的值如下所示:

[Just (1,4),Just (1,4),Nothing,Nothing...]

幸运的是,Data.Maybe中有一个名为catMaybes的函数正是我们对这种情况所需要的 - 它会丢弃所有Nothing并提取Just值在删除文字Just构造函数的同时:

> catMaybes result
[(1,4),(1,4),(1,3),...]

有一些重复项应该删除,所以让我们使用nub中的Data.List删除它们:

> nub (catMaybes result)
[(1,4),(1,3),(2,3),(2,2),(2,1),(3,2),(3,1),(4,1)]

看起来像你可能想要的答案,但我认为你在示例输出中错过了(2,3)

完整的程序,略微概括,看起来像这样:

module Relations where

import Data.List
import Data.Maybe

r1 = [(1,1),(1,2),(2,2),(2,3),(3,3),(3,4),(4,4)]
r2 = [(1,4),(1,4),(2,3),(2,3),(3,2),(3,1),(4,1)]

combine (a,b) (c,d) | b == c = Just (a,d)
                    | otherwise = Nothing

funcComp r1 r2
  = nub $ catMaybes [ combine x y | x <- r1, y <- r2 ]

result = funcComp r1 r2

如果您还没有:

  • 习惯于在从一个或多个现有列表生成项目的任何情况下至少考虑列表推导
  • 阅读PreludeData.ListData.Maybe等关键模块的文档,以便熟悉其中可用的功能

答案 1 :(得分:0)

“集合理解”方面的许多数学定义对列表推导具有相当直接的翻译。 (从左到右)关系构成的定义是:

R; S = {(x,z)| (x,y)∈R,(y,z)∈S}

也就是说,x与复合中的z有关,如果你可以从x到z跳过一些y到R,然后从y跳到z到s。

你几乎可以准确地翻译它 - 我们只需要将y的两个提及分开,因为你只能绑定模式中的新变量。

compose r s = [ (x,z) | (x,y) <- r, (y',z) <- s, y == y' ]

要给它一个类型签名,我会创建一个类型同义词,然后

type Relation a b = [(a,b)]
compose :: Relation a b -> Relation b c -> Relation a c

请注意,这与许多定义组合的顺序相反。我只是把关系视为成对的对象,特别清楚这个方向,并且反过来混淆。