Функциональный способ получить матрицу из текста - PullRequest
3 голосов
/ 26 марта 2010

Я пытаюсь решить некоторые проблемы с Google Code Jam, где матрица ввода обычно задается в этой форме:

2 3 #matrix dimensions
1 2 3 4 5 6 7 8 9 # all 3 elements in the first row
2 3 4 5 6 7 8 9 0 # each element is composed of three integers

где каждый элемент матрицы состоит, скажем, из трех целых чисел. Так что этот пример должен быть преобразован в

#!scala
Array(
     Array(A(1,2,3),A(4,5,6),A(7,8,9),
     Array(A(2,3,4),A(5,6,7),A(8,9,0),
)

Императивное решение будет иметь вид

#!python
input = """2 3
1 2 3 4 5 6 7 8 9
2 3 4 5 6 7 8 9 0
"""
lines = input.split('\n')
class Aclass:
    def __init__(self,a,b,c):
        pass

print lines[0]
m,n = (int(x) for x in lines[0].split())
array = []
row = []
A = []
for line in lines[1:]:
    for elt in line.split():
        A.append(elt)
        if len(A)== 3:
            row.append(Aclass(A[0],A[1],A[2]))
            A = []
    array.append(row)
    row = []

from pprint import pprint
pprint(array)

Функциональное решение, о котором я подумал:

#!scala
def splitList[A](l:List[A],i:Int):List[List[A]] = {
  if (l.isEmpty) return List[List[A]]()
  val (head,tail) = l.splitAt(i)
  return head :: splitList(tail,i)
}

def readMatrix(src:Iterator[String]):Array[Array[TrafficLight]] = {
  val Array(x,y) = src.next.split(" +").map(_.trim.toInt)
  val mat = src.take(x).toList.map(_.split(" ").
          map(_.trim.toInt)).
          map(a => splitList(a.toList,3).
                map(b => TrafficLight(b(0),b(1),b(2))
                 ).toArray
                ).toArray
    return mat
  }

Но я действительно чувствую, что это неправильный путь, потому что:

  1. Я использую функциональную структуру List для каждой строки, а затем преобразую ее в массив. Весь код кажется гораздо менее эффективным
  2. Я считаю, что он длиннее, менее изящен и гораздо менее читабелен, чем решение на python. Сложнее определить, какая из функций карты работает с чем, поскольку все они используют одну и ту же семантику.

Каков правильный функциональный способ сделать это?

Ответы [ 4 ]

7 голосов
/ 26 марта 2010
val x = """2 3
1 2 3 4 5 6 7 8 9
2 3 4 5 6 7 8 9 0
"""

val a = x split "\n" map (_.trim.split(" "))
val rows = a(0)(0).toInt
val columns = a(0)(1).toInt

val matrix = (a drop 1) map (_ grouped columns toList) toList

И распечатать результат:

matrix.map(_.map(_.mkString("(",",",")")).mkString("(",",",")")).mkString("\n")

res1: String =
((1,2,3),(4,5,6),(7,8,9))
((2,3,4),(5,6,7),(8,9,0))

с допущениями:

assert(rows == matrix.length)
assert(matrix.forall(_.forall(_.size == columns))) 

Для создания массива tabulate подходит лучше:

val a = x split "\n" map (_.trim.split(" "))
val rows = a(0)(0).toInt
val columns = a(0)(1).toInt
val matrix = Array.tabulate(rows, a(1).size / columns, columns)(
  (i,j,k) => a(i +  1)(j * columns + k))
2 голосов
/ 27 марта 2010

Вот версия, которая работает на Scala 2.7:

val x = """2 3
1 2 3 4 5 6 7 8 9
2 3 4 5 6 7 8 9 0
"""

val a = x.trim split "\n" map (_.trim.split(" "))
val rows = a(0)(0).toInt
val columns = a(0)(1).toInt

def intervals(n: Int) = (Stream from (0, n)) zip (Stream from (n, n))

val matrix = (a drop 1) map (v =>
  intervals(v.size / columns) 
  take columns 
  map Function.tupled(v.subArray) 
  toArray
) toArray

val repr = matrix map (
  _ map (
    _ mkString ("Array(", ", ", ")")
  ) 
  mkString ("Array(", ", ", ")")
) mkString ("Array(\n\t", ",\n\t", "\n)")

println(repr)
0 голосов
/ 07 февраля 2011

Я недавно задал вопрос, который очень похож. Я думаю, что вы найдете ответ там.

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

Ввод начинается как String, и в процессе преобразовывается в серию 2D матриц.

0 голосов
/ 26 марта 2010

Давайте попробуем это потом ... кажется, вы не слишком беспокоитесь о языке, поэтому я просто опишу код для него.

поэтому у нас будет наша функция, которая принимает эту строку и возвращает многомерный массив.

Первое, что нужно сделать функции - это прочитать строку до тех пор, пока она не получит пробел, затем преобразовать эту подстроку в int и сохранить ее как 'строки', затем сделать то же самое снова, но сохранить ее как 'столбцы'.

Далее, он должен будет перебрать остаток строки, считывая числа и сохраняя их в виде целых чисел в массиве.

Затем необходимо рассчитать количество чисел в ячейке, которое должно быть «row * columns / numbers_of_ints». Это деление должно быть таким, которое скажет «16/5 = 3», а не «16/5 = 1» или 16/5 = 3,2222 ... ".

Затем мы создаем наш массив строк длины, где каждый элемент является массивом длины columsn, где каждый элемент является массивом чисел длины на ячейку. Этот 3D-массив позволяет нам по-прежнему получать доступ к каждому сохраненному номеру.

теперь нам нужно перебрать каждую ячейку и поместить в нее ее номера.

for(i = 0 ; i < rows ; i = i + 1)
{
  for(j = 0 ; j < columns ; j = j + 1)
  {
    for(k = 0 ; k < numbers_per_cell ; k = k + 1)
    {
      matrix[i][j][k] = numbers[( i * columns ) + j + k]
    }
  }
}

Теперь у вас должна быть матрица, которая содержит все наши числа в виде единого целого, сохраненного где-то в массиве.

должно выглядеть как

Array(
  Array(Array(1,2,3),Array(4,5,6),Array(7,8,9),
  Array(Array(2,3,4),Array(5,6,7),Array(8,9,0),
)

Надеюсь, это поможет вам. Я обновлю его, если мне нужно что-то объяснить лучше, или у кого-то есть предложение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...