1

我有一个格式如下的日志文件:

3
1 2 3
1 2 3 
1 2 3
1 2 3
4
1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4

单个数字表示矩阵的宽度,因为它们始终具有相同的高度。并且在同一个日志文件中可以有多个矩阵。我不想将矩阵数据解析为数组。我用 阅读了这些行scala.io.Source.fromFile(f).getLines.mkString,但我正在努力填充数组。

for(i <- 0 to 3) {
    for(j <- 0 to N-1) {
        matrix(i)(j) = ...
    }
}

如果这些行的索引方式与我希望矩阵的索引方式相同,那么这不会那么难。但是当 lines(n) 包含空格时,换行符..我做错了什么?

4

7 回答 7

6

您可以通过几个简单的步骤轻松完成此操作:

  1. 首先将输入分成List一行
  2. 然后将每一行分成List一个Strings
  3. 然后将String列表中的每个转换为Int
  4. 最后总结this Listof Lists Listto a Listof Arrays(使用简单的状态机)

状态机非常简单。

  1. 它首先读取下一个矩阵中的行数并记住它
  2. 然后它将该行数读入当前矩阵
  3. 在读取了记住的行数后,它将当前矩阵添加到读取矩阵列表中并返回到步骤 1

代码将如下所示:

    import io.Source

    def input = Source.fromString(
       """|3
          |1 2 1
          |1 2 2 
          |1 2 3
          |4
          |1 2 3 1
          |1 2 3 2
          |1 2 3 3
          |1 2 3 4""".stripMargin) // You would probably use Source.fromFile(...)

    type Matrix = List[Array[Int]]

    sealed trait Command
    case object ReadLength extends Command
    case class ReadLines(i: Int, matrix: Matrix) extends Command

    case class State(c: Command, l: List[Matrix])

    val parsedMatrixes = input.getLines().map(_.split(" ")).map(_.map(_.toInt)).foldLeft(State(ReadLength, List())) {
       case (State(ReadLength, matrixes), line) => State(ReadLines(line(0), List()), matrixes)
       case (State(ReadLines(1, currentMatrix), matrixes), line) => State(ReadLength,((line::currentMatrix).reverse)::matrixes)
       case (State(ReadLines(i, currentMatrix), matrixes), line) => State(ReadLines(i - 1, line::currentMatrix), matrixes)
    }.l.reverse

并为您提供以下结果:

parsedMatrixes: List[Matrix] = 
List(
  List(Array(1, 2, 1), 
       Array(1, 2, 2), 
       Array(1, 2, 3)), 
  List(Array(1, 2, 3, 1), 
       Array(1, 2, 3, 2), 
       Array(1, 2, 3, 3), 
       Array(1, 2, 3, 4)))

请注意,这不是最终解决方案,因为它没有任何错误处理。而且它不会释放其资源(关闭源)。

于 2013-09-20T11:34:39.270 回答
4

我认为不需要状态机;以下将为您提供与状态机解决方案在形状和内容上等效的数据结构:

import scala.io.Source

val input = Source.fromString(
  """|3
     |1 2 1
     |1 2 2
     |1 2 3
     |3 2 1
     |4
     |1 2 3 1
     |1 2 3 2
     |1 2 3 3
     |1 2 3 4""".stripMargin)

val matrices = input.getLines.grouped(5).map {
  case List(w, l1, l2, l3, l4) =>
    // feel free to use the value of `w.toInt` to do an assertion on the 4 lines
    List(l1, l2, l3, l4) map { _.split(' ').map(_.toInt).toList }
}

for (matrix <- matrices)
  println(matrix.map(_.mkString("[", ", ", "]")).mkString("\n"))

// prints:
// [1, 2, 1]
// [1, 2, 2]
// [1, 2, 3]
// [3, 2, 1]
// [1, 2, 3, 1]
// [1, 2, 3, 2]
// [1, 2, 3, 3]
// [1, 2, 3, 4]
于 2013-09-20T14:53:30.680 回答
0

stefan 的代码是功能状态机的一个很好的例子,但我个人更喜欢这样的东西

import io.Source

val input = Source.fromString(
   """|3
      |1 2 1
      |1 2 2
      |1 2 3
      |1 2 4
      |4
      |1 2 3 1
      |1 2 3 2
      |1 2 3 3
      |1 2 3 4""".stripMargin)

type Matrix = List[List[Int]]

def readMatrix(list: List[Int], height: Int, width: Int): Matrix =  {
  list.take(height * width).grouped(width).toList
}

def readMatrices(list: List[Int]): List[Matrix] = {
  if (list.isEmpty) List()
  else readMatrix(list.tail, 4, list.head) :: readMatrices(list.drop(4 * list.head + 1))
}

