2013-09-15 2 views
2

Я хотел написать его функционально, и лучшее, что я мог сделать, это:Scala фильтр в списке по индексу

list.zipWithIndex.filter((tt:Tuple2[Thing,Int])=>(tt._2%3==0)).unzip._1 

получить элементы 0, 3, 6, ...

Есть более читаемая идиома Scala для этого?

+0

Как вы определяете функционал? Для понимания приемлемы? Потоки? Итераторы? Или вы имеете в виду * только * функции комбинатора списка? – itsbruce

+0

Я просто пытаюсь исключить процедурные ответы. Мои главные проблемы здесь - читаемость и лаконичность. – user833970

+0

Тогда я думаю, что метод индексов om-nom-nom и первое, наивное для понимания в моем ответе оба квалифицируются. – itsbruce

ответ

8

Если эффективность не является проблемой, вы можете сделать следующее:

list.grouped(3).map(_.head) 

Обратите внимание, что это создает промежуточные списки.

В качестве альтернативы вы можете использовать для-понимания:

for { 
    (x,i) <- list zipWithIndex 
    if i % 3 == 0 
} yield x 

Это, конечно, почти идентичен исходному раствору, просто написано по-разному.

Моя последняя альтернатива для вас является использование скапливаться на архивный список:

list.zipWithIndex.collect { 
    case (x,i) if i % 3 == 0 => x 
} 
5

Не очень понятно, но все-таки:

xs.indices.collect { case i if i % 3 == 0 => xs(i) } 
+4

Возможно, вы захотите добавить, что индексированный доступ должен быть «O (1)», иначе это не будет выполнять «O (n)». – gzm0

1

Clojure имеет take-nth функцию, которая делает то, что вы хотите, но я с удивлением обнаружил, что есть не эквивалентный метод в Scala. Вы можете написать код аналогичный рекурсивное решение, основанное от кода Clojure, или вы могли бы прочитать это сообщение в блоге:

Scala collections: Filtering each n-th element

Автор действительно имеет хороший график в конце, показывающий относительную эффективность каждого из его решений ,

0

Я хотел бы сделать это как в Octave математической программы.

val indices = 0 until n by 3 // Range 0,3,6,9 ... 

, а затем мне нужно было как-то выбрать индексы из коллекции. Очевидно, мне пришлось иметь коллекцию с произвольным доступом O (1). Как Array или Vector. Например, здесь я использую Vector. Для того, чтобы обернуть доступ в хороший DSL я бы добавить неявный класс:

implicit class VectorEnrichedWithIndices[T](v:Vector[T]) { 
    def apply(indices:TraversableOnce[Int]):Vector[T] = { 
    // some implementation 
    indices.toVector.map(v) 
    } 
} 

Использование будет выглядеть:

val vector = list.toVector 
val every3rdElement = vector(0 until vector.size by 3) 
0

Ах, как об этом?

val l = List(10,9,8,7,6,5,4,3,2,1,0) 
for (i <- (0 to l.size - 1 by 3).toList) yield l(i) 
//res0: List[Int] = List(10, 7, 4, 1) 

, которые могут быть сделаны более общим по

def seqByN[A](xs: Seq[A], n: Int): Seq[A] = for (i <- 0 to xs.size - 1 by n) yield xs(i) 

scala> seqByN(List(10,9,8,7,6,5,4,3,2,1,0), 3) 
res1: Seq[Int] = Vector(10,7,4,1) 

scala> seqByN(List(10,9,8,7,6,5,4,3,2,1,0), 3).toList 
res2: Seq[Int] = List(10,7,4,1) 

scala> seqByN(List[Int](), 3) 
res1: Seq[Int] = Vector() 

Но функциональной вы имеете в виду только с помощью различных функций Список комбинаторов? В противном случае, потоки достаточно функциональны?

def fromByN[A](xs: List[A], n: Int): Stream[A] = if (xs.isEmpty) Stream.empty else 
    xs.head #:: fromByN(xs drop n, n) 

scala> fromByN(List(10,9,8,7,6,5,4,3,2,1,0), 3).toList 
res17: List[Int] = List(10, 7, 4, 1) 
5

Приятное, функциональное решение, без создания временных векторов, списков, и так далее:

def everyNth[T](xs: List[T], n:Int): List[T] = xs match { 
    case hd::tl => hd::everyNth(tl.drop(n-1), n) 
    case Nil => Nil 
} 
Смежные вопросы