[英]Scala - how to loop around list in functional way
我需要遍历一个列表,并将列表中的上一个元素与当前元素进行比较。 从传统的编程背景开始,我使用带变量的循环来保存先前的值,但是对于Scala,我认为应该有一种更好的方法。
for (item <- dataList)
{
// Code to compare previous list element
prevElement = item
}
上面的代码可以正常工作并给出正确的结果,但是我想探索编写相同代码的功能方法。
有许多不同的解决方案。 也许最通用的是使用辅助函数:
// 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
}
在这种情况下,我们使用fold
操作的zero
参数存储最小值。
如果这看起来太抽象了,您可以在循环中发布更多有关您想做的事情的详细信息,我可以在回答中解决这个问题吗?
更新 :好的,因此,看到您对日期的评论后,这里是一个更具体的版本。 在此示例中,我使用了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
}
}
如果您只想比较两个连续的元素并获得布尔结果:
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 ()
结果是:
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
如果您不更改列表,而只是尝试并排比较两个元素。 这可以为您提供帮助。 它是功能性的。
def cmpPrevElement[T](l: List[T]): List[(T,T)] = {
(l.indices).sliding(2).toList.map(e => (l(e.head), l(e.tail.head)))
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.