Идиоматическое обновление коллекции «до»

Сценарий

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 голоса | спросил flavian 29 +04002013-10-29T15:38:08+04:00312013bEurope/MoscowTue, 29 Oct 2013 15:38:08 +0400 2013, 15:38:08

2 ответа


0

Ваше желание «более чистого, более идиоматического» решения, конечно, немного размыто, поскольку оставляет много места для субъективности. В общем, я считаю, что подпрограмма хвостового рекурсивного обновления более идиоматична, но она может быть не «чище», если вы более знакомы с нефункциональным стилем программирования. Я придумал это:

@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 для более простого сопоставления с образцом, поскольку массивы легко преобразуются в списки), и заменяет replace char рекурсивно на 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, этот ответ может быть хорошим началом.

ответил fresskoma 29 +04002013-10-29T17:14:56+04:00312013bEurope/MoscowTue, 29 Oct 2013 17:14:56 +0400 2013, 17:14:56
0

x3ro - правильный ответ, особенно если вы заботитесь о производительности или собираетесь использовать эту операцию в нескольких местах. Я хотел бы добавить простое решение, используя только то, что вы найдете в API коллекций:

col.map { a =>
  val (l, r) = a.span(_ == '.')
  l.map {
    case '.' => ch
    case x => x
  } ++ r
}
ответил wingedsubmariner 29 +04002013-10-29T17:18:25+04:00312013bEurope/MoscowTue, 29 Oct 2013 17:18:25 +0400 2013, 17:18:25

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132