简体   繁体   中英

Transform imperative function to be more functional

I found this guy here:

      var time = createdAt
      var coolDown = duration
      while (time <= timestamp) {
        if (time <= timestamp && timestamp <= time + timeLimit.getOrElse(0L)) {
          return None
        } else {
          time = time + timeLimit.getOrElse(0L) + coolDown
          coolDown = 2 * coolDown
        }
      }
      Some("Error")

and wanted to refactor it to be more functional than imperative, but I'm quite new in functional programming and I would like to know your approach of achieving that. I covered it with unit tests and was about to either create a mathematic function or to have a recursive function. Is there a way of doing it step by step? Starting from the scratch? How would you solve it?

The function should return this:

-N-|-C-|-N-|--C(2x)--|-N-|----C(4x)----|-N-|--------C(8x)---------|-N-|...

The times where it returns Error increases exponential. Let's say coolDown and timeLimit are one day on the first day it returns None , on the second day Error , on the third day None , then two days of Error and so on...

Yep, that was easy after the comment by @Carcigenicate. I already started to rewrite it step by step and it went same direction. The result:

def evaluateCooldown(time: Long, coolDown: Long): Option[String] = {
  if (time <= timestamp) {
    if (timestamp <= time + timeLimit.getOrElse(0L)) {
      None
    } else {
      evaluateCooldown(time + timeLimit.getOrElse(0L) + coolDown, 2 * coolDown)
    }
  } else {
    Some("Error")
  }
}
evaluateCooldown(unlockedAt, cdc.duration)

Naturally there are many ways to do that. Here's one using lazy ADTs ...

We define val ts = timestamp.getOrElse(0L)

def slen(n: Int) = math.pow(2, n - 1) * countDown + timeLimit
val prev = Stream.iterate(1)(slen).scan(0)(_ + _).takeWhile(_ < ts).last

to find the length of the list up to the last N|C section before timestamp, then

if (prev + timeLimit > ts) None else Some("Error")

Another optimized approach

We can use

to replace the scan with the cumulative sum

def clen(n: Int) = (math.pow(2, n) - 1) * countDown + n * timeLimit
val nextn = Stream.from(1).find(clen(_) > ts)

to find the index of the first section after the timestamp, then

if (len(nextn - 1) + timeLimit > ts) None else Some("Error")

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM