开发者

Functional way to get a matrix from text

I'm trying to solve some Google Code Jam problems, where an input matrix is typically given in this form:

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

where each element of the matrix is composed of, say, three integers. So this example should be converted to

#!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),
)

An imperative solution would be of the form

#!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)

A functional solution I've thought of is

#!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
  }

But I really feel it's the wrong way to go because:

  1. I'm using the functional List structure for each line, and then convert it to an array. The whole code seems much less efficeint
  2. I find it longer less elegant and much less readable than the python solution. It is harder to which of the map functions operates on what, as they all use the same semantics.

What is the right functional way to do that?


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

And to print the result:

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))

with the assumptions:

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

To produce an array tabulate fits better:

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))


Here's a version that works on 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)


I asked a question recently that is very similar. I think you will find the answer there.

find unique matrices from a larger matrix

The input begins as a String, and in the process is transformed into series of 2D matrices.


Lets try this then... seems your not too woried about language, so I'll just describe the code for it.

so we shall have our function that takes in this string, and returns a multi-dimensional array.

The first thing the funciton needs to do is read the string until it gets a space, then convert this sub string into a int and store it as 'rows', then do the same again but store it as 'columns'.

Next, it will need to loop through the remainder of the string, reading out numbers and storing them as ints in an array.

Then it needs to calculate the amount of numbers per cell, which should be "rows * columns / numbers_of_ints" That divide should be the one that would say "16 / 5 = 3" not "16 / 5 = 1" or 16 / 5 = 3.2222...".

Then we create our our array of length rows, where each element is an array of length columsn, where each element is and array of length 'numbers per cell'. This 3D array lets us still access each and every number stored.

now we need to loop through each cell and put its numbers into it.

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]
    }
  }
}

You should now have a matrix which contains all of our numbers as a single int stored some where with in the array.

should look like

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),
)

Hope this helps you. I will update it if I need explain something better, or some one has a suggestion.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