4

场景

val col: IndexedSeq[Array[Char]] = for (i <- 1 to n) yield {
   val x = for (j <- 1 to m) yield 'x'
   x.toArray
}

这是一个相当简单的字符矩阵。toArray用于允许更新。

  var west = last.x - 1
  while (west >= 0 && arr(last.y)(west) == '.') {
      arr(last.y)(west) = ch;
      west -= 1;
  }

这将全部更新.为,ch直到找到非点字符。

通常,更新直到满足停止条件,步数未知。

它的惯用等价物是什么?

结论

这是可行的,但权衡是不值得的,当集合允许更新时,表达语法会损失很多性能。

4

2 回答 2

3

您对“更清洁,更惯用”的解决方案的愿望当然有点模糊,因为它为主观性留下了很大的空间。一般来说,我认为尾递归更新例程更惯用,但如果您更熟悉非函数式编程风格,它可能不会“更干净”。我想出了这个:

@tailrec
def update(arr:List[Char], replace:Char, replacement:Char, result:List[Char] = Nil):List[Char] = arr match {
    case `replace` :: tail =>
        update(tail, replace, replacement, replacement :: result)
    case _ => result.reverse ::: arr
}

这需要一个内部序列(假设 aList更容易进行模式匹配,因为数组可以很容易地转换为列表),并用递归替换replacechar 。replacement

然后您可以使用 map 更新外部序列,如下所示:

col.map { x => update(x, '.', ch) }

另一种更可重用的替代方法是编写自己的mapUntil,或使用在补充库中实现的(Scalaz 可能有类似的东西)。我想出的一个看起来像这样:

def mapUntil[T](input:List[T])(f:(T => Option[T])) = {
    @tailrec
    def inner(xs:List[T], result:List[T]):List[T] = xs match {
        case Nil => Nil
        case head :: tail => f(head) match {
            case None => (head :: result).reverse ::: tail
            case Some(x) => inner(tail, x :: result)
        }
    }

    inner(input, Nil)
}

它与常规map调用相同,只是它在传递的函数返回时立即停止None,例如

mapUntil(List(1,2,3,4)) {
    case x if x >= 3 => None
    case x => Some(x-1)
}

会导致

List[Int] = List(0, 1, 3, 4)

如果您想查看 Scalaz,这个答案可能是一个不错的起点。

于 2013-10-29T13:14:56.223 回答
2

x3ro 的答案是正确的答案,尤其是。如果您关心性能或打算在多个地方使用此操作。我想仅使用您在集合 API 中找到的内容添加简单的解决方案:

col.map { a =>
  val (l, r) = a.span(_ == '.')
  l.map {
    case '.' => ch
    case x => x
  } ++ r
}
于 2013-10-29T13:18:25.527 回答