2015-02-19 4 views
2

Учитывая последовательность Scala ...Collapse подпоследовательность согласующих элементов в последовательности Scala

val sequence: Seq = List(3, 1, 4, 1, 5, 9, 2, 6, 5) 

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

val sequence2: Seq = List(2, 4, 3, 2, 6, 1) 

(Да, это довольно надуманный пример, но лаконичным.)

до сих пор лучшее, что я был в состоянии сделать это некрасиво императив хак:

val sequence: Seq[Int] = List(3, 1, 4, 1, 5, 9, 2, 6, 5) 

var sequence2 = List[Int]() // this is going to be our result 
var subsequence = List[Int]() 

for (s <- sequence) { 
    if (s % 2 == 0) { 
    if (!subsequence.isEmpty) { 
     sequence2 = sequence2 :+ subsequence.length 
     subsequence = List[Int]() 
    } 
    sequence2 = sequence2 :+ s 
    } else { 
    subsequence = subsequence :+ s 
    } 
} 

if (!subsequence.isEmpty) { 
    sequence2 = sequence2 :+ subsequence.length 
} 

есть элегантный (/ функциональный) способ сделать это?

ответ

3

Использование multiSpan для затягивающего список в подсписков по заданным критериям, рассмотрим это решение задачи, изображенной выше,

sequence.multiSpan(_ % 2 == 0).flatMap { 
    case h :: xs if h % 2 != 0 => List((h::xs).length) 
    case h :: Nil => List(h) 
    case h :: xs => List(h, xs.length) } 

Обратите внимание, что

sequence.multiSpan(_ % 2 == 0) 
List(List(3, 1), List(4, 1, 5, 9), List(2), List(6, 5)) 

и поэтому мы вставляем эти вложенные списки, рассматривая три случая: не выполняется ли условие и поэтому мы применяем функцию; является ли он одиночным списком (условие выполняется); или же выполняется ли первый элемент, а остальная часть - функция.

2

Что вы ищете является fold:

sequence.foldLeft(List(0)) { (soFar, next) => 
    if(next % 2 == 0) soFar :+ next :+ 0 else soFar.init :+ (soFar.last + 1) 
}.filter(_ != 0) 

Или в другом стиле:

(List(0) /: sequence) { 
    case(soFar, next) if next % 2 == 0 => soFar :+ next :+ 0 
    case(soFar, _) => soFar.init :+ (soFar.last + 1) 
}.filter(_ != 0) 

Или с foldRight вместо, который иногда более производительным:

(sequence :\ List(0)) { 
    case(next, soFar) if next % 2 == 0 => 0 :: next :: soFar 
    case(_, hd::tl) => (hd + 1)::tl 
}.filter(_ != 0).reverse 

Вы можете узнать больше о fold, foldLeft, foldRight и другие полезные функции here и here.

Я изначально думал, что вы просите все подпоследовательности последовательности. Это может быть полезно в подобных ситуациях, поэтому я оставлю это здесь. Вы можете использовать inits и tails вместе, чтобы получить все подпоследовательности, а затем использовать filter и map для ваших целей:

val sequence = List(3, 1, 4, 1, 5, 9, 2, 6, 5) 
val subsequences = sequence.tails.flatMap(_.inits).toList.distinct 
subsequences.filter(_.forall(_ % 2 == 1)).map(_.length) 
+0

Это дает мне «Список (2, 1, 0, 1, 3, 2, 2, 1, 1)». Я думаю, что «все подпоследовательности» более комбинаторны, чем то, что мне нужно. –

+0

Я полностью не понял ваш вопрос. Я получаю это сейчас. Дай мне минуту. –

1

Вот моя попытка рекурсивной реализации

def subSequenceApply(list: List[Int], predicate: (Int)=> Boolean, func: (List[Int]) => Int):List[Int] = list match { 
    case Nil => Nil 
    case h :: t if !predicate(h) => h :: subSequenceApply(t, predicate, func) 
    case _ => 
    val (matchSeq,nonMatch) = list.span(predicate) 
    func(matchSeq) :: subSequenceApply(nonMatch, predicate, func) 
} 

Поэтому, учитывая последовательность в вашем примере , Вы можете запустить его как

subSequenceApply(sequence, _ % 2 != 0, _.length)