Как добавить значения выборочных строк из списка в функциональном стиле? - PullRequest
4 голосов
/ 18 февраля 2011

Я решил свою проблему в императивном стиле, но это выглядит очень некрасиво. Как я могу сделать это лучше (более элегантно, более кратко, более функционально - наконец, это Scala). Строки с теми же значениями, что и в предыдущей строке, но с другой буквой, должны быть пропущены, все остальные значения строк должны быть добавлены.

val row1 = new Row(20, "A", true)    // add value  
val row2 = new Row(30, "A", true)    // add value  
val row3 = new Row(40, "A", true)    // add value  
val row4 = new Row(40, "B", true)    // same value as the previous element & different letter -> skip row  
val row5 = new Row(60, "B", true)    // add value  
val row6 = new Row(70, "B", true)    // add value  
val row7 = new Row(70, "B", true)    // same value as the previous element, but the same letter -> add value  

val rows = List(row1, row2, row3, row4, row5, row6, row7)  

var previousLetter = " "    
var previousValue  = 0.00  
var countSkip = 0  

for (row <- rows) {  
  if (row.value == previousValue && row.letter != previousLetter) {  
      row.relevant = false  
      countSkip += 1  
  }  
  previousLetter = row.letter  
  previousValue  = row.value  
}  

// get sum  
val sumValue = rows.filter(_.relevant == true).map(_.value) reduceLeftOption(_ + _)  
val sum = sumValue match {  
    case Some(d) => d  
    case None => 0.00  
}  

assert(sum == 290)  
assert(countSkip == 1) 

Заранее спасибо

Твистлтон

Ответы [ 5 ]

9 голосов
/ 18 февраля 2011
(rows.head :: rows).sliding(2).collect{ 
     case List(Row(v1,c1), Row(v2,c2)) if ! (v1 == v2 && c1 != c2) => v2 }.sum
6 голосов
/ 18 февраля 2011

Я думаю, что самое короткое (пуленепробиваемое) решение, когда Row является классом дел (отбрасывая логическое значение), равно

(for ((Row(v1,c1), Row(v2,c2)) <- (rows zip rows.take(1) ::: rows) if (v1 != v2 || c1 == c2)) yield v1).sum

Некоторые другие решения не обрабатывают регистр с пустым списком, ноВо многом это связано с тем, что sliding содержит ошибку, из-за которой он возвращает частичный список, если список слишком короткий.Мне понятнее (а также пуленепробиваемое):

(rows zip rows.take(1) ::: rows).collect{
  case (Row(v1,c1), Row(v2,c2)) if (v1 != v2 || c1 == c2) => v1
}.sum

(что всего на два символа больше, если держать его на одной строке).Если вам нужен пропущенный номер,

val indicated = (rows zip rows.take(1) ::: rows).collect {
  case (Row(v1,c1), Row(v2,c2)) => (v1, v1 != v2 || c1 == c2)
}
val countSkip = indicated.filterNot(_._2).length
val sum = indicated.filter(_._2).map(_._1).sum
3 голосов
/ 18 февраля 2011

Сложите это:

scala> rows.foldLeft((row1, 0))((p:(Row,Int), r:Row) => (r, p._2 + (if (p._1.value == r.value && p._1.letter != r.letter) 0 else r.value)))._2

res2: Int = 290
2 голосов
/ 18 февраля 2011
(new Row(0, " ", true) +: rows).sliding(2).map { case List(r1, r2) => 
  if (r1.value != r2.value || r1.letter == r2.letter) { r2.value } 
  else { 0 } 
}.sum

Конечно, вы можете отбросить логический элемент Row, если он вам не нужен для чего-то другого

1 голос
/ 18 февраля 2011

Уменьшить:

rows.reduceLeft { (prev, curr) =>
  if (prev.value == curr.value && prev.letter != curr.letter) {
    curr.relevant = false
    countSkip += 1
  }
  curr
}
...