1

我是函数式编程的新手,所以我正在做一些练习。我想写一个函数,给定一个独特的自然矩阵,比如说 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,但似乎无法弄清楚。

4

2 回答 2

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)

但是,唉,a mapplus aflatten是 a flatMap

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

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

于 2011-02-04T20:38:02.030 回答
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

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

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

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

于 2011-02-04T14:48:38.423 回答