Scala - как обойти список функционально - PullRequest
0 голосов
/ 22 сентября 2018

Мне нужно перебрать список и сравнить предыдущий элемент списка с текущим.Исходя из традиционного опыта программирования, я использовал цикл for с переменной для хранения предыдущего значения, но в Scala я считаю, что должен быть лучший способ сделать это.

for (item <- dataList)
{
   // Code to compare previous list element
   prevElement = item
}

Приведенный выше код работает и дает правильные результатыно я хотел изучить функциональный способ написания того же кода.

Ответы [ 3 ]

0 голосов
/ 22 сентября 2018

Если вы хотите просто сравнить два последовательных элемента и получить логический результат:

val list = Seq("one", "two", "three", "one", "one")
val result = list.sliding(2).collect { case Seq(a,b) => a.equals(b)}.toList

Предыдущий даст вам этот

Список (false, false, false, true)

Альтернативой предыдущему является то, что вы можете получить значение:

val result = list.sliding(2).collect { 
  case Seq(a,b) => if (a.equals(b)) Some(a) else None
  case _ => None
}.toList.filter(_.isDefined).map(_.get)

Это даст следующее:

Список (один)

Но если вы просто хотите сравнить элементы в списке, можно сделать что-то вроде этого:

val list = Seq("one", "two", "three", "one")

for {
  item1 <- list
  item2 <- list
  _ <- Option(println(item1 + ","+ item2))
} yield ()

ThisРезультаты:

one,one
one,two
one,three
one,one
two,one
two,two
two,three
two,one
three,one
three,two
three,three
three,one
one,one
one,two
one,three
one,one
0 голосов
/ 24 сентября 2018

Если вы не изменяете список и пытаетесь сравнить два элемента рядом друг с другом.Это может помочь вам.Это функционально.

def cmpPrevElement[T](l: List[T]): List[(T,T)] = {
  (l.indices).sliding(2).toList.map(e => (l(e.head), l(e.tail.head)))
}
0 голосов
/ 22 сентября 2018

Существует множество возможных решений.Возможно, наиболее общим является использование вспомогательной функции:

// This example finds the minimum value (the hard way) in a list of integers. It's
// not looking at the previous element, as such, but you can use the same approach.
// I wanted to make the example something meaningful.
def findLowest(xs: List[Int]): Option[Int] = {

  @tailrec // Guarantee tail-recursive implementation for safety & performance.
  def helper(lowest: Int, rem: List[Int]): Int = {

    // If there are no elements left, return lowest found.
    if(rem.isEmpty) lowest

    // Otherwise, determine new lowest value for next iteration. (If you need the
    // previous value, you could put that here.)
    else {
      val newLow = Math.min(lowest, rem.head)
      helper(newLow, rem.tail)
    }
  }

  // Start off looking at the first member, guarding against an empty list.
  xs match {
    case x :: rem => Some(helper(x, rem))
    case _ => None
  }
}

В этом конкретном случае ее можно заменить на fold.

def findLowest(xs: List[Int]): Option[Int] = xs match {
  case h :: _ => Some(xs.fold(h)((b, m) => Math.min(b, m)))
  case _ => None
}

, которую можно упростить допросто:

def findLowest(xs: List[Int]): Option[Int] = xs match {
  case h :: _ => Some(xs.foldLeft(h)(Math.min))
  case _ => None
}

В этом случае мы используем аргумент zero операции fold для хранения минимального значения.

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

Обновление : ОК, так что, посмотрев ваш комментарий о датах, вотболее конкретная версия.В этом примере я использовал DateRange (который вам нужно будет заменить на ваш фактический тип).Вам также нужно будет определить, что overlap и merge нужно сделать.Но вот ваше решение:

// Determine whether two date ranges overlap. Return true if so; false otherwise.
def overlap(a: DateRange, b: DateRange): Boolean = { ... }

// Merge overlapping a & b date ranges, return new range.
def merge(a: DateRange, b: DateRange): DateRange = { ... }

// Convert list of date ranges into another list, in which all consecutive,
// overlapping ranges are merged into a single range.
def mergeDateRanges(dr: List[DateRange]): List[DateRange] = {

  // Helper function. Builds a list of merged date ranges.
  def helper(prev: DateRange, ret: List[DateRange], rem: List[DateRange]):
  List[DateRange] = {

    // If there are no more date ranges to process, prepend the previous value
    // to the list that we'll return.
    if(rem.isEmpty) prev :: ret

    // Otherwise, determine if the previous value overlaps with the current
    // head value. If it does, create a new previous value that is the merger
    // of the two and leave the returned list alone; if not, prepend the
    // previous value to the returned list and make the previous value the
    // head value.
    else {
      val (newPrev, newRet) = if(overlap(prev, rem.head)) {
        (merge(prev, rem.head), ret)
      }
      else (rem.head, prev :: ret)

      // Next iteration of the helper (I missed this off, originally, sorry!)
      helper(newPrev, newRet, rem.tail)
    }
  }

  // Start things off, trapping empty list case. Because the list is generated by pre-pending elements, we have to reverse it to preserve the order.
  dr match {
    case h :: rem => helper(h, Nil, rem).reverse // <- Fixed bug here too...
    case _ => Nil
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...