views:

171

answers:

3

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?

A: 

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.

thecoshman
It's really a great imperative way of doing that, but, I was looking for a functional way. `matrix` here saves its state.
Elazar Leibovich
I was implying you would but that into a function so you could call something likeget_matrix(""2 3 1 2 3 4 5 6 7 8 9 2 3 4 5 6 7 8 9 0");and have the 3D array returned to you.
thecoshman
Either I don't understand what you just said, or that we don't agree about the meaning of the term "functional programming". http://en.wikipedia.org/wiki/Functional_programming In any definition I know, putting things into functions does not make your program any more functional
Elazar Leibovich
Well it seems the definition of functional programming is something the did go over my head. I don't mean to sound sarcastic with that. That wiki seems to imply that a functional function will always give you the same result with the same inputs... which that function will, once you actually code it. Reading that wiki, and one for imperative programming dose confuse me some what as the actual difference.
thecoshman
+7  A: 
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))
Thomas Jung
Pretty much the same idea as mine, but you divided it to variables names more nicely, and used the 2.8 grouped function. I think you're having a problem with the parenthesis in the line of `z foreach (assert(_.size ==`
Elazar Leibovich
Oh, and is there a functional supporting array (you returned a list, and array could be way more efficient). Creating a list and converting it to an array seems awkward to me.
Elazar Leibovich
`tabulate` is great! It's not in 2.7, but fortunately people using 2.7 can achieve the same thing with slightly more awkward syntax by using `fromFunction`.
Rex Kerr
@Rex Actually `Traversable.tabulate` is the better option even if you did not want to build arrays.
Thomas Jung
@Thomas: Neither `Traversable` nor `tabulate` is in 2.7. I was just giving a 2.7 answer; I like your answer for 2.8.
Rex Kerr
I think `_ grouped columns` is incorrect in this context. It works because the number of elements in each column is the same as the number of columns in the example. I think the correct statement would be `a => a grouped (a.size / columns)`, perhaps with some padding as well.
Daniel
+2  A: 

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