Scala: вырезать список из первого ненулевого элемента - PullRequest
4 голосов
/ 26 марта 2020

Предположим, у меня есть список, заполненный нулями

val a = List(0,0,0,0,2,4,0,6,0,7)

Я хочу вырезать нули, предшествующие первому ненулевому элементу, а также вернуть индекс, в котором присутствует 1-й ненулевой элемент. Для приведенного выше случая я хочу вывод:

output = List(2,4,0,6,0,7)
idx = 4

Как мне это сделать?

Ответы [ 7 ]

3 голосов
/ 27 марта 2020

Во-первых, вы можете использовать zipWithIndex для удобного сопряжения каждого элемента с его индексом. Затем используйте dropWhile для возврата всех предыдущих нулевых элементов. Оттуда у вас будут все остальные элементы в паре с их индексами из оригинального List. Вы можете unzip их. Поскольку это может привести к пустому списку, индекс, который вы ищете, должен быть необязательным.

scala> val (remaining, indices) = a.zipWithIndex.dropWhile { case (a, i) => a == 0 }.unzip
remaining: List[Int] = List(2, 4, 0, 6, 0, 7) // <--- The list you want
indices: List[Int] = List(4, 5, 6, 7, 8, 9)

scala> val index = indices.headOption
index: Option[Int] = Some(4) // <--- the index of the first non-zero element
3 голосов
/ 27 марта 2020

Это вариант использования для диапазона:

val a = List(0,0,0,0,2,4,0,6,0,7)

val (zeros, output) = a.span(_ == 0)
val idx = zeros.length
2 голосов
/ 26 марта 2020

Немного изменен из ответа @bottaio, но возвращает индекс Option [Int] вместо простого Int для индекса.

def firstNonZero(l: List[Int]): (Option[Int], List[Int]) = {
  @annotation.tailrec
  def go(remaining: List[Int], idx: Int): (Int, List[Int]) =
    remaining match {
      case Nil     => idx -> Nil
      case 0 :: xs => go(remaining = xs, idx + 1)
      case xs      => idx -> xs
    }

  l match {
    case 0 :: xs =>
      val (idx, list) = go(remaining = xs, idx = 1)
      Some(idx) -> list

    case list =>
      None -> list
  }
}
2 голосов
/ 26 марта 2020

Вы можете сделать это довольно чисто с помощью indexWhere:

val idx = a.indexWhere(_!=0)
val output = a.drop(idx)
2 голосов
/ 26 марта 2020

использование dropWhile:

val output = a.dropWhile{ _ == 0 }
val idx = output.headOption
  .map(_ => a.length - output.length)
  .getOrElse(-1) // index starting from 0, -1 if not found
1 голос
/ 26 марта 2020

Просто еще одно решение с использованием foldLeft:

val (i, l) =
  a.foldLeft((None: Option[Int], List.empty: List[Int]))((b, n) => {
    if (n == 0 && b._2.isEmpty) (b._1.orElse(Some(0)).map(_ + 1), List.empty)
    else (b._1.orElse(Some(0)), b._2 :+ n)
  })
i: Option[Int] = Some(4)
l: List[Int] = List(2, 4, 0, 6, 0, 7)
1 голос
/ 26 марта 2020

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

def firstNonZero(l: List[Int]): (Int, List[Int]) = {
  @tailrec
  def go(l: List[Int], idx: Int): (Int, List[Int]) = l match {
    case Nil => (idx, Nil)
    case 0 :: xs => go(xs, idx + 1)
    case xs => (idx, xs)
  }

  go(l, 0)
}

, что также эквивалентно

val (leadingZeros, rest) = a.span(_ == 0)
val (index, output) = (leadingZeros.length, rest)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...