从更大的矩阵中找到唯一的矩阵

时间:2011-02-04 14:24:57

标签: scala functional-programming pointfree

我是一个相当新的函数式编程,所以我正在进行一些练习练习。我想编写一个函数,给定一个独特的自然矩阵,比如说5x5,返回一个较小尺寸的独特矩阵的集合,比如3x3,其中矩阵必须是完整的,即根据原始的相邻值创建。

01 02 03 04 05
06 07 08 09 10
11 12 13 14 15
16 17 18 19 20
21 22 23 24 25

简单。只需以3个为一组逐个滑动,然后向下滑动,即可获得如下内容:

01 02 03 | 02 03 04 | 03 04 05 | 06 07 08
06 07 08 | 07 08 09 | 08 09 10 | 11 12 13
11 12 13 | 12 13 14 | 13 14 15 | 16 17 18

或者,在Scala中,

List(List(1, 2, 3), List(6, 7, 8), List(11, 12, 13))
List(List(2, 3, 4), List(7, 8, 9), List(12, 13, 14))
List(List(3, 4, 5), List(8, 9, 10), List(13, 14, 15))
List(List(6, 7, 8), List(11, 12, 13), List(16, 17, 18))

依旧等......

所以我冒险使用Scala(我选择的语言,因为它允许我从命令式发展到功能性,而且我在Java中度过了最近几年。

val array2D = "01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25".grouped(3).map(_.trim.toInt).grouped(5)
val sliced = array2D.map(row => row.sliding(3, 1).toList).sliding(3, 1).toList

现在我有一个可以使用的数据结构,但我看不到功能方式。当然,我可以遍历sliced的每一部分,创建一个var matrix = new ListBuffer[Seq[Int]](),然后必须创建一个包,然后我就完成了。

我希望使用Scala找到一个功能强大,理想情况下无点的方法,但我很难过。必须有一种方法可以用3或类似的方式压缩...我已经搜索过ScalaDocs并且似乎无法弄明白。

2 个答案:

答案 0 :(得分:2)

你到了一半。事实上,我无法弄清楚如何做你已经做过的事情。我稍微分解了你的代码,以便更容易理解。另外,我将array2D设为List,因此我可以更轻松地使用代码。 : - )

val input = "01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25"
val intArray = (input split " " map (_.toInt) toList)
val array2D = (intArray grouped 5 toList)
val sliced = array2D.map(row => row.sliding(3, 1).toList).sliding(3, 1).toList

好的,所以你有一堆列表,每个都有点像这样:

List(List(List( 1,  2,  3), List( 2,  3,  4), List( 3,  4,  5)), 
     List(List( 6,  7,  8), List( 7,  8,  9), List( 8,  9, 10)), 
     List(List(11, 12, 13), List(12, 13, 14), List(13, 14, 15)))

你想要他们这样:

List(List(List(1, 2, 3), List(6, 7,  8), List(11, 12, 13)), 
     List(List(2, 3, 4), List(7, 8,  9), List(12, 13, 14)), 
     List(List(3, 4, 5), List(8, 9, 10), List(13, 14, 15)))

这对你感觉合适吗?三个子列表中的每一个都是一个矩阵:

List(List(1, 2, 3), List(6, 7,  8), List(11, 12, 13))

01 02 03
06 07 08
11 12 13

所以,基本上,你想要转置它们。接下来的步骤是:

val subMatrices = sliced map (_.transpose)

该东西的类型是List[List[List[Seq[Int]]]]。让我们考虑一下...... 2D矩阵由序列序列表示,因此List[Seq[Int]]对应于矩阵。让我们说:

type Matrix = Seq[Seq[Int]]
val subMatrices: List[List[Matrix]] = sliced map (_.transpose)

但是你想要一个矩阵列表,所以你可以压扁它:

type Matrix = Seq[Seq[Int]]
val subMatrices: List[Matrix] = (sliced map (_.transpose) flatten)

但是,唉,map加上flattenflatMap

type Matrix = Seq[Seq[Int]]
val subMatrices: List[Matrix] = sliced flatMap (_.transpose)

现在,您需要唯一的子矩阵。这很简单:它是一套。

val uniqueSubMatrices = subMatrices.toSet

或者,如果您希望将结果保留为序列,

val uniqueSubMatrices = subMatrices.distinct

就是这样。完整代码只是为了说明:

type Matrix = Seq[Seq[Int]]
val input = "01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25"
val intArray = (input split " " map (_.toInt) toList)
val array2D: Matrix = (intArray grouped 5 toList)
val sliced: List[List[Matrix]] = (array2D map (row => row sliding 3 toList) sliding 3 toList)
val subMatrices: List[Matrix] = sliced flatMap (_.transpose)
val uniqueSubMatrices: Set[Matrix] = subMatrices.toSet

它可以写成单个表达式,但除非你将其分解为函数,否则读起来会很糟糕。而且您要么必须使用正向管道(|>,而不是标准库中),要么将这些函数隐式添加到它们所处理的类型中,否则无论如何都难以阅读。

答案 1 :(得分:1)

编辑:好的,我想我终于明白了你的想法。我将展示一种有效的方式,而不是一种高性能的方式。 (这通常是类似Java的可变解决方案,但您已经知道如何做到这一点。)

首先,你真的,真的应该用你自己的2D合理的系列来做到这一点。使用一堆1D集合来模拟2D集合将导致不必要的混淆和复杂化。不要这样做。真。这是一个坏主意。

但是,好吧,不管怎样,让我们​​这样做。

val big = (1 to 25).grouped(5).map(_.toList).toList

这是您想要的整个矩阵。接着,

val smaller = (for (r <- big.sliding(3)) yield r.toList).toList

是您想要的行组。现在,您应该使用2D数据结构,因为您想要做一些不能很好地映射到1D操作的东西。但是:

val small = smaller.map(xss =>
  Iterator.iterate(xss.map(_.sliding(3)))(identity).
    takeWhile(_.forall(_.hasNext)).
    map(_.map(_.next)).
    toList
).toList

如果你小心翼翼地将它拆开,你会发现你正在创建一堆迭代器(xss.map(_.sliding(3))),然后通过一步一步地保持那些相同的迭代器,在锁定步骤中迭代它们,停止时其中至少有一个是空的,并将它们映射到下一个值(这就是你向前走的方式)。

现在你已经有了矩阵,你可以根据需要存储它们。就个人而言,我已经将名单压扁了:

val flattened = small.flatten

你编写了一个并排有矩阵的结构,你也可以做一些努力(同样,因为从一维操作中创建2D操作并不总是那么简单):

val sidebyside = flattened.reduceRight((l,r) => (l,r).zipped.map(_ ::: _))

(注意reduceRight使其成为O(n)操作而不是O(n ^ 2) - 加入到长累积列表的末尾是一个坏主意 - 但是请注意,如果矩阵太多,这可能会溢出堆栈。)