在 Scala 数独生成器中过滤嵌套列表和列表项

Filtering nested lists and list items in Scala sudoku generator

作为数独生成器的一部分,我有一个旨在过滤嵌套列表的函数,以便 return 仅 return 某些索引的内部列表和这些列表中某些索引的内部列表元素。这个想法是 return 一个 List[Int],其中包含取自 9x9 数独拼图的 3x3 正方形的值,表示为 List[List[Int]],它作为函数的参数提供。

我尝试了两种方法,但都无法始终如一地工作。一种方法尝试从列表中过滤掉某些子列表,然后从剩余列表中过滤掉项目。此函数完全适用于某些索引,但对于其他索引,它会从某些子列表中过滤掉太多或太少的索引值,但不是全部:

def getGroup(indexX: Int, indexY: Int, puzzle: List[List[Int]]): List[Int] = {
  val groupX = { //determining row coordinate
  if(indexX <= 2) 2
  else if(indexX <= 5) 5
  else 8
  }
  val groupY = { //determining column coordinate
  if(indexY <= 2) 2
  else if(indexY <= 5) 5
  else 8
  }
  // Using filter
  val subsection: List[List[Int]] = puzzle.filter(x => puzzle.indexOf(x) <= groupX && puzzle.indexOf(x) >= groupX - 2) 
  // This sometimes filters out too many or too few items
  val group: List[List[Int]] = subsection.map(x => x.filter(y => x.indexOf(y) <= groupY && x.indexOf(y) >= groupY - 2))

  val result = group.flatten
  println("subsection " + subsection)
  println("group " + group)
  result
}

这是测试 List[List[Int]] 的一些输出,并打印以显示结果。我不清楚为什么不正确的索引会从某些子列表中过滤掉,但不是全部都在同一个函数调用中。我不认为我只是选择了错误的索引,否则对于同一函数调用中的所有子列表,它应该以相同的方式不正确。

test: List[List[Int]] = List(List(0, 2, 3, 4, 5, 6, 7, 8, 9), List(1, 2, 3, 4, 5, 6, 7, 8, 9), List(2, 2, 3, 4, 5, 6, 7, 8, 9), List(3, 2, 3, 4, 5, 6, 7, 8, 9), List(4, 2, 3, 4, 5, 6, 7, 8, 9), List(5, 2, 3, 4, 5, 6, 7, 8, 9), List(6, 2, 3, 4, 5, 6, 7, 8, 9), List(7, 2, 3, 4, 5, 6, 7, 8, 9), List(8, 2, 3, 4, 5, 6, 7, 8, 9))

scala> getGroup(2,2,test)
subsection: List(List(0, 2, 3, 4, 5, 6, 7, 8, 9), List(1, 2, 3, 4, 5, 6, 7, 8, 9), List(2, 2, 3, 4, 5, 6, 7, 8, 9))
group: List(List(0, 2, 3), List(1, 2, 3), List(2, 2, 3))
res12: List[Int] = List(0, 2, 3, 1, 2, 3, 2, 2, 3) //Correct 

scala> getGroup(2,7,test)
subsection: List(List(0, 2, 3, 4, 5, 6, 7, 8, 9), List(1, 2, 3, 4, 5, 6, 7, 8, 9), List(2, 2, 3, 4, 5, 6, 7, 8, 9))
group: List(List(7, 8, 9), List(7, 8, 9), List(7, 8, 9))
res13: List[Int] = List(7, 8, 9, 7, 8, 9, 7, 8, 9) //Correct 

scala> getGroup(7,7,test)
subsection: List(List(6, 2, 3, 4, 5, 6, 7, 8, 9), List(7, 2, 3, 4, 5, 6, 7, 8, 9), List(8, 2, 3, 4, 5, 6, 7, 8, 9))
group: List(List(7, 8, 9), List(8, 9), List(7, 9)) //Missing a 7 and an 8
res14: List[Int] = List(7, 8, 9, 8, 9, 7, 9) 

scala> getGroup(4,0,test)
subsection: List(List(3, 2, 3, 4, 5, 6, 7, 8, 9), List(4, 2, 3, 4, 5, 6, 7, 8, 9), List(5, 2, 3, 4, 5, 6, 7, 8, 9))
group: List(List(3, 2, 3), List(4, 2, 3, 4), List(5, 2, 3, 5)) //Not enough values filtered out--unwanted 4 and 5
res32: List[Int] = List(3, 2, 3, 4, 2, 3, 4, 5, 2, 3, 5)

另一种方法使用 for 循环,它只会产生符合 if 条件的值:

def getGroup(indexX: Int, indexY: Int, puzzle: List[List[Int]]): List[Int] = {
         //this portion is the same as above until the for expression:
  val groupX = { //determining row coordinate
    if(indexX <= 2) 2
    else if(indexX <= 5) 5
    else 8
  }
  val groupY = { //determining column coordinate
   if(indexY <= 2) 2
   else if(indexY <= 5) 5
   else 8
 }
    // using for expression
    val group = for(
      outer <- puzzle if puzzle.indexOf(outer) <= groupX && puzzle.indexOf(outer) >= groupX - 2; 
      inner <- outer if outer.indexOf(inner) <= groupY && outer.indexOf(inner) >= groupY - 2) 
        yield inner
  group
}

我已经用这个 List[List[Int]] 测试了这个函数,其中每个列表都以它的索引号开始,然后才向上计数:

val testGrid = List(List(0,2,3,4,5,6,7,8,9),List(1,2,3,4,5,6,7,8,9),List(2,2,3,4,5,6,7,8,9),
List(3,2,3,4,5,6,7,8,9),List(4,2,3,4,5,6,7,8,9),List(5,2,3,4,5,6,7,8,9),
List(6,2,3,4,5,6,7,8,9),List(7,2,3,4,5,6,7,8,9),List(8,2,3,4,5,6,7,8,9))

然而,输出并不总是正确的:

scala> getGroup(0, 0, testGrid)
res0: List[Int] = List(0, 2, 3, 1, 2, 3, 2, 2, 3) (correct)
scala> getGroup(3,3,testGrid)
res1: List[Int] = List(4, 5, 6, 5, 6, 4, 6) (too few)
scala> getGroup(3,7,testGrid)
res2: List[Int] = List(7, 8, 9, 7, 8, 9, 7, 8, 9) (correct)
scala> getGroup(7,7,testGrid)
res3: List[Int] = List(7, 8, 9, 8, 9, 7, 9) (too few)
scala> getGroup(7,0,testGrid)
res5: List[Int] = List(6, 2, 3, 6, 7, 2, 3, 7, 8, 2, 3, 8) (too many)

对于为什么我得到这个输出可能有一个明显的答案,但它已经逃脱了我。我怀疑它与内部循环有关,因为 indexY 小于 3 的所有测试都是正确的,无论 indexX 的值如何。

我将不胜感激任何对这些尝试的反馈。我也很感激任何关于可以使此功能更直观或简洁的替代方法的想法。

您可以使用 .slice 按索引过滤列表的一部分。然后 .map(x => x.slice(...)) 对内部列表执行相同的操作,最后 .flattenList[List[A]] 转换为 List[A]。总计:

def getGroup(indexX: Int, indexY: Int, puzzle: List[List[Int]]): List[Int] = {
  val xStart = indexX / 3 * 3
  val yStart = indexY / 3 * 3
  puzzle.slice(xStart, xStart + 3).map(row => row.slice(yStart, yStart + 3)).flatten
}