def printMatrix(matrix: Matrix) = println(matrix.map(_.mkString("", ", ", "")).mkString("", "\n", "\n"))

val parsedMatrices = readMatrices(input.mkString.split("\\s+").map(_.toInt).toList)
parsedMatrices.foreach(printMatrix)
于 2013-09-20T15:00:12.490 回答
0

下面的递归解决方案怎么样?

val fixedHeight = 4

def readMatrices(lines: List[String]): List[Array[Array[Int]]] = {

  def readMatrices0(lines: List[String], result: ListBuffer[Array[Array[Int]]]): List[Array[Array[Int]]] = lines match {
    case None => result.toList
    case head :: tail =>
      val n = head.toInt 
      val mat = readMatrix(tail.take(fixedHeight))
      // check that the matrix has width n:
      require(mat.forall(_.length == n), "Incorrect width")
      readMatrices0(tail.drop(fixedHeight), result + mat)
  }

  def readMatrix(lines: List[String]): Array[Array[Int]] = 
    lines.map(_.split(' ').map(_.toInt).toArray

  readMatrices0(lines, new ListBuffer)
}

val mats = readMatrices(scala.io.Source.fromFile(f).getLines)
于 2013-09-20T17:31:19.810 回答
0

好的,我想我有一个不错的:

  • 它处理具有不同行数的矩阵(我敢打赌海报日志文件中有具有不同行数的矩阵:-)
  • 它不会一次解析整个输入,而是逐步解析(因此使用的内存不多)
  • 它允许验证输入

准备

第一部分与我的第一个答案几乎相同。但我添加zipWithIndex以保留输入的行号。

    import io.Source

    def rawInput = Source.fromString(
       """|3
          |1 2 1
          |1 2 2 
          |1 2 3
          |4
          |1 2 3 1
          |1 2 3 2
          |1 2 3 3
          |1 2 3 4""".stripMargin) // You would probably use Source.fromFile(...)

    type Matrix = List[Array[Int]]

    def parsedInput = rawInput.getLines().map(_.split(" ")).map(_.map(_.toInt)).zipWithIndex

带迭代器的版本

此版本使用具有可变状态的经典 Java 迭代器。它不是函数式的,但应该运行得很快:

    def matrixIterator= new Iterator[Matrix] {
      val input = parsedInput

      var expectedNumerOfRows : Option[Int] = None

      override def hasNext = input.hasNext

      override def next() : Matrix = {
        import collection.mutable.MutableList
        var matrix : MutableList[Array[Int]] = MutableList()
        while (input.hasNext) {
          val (currentLine, lineNumber)=input.next()
          if (currentLine.size==1){
            expectedNumerOfRows=Some(currentLine.head)
            return matrix.toList
          }else{
            matrix+=currentLine
            expectedNumerOfRows.filter(_ != currentLine.size).foreach{ expected : Int =>
              //println(String.format("Warning in line %s: Expected %s columns, got %s", lineNumber+1, expected, currentLine.size))
            }
          }
        }
        return matrix.toList
      }
    }.next()

带流的版本

这个版本使用 Scala 流。它是递归的(虽然不是尾递归)并且不使用可变变量。它应该比 Iterator 版本慢一点,但更具可读性:

    def matrixStream : Stream[Matrix] = {
      def matrix(input : Iterator[(Array[Int], Int)], numberOfColumns : Int, currentMatrix : Matrix) : Stream[Matrix] = {
        if (!input.hasNext) {
          currentMatrix #:: Stream.empty
        }else{
          val (line, number) = input.next()
          if (line.size == 1) {
            currentMatrix.reverse #:: matrix(input, line.head, List.empty)
          }else{
            //if (numberOfColumns != line.size) println(...)
            matrix(input, numberOfColumns, line :: currentMatrix)
          }
        }
      }
      matrix(parsedInput,0,List()).drop(1)
    }
于 2013-09-23T06:29:46.937 回答
-1

正则表达式可以提供帮助。

val space = " ".r
val arrayOfNumbersAsStrings = space.split(line)
val arrayOfNumbersAsInts = arrayOfNumbersAsStrings.map(_.toInt)

UPD

val arrayOfNumbersAsStrings = space.split(' ')
val arrayOfNumbersAsInts = arrayOfNumbersAsStrings.map(_.toInt)
于 2013-09-20T10:22:38.397 回答
-1

即使没有正则表达式:

for (i <- 0 to 3) {
  matrix(i) = line.split(" ")
}
于 2013-09-20T10:30:01.300 回答