方案:
val col: IndexedSeq[Array[Char]] = for (i <- 1 to n) yield {
val x = for (j <- 1 to m) yield 'x'
x.toArray
}
这是一个相当简单的char矩阵。 toArray
过去曾允许更新。
var west = last.x - 1
while (west >= 0 && arr(last.y)(west) == '.') {
arr(last.y)(west) = ch;
west -= 1;
}
这是将所有.
更新为ch
,直到找到非点字符。
通常,更新直到满足停止条件,步骤数未知。
它的惯用等价物是什么?
结论
这是可行的,但是权衡不值得,当集合允许更新时,表达语法会失去很多性能。
答案 0 :(得分: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
}
这需要一个内部序列(假设List
更容易进行模式匹配,因为Arrays可以简单地转换为列表),并递归地用replace
char替换replacement
char。
然后您可以使用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,this answer可能是一个很好的起点。
答案 1 :(得分:2)
x3ro的答案是正确的答案,尤其是如果您关心性能或将在多个地方使用此操作。我想仅使用您在集合API中找到的内容添加简单的解决方案:
col.map { a =>
val (l, r) = a.span(_ == '.')
l.map {
case '.' => ch
case x => x
} ++ r
}