Список каждого n-го элемента в данном списке

Это простое упражнение, которое я решаю в Scala: заданный список l возвращает новый список, содержащий каждый элемент n-th l. Если n > l.size возвращает пустой список.

def skip(l: List[Int], n: Int) = 
  Range(1, l.size/n + 1).map(i => l.take(i * n).last).toList

Мое решение (см. выше), похоже, работает, но я ищу что-л. проще. Как бы вы его упростили?

Ответ 1

scala> def skip[A](l:List[A], n:Int) = 
         l.zipWithIndex.collect {case (e,i) if ((i+1) % n) == 0 => e} // (i+1) because zipWithIndex is 0-based
skip: [A](l: List[A], n: Int)List[A]

scala> val l = (1 to 10).toList
l: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> skip(l,3)
res2: List[Int] = List(3, 6, 9)

scala> skip(l,11)
res3: List[Int] = List()

Ответ 2

Несколько проще:

scala> val l = (1 to 10).toList
l: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

// n == 3

scala> l.drop(2).grouped(3).map(_.head).toList
res0: List[Int] = List(3, 6, 9)

// n > l.length

scala> l.drop(11).grouped(12).map(_.head).toList
res1: List[Int] = List()

(toList просто для того, чтобы заставить iteratot оцениваться)

Работает с бесконечными списками:

Stream.from(1).drop(2).grouped(3).map(_.head).take(4).toList
res2: List[Int] = List(3, 6, 9, 12)

Ответ 3

Более читаемый бит и размер цикла O(l.length/n):

def skip(l: List[Int], n: Int) = {
  require(n > 0)
  for (step <- Range(start = n - 1, end = l.length, step = n))
    yield l(step)
}

Ответ 4

Сдвиг влево O(n)

def skip(xs: List[Int], n: Int) = {
  xs.foldLeft((List[Int](), n)){ case ((acc, counter), x) =>
      if(counter==1)
        (x+:acc,n)
      else
        (acc, counter-1)
  }
  ._1
  .reverse
}

scala > skip(List(1,2,3,4,5,6,7,8,9,10), 3)

Tailrec менее читаемый подход O(n)

import scala.annotation.tailrec

def skipTR(xs: List[Int], n: Int) = {
  @tailrec
  def go(ys: List[Int], acc: List[Int], counter: Int): List[Int] = ys match {
    case k::ks=>
      if(counter==1)
        go(ks, k+:acc , n)
      else
        go(ks, acc, counter-1)
    case Nil => acc
  }
  go(xs, List(), n).reverse

}
skipTR(List(1,2,3,4,5,6,7,8,9,10), 3)

Ответ 5

Два подхода, основанные на filter на индексах, следующим образом:

implicit class RichList[A](val list: List[A]) extends AnyVal {

  def nthA(n: Int) = n match {
    case 0 => List()
    case _ => (1 to a.size).filter( _ % n == 0).map { i => list(i-1)}
  }

  def nthB(n: Int) = n match {
    case 0 => List()
    case _ => list.zip(Stream.from(1)).filter(_._2 % n == 0).unzip._1
  }
}

и так для данного списка

val a = ('a' to 'z').toList

имеем, что

a.nthA(5)
res: List(e, j, o, t, y)

a.nthA(123)
res: List()

a.nthA(0)
res: List()

Обновление

Используя List.tabulate следующим образом,

implicit class RichList[A](val list: List[A]) extends AnyVal {
  def nthC(n: Int) = n match {
    case 0 => List()
    case n => List.tabulate(list.size) {i => 
                if ((i+1) % n == 0) Some(list(i)) 
                else None }.flatten
  }
}

Ответ 6

Вы можете опустить toList, если вы не против итератора:

 scala> def skip[A](l:List[A], n:Int) =
      l.grouped(n).filter(_.length==n).map(_.last).toList
 skip: [A](l: List[A], n: Int)List[A]

 scala> skip (l,3)
 res6: List[Int] = List(3, 6, 9)