【问题标题】:Functional way to get a matrix from text从文本中获取矩阵的函数式方法
【发布时间】:2011-02-01 03:33:38
【问题描述】:

我正在尝试解决一些 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 解决方案更不优雅,更不可读。哪个映射函数对什么进行操作更难,因为它们都使用相同的语义。

什么是正确的功能方法?

【问题讨论】:

  • 那么 scala 会是语言吗?如果不是,您使用的是什么语言?我知道您正在寻求一种功能性的方法来做到这一点,但是一个好的课程可能会为您提供更好的解决方案
  • @thecoshman 语言只是我传达 FP 想法的管道。出于实际目的,我不需要它,我只是在学习 FP。我碰巧知道 Scala 而不是 Haskell,所以我使用了它,但我可以在每一种功能足够的语言中做到这一点(即使 python 也很实用)。也许这个任务确实更适合命令式/OO 方法,但我仍在寻找最佳的功能解决方案。
  • 3x2矩阵的数据会是什么样子?
  • @Thomas 每个都是使用三个变量的数据结构。假设它是一个三元组数组。
  • @Thomas Jung 矩阵是(通常)二维数字网格。它们用于数学,非常方便地表示一组操作,例如,将 y 轴向上移动十个空格,围绕 x 轴旋转 60 度,沿 z-y 轴缩放 0.5。遗憾的是,cmets 不允许我使用新线,所以我无法向您展示数学中的新线。

标签: arrays scala functional-programming matrix


【解决方案1】:

我最近问了一个非常相似的问题。我想你会在那里找到答案。

find unique matrices from a larger matrix

输入以String 开始,并在此过程中转换为一系列二维矩阵。

【讨论】:

    【解决方案2】:

    这是适用于 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)
    

    【讨论】:

      【解决方案3】:
      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.8 的分组函数。我认为您在z foreach (assert(_.size == 行中的括号有问题
      • 哦,是否有功能支持数组(您返回了一个列表,数组可能更有效)。创建列表并将其转换为数组对我来说似乎很尴尬。
      • tabulate 太棒了!它不在 2.7 中,但幸运的是,使用 2.7 的人可以通过使用 fromFunction 以稍微笨拙的语法实现相同的目标。
      • @Rex 实际上Traversable.tabulate 是更好的选择,即使您不想构建数组。
      • @Thomas:Traversabletabulate 均不在 2.7 中。我只是给出一个 2.7 的答案;我喜欢你对 2.8 的回答。
      【解决方案4】:

      那么让我们试试吧……看来你对语言不太担心,所以我只描述它的代码。

      所以我们的函数将接收这个字符串,并返回一个多维数组。

      函数需要做的第一件事是读取字符串,直到它得到一个空格,然后将此子字符串转换为 int 并将其存储为“行”,然后再次执行相同操作,但将其存储为“列”。

      接下来,它需要遍历字符串的其余部分,读出数字并将它们作为整数存储在数组中。

      然后它需要计算每个单元格的数字数量,应该是“rows * columns / numbers_of_ints”那个除法应该是“16 / 5 = 3”而不是“16 / 5 = 1”或16 / 5 = 3.2222..."。

      然后我们创建我们的长度行数组,其中每个元素是一个长度为列的数组,其中每个元素是长度为“每个单元格的数字”的数组。这个 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]
          }
        }
      }
      

      您现在应该有一个矩阵,其中包含我们所有的数字作为单个 int 存储在数组中的某个位置。

      应该看起来像

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

      希望这对您有所帮助。如果我需要更好地解释或有人有建议,我会更新它。

      【讨论】:

      • 这确实是一种非常必要的方法,但是,我正在寻找一种功能性的方法。 matrix 这里保存它的状态。
      • 我的意思是你会,但它会变成一个函数,这样你就可以调用类似 get_matrix(""2 3 1 2 3 4 5 6 7 8 9 2 3 4 5 6 7 8 9 0") ; 并将 3D 数组返回给您。
      • 要么我不明白你刚才说的话,要么我们不同意“函数式编程”一词的含义。 en.wikipedia.org/wiki/Functional_programming 在我所知道的任何定义中,将事物放入函数不会使您的程序更具功能性
      • 好吧,似乎函数式编程的定义确实让我难以理解。我并不是要听起来讽刺。该 wiki 似乎暗示功能函数将始终使用相同的输入为您提供相同的结果......一旦您实际编码,该功能将提供。阅读那个 wiki,以及一个用于命令式编程的 wiki,让我感到有些困惑。
      猜你喜欢
      • 1970-01-01
      • 2018-10-25
      • 1970-01-01
      • 2018-02-11
      • 2021-07-17
      • 1970-01-01
      • 2017-03-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多