Functional way to write while loop with mutable variable

时间:2017-06-12 16:53:15

标签: scala functional-programming

I need to obtain a collection that should be filled with datetime intervals between some input and LocalDatetime.now.

Example:

If I would to build a interval based on every 30 minutes, it should be something like 2017-06-12 00:30, 2017-06-12 01:00, 2017-06-12 01:30, 2017-06-12 02:00..and go on.

I've wrote a code that does my requirement but it looks like Java using Scala syntax - not a functional and cleaner way.

  val now = LocalDateTime.now
  val dates = if (interval.toInt > 0) {
    var tempDate = LocalDate.now.atStartOfDay()
    val allDates = scala.collection.mutable.ListBuffer[String]()
    while (tempDate.isBefore(now)) {
      allDates += tempDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
      tempDate = tempDate.plusMinutes(interval.toInt)
    }
    allDates
  } else {
    var initialDay = LocalDate.now.minusDays(30)
    val allDates = scala.collection.mutable.ListBuffer[String]()
    while (initialDay.isBefore(LocalDate.now)) {
      allDates += initialDay.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))
      initialDay = initialDay.plusDays(1)
    }
    allDates
  }

How can I refactor this code to looks like functional style or at least remove that mutable variable?

1 个答案:

答案 0 :(得分:6)

You should use Scala's Stream type to build a lazy stream from the start date in half-hour increments, and then lazily take only as many dates from that which are earlier than the stop date. E.g.:

def halfHoursFrom(start: LocalDateTime): Seq[LocalDateTime] =
  Stream
    .iterate(start)(_.plusMinutes(30))
    .takeWhile(_.isBefore(LocalDateTime.now))

You will find the documentation for the Stream type in the standard library reference. The two notable methods we're using here are:

  • Stream.iterate (in the Stream companion object): builds an infinite stream lazily from a starting value and an increment function
  • Stream#takeWhile (in the Stream class): takes only as many elements from the given stream as match the given predicate (function which tests each element and returns true/false).

Note that I'm upcasting the return type to Seq[LocalDateTime] to hide the implementation details from the caller.