найти уникальные матрицы из большей матрицы

Я довольно новичок в функциональном программировании, поэтому я прохожу некоторые практические упражнения. Я хочу написать функцию, по заданной матрице уникальных натуральных чисел, скажем 5х5, возвращающую набор уникальных матриц меньшего размера, скажем 3х3, где матрицы должны быть целыми, т.е. созданными из значений, которые в оригинале соседствуют.

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

или, в Скала,

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 и не могу понять это.


person andyczerwonka    schedule 04.02.2011    source источник


Ответы (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))

is

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 плюс flatten получается 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

Его можно было бы записать как одно выражение, но если вы не разобьете его на функции, его будет ужасно читать. И вам придется либо использовать прямой канал (|>, не в стандартной библиотеке), либо добавлять эти функции неявно к типам, с которыми они работают, иначе их все равно будет трудно читать.

person Daniel C. Sobral    schedule 04.02.2011
comment
Мне пришлось изменить ваш код - он не скомпилируется, потому что фрагмент sliced flatMap (_.transpose) не возвращает Seq[Seq[Int]], тип Matrix. - person andyczerwonka; 06.02.2011
comment
@arcticpenguin Нет, он возвращает List[Seq[Seq[Int]]], который я использую в своем коде. Он возвращает список матриц, а не одну матрицу. Так что я не понимаю, о каких изменениях вы говорите. - person Daniel C. Sobral; 06.02.2011
comment
вы должны сказать toList на intArray, иначе вы получите List[List[Array[Int]]], что согласно 2.8.1 несовместимо с List[Seq[Seq[Int]]] - person andyczerwonka; 07.02.2011
comment
@articpenguin Понятно. Любопытно, так как я тестировал весь код. Интересно, что изменилось в моем окружении в то время? - person Daniel C. Sobral; 07.02.2011

Редактировать: Хорошо, кажется, я наконец понял, чего вы хотите. Я собираюсь показать способ, который работает, а не способ, который обеспечивает высокую производительность. (Как правило, это изменяемое решение, похожее на 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-операций из 1D-операций не всегда просто):

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

(обратите внимание, что reduceRight делает это операцией O(n) вместо O(n^2) — соединение с концом длинных накапливающихся списков — плохая идея, — но также обратите внимание, что при слишком большом количестве матриц это, вероятно, приведет к переполнению стека. ).

person Rex Kerr    schedule 04.02.2011
comment
Когда я говорю неповрежденные, я имею в виду смежные поперек и вниз 3. Пример, который я показываю, — это только первые три матрицы в ответе, скользящие по оригиналу 5x5. - person andyczerwonka; 04.02.2011
comment
Я скорректировал исходный вопрос, чтобы уточнить - person andyczerwonka; 04.02.2011
comment
@arcticpenguin - Хорошо, это помогает. Но теперь я понятия не имею, что вы пытаетесь сделать с полученной структурой данных. - person Rex Kerr; 04.02.2011
comment
Как только я это получу, я в порядке. Я хочу передать каждую результирующую матрицу другой функции, но эту часть я проработал. - person andyczerwonka; 04.02.2011
comment
@arcticpenguin - Значит, sliceduniquely не то, что ты хочешь? (Или slicedflat, если вы знаете, что ваши подматрицы уникальны?) - person Rex Kerr; 04.02.2011
comment
Я не уверен, что делает flatten в этом контексте, дает ли это мне 2d-массив 3x3, который я ищу? - да, я знаю, что они уникальны - person andyczerwonka; 04.02.2011