def findTriggerTimesRecursive(expr: CronExpression, after: Date): List[Date] = expr getNextValidTimeAfter after match { case null => Nil case next => next :: findTriggerTimesRecursive(expr, next) }

getNextValidTimeAfter()

null

we don't really know how many future dates client needs so we most likely generate too much, unnecessarily consuming CPU cycles 1





even worse, some Cron expressions never end. "0 0 17 * * ? *" will run at 5 PM every day, every year, to infinity. We definitely don't have that much time and memory





will run at 5 PM every day, every year, to infinity. We definitely don't have that much time and memory our implementation is not tail-recursive. Easy to fix though





Stream[Date]

def findTriggerTimes(expr: CronExpression, after: Date): Stream[Date] = expr getNextValidTimeAfter after match { case null => Stream.Empty case next => next #:: findTriggerTimes(expr, next) }

List[Date]

Stream[Date]

Nil

Stream.Empty

::

#::

#::

tl: => Stream[A]

findTriggerTimes(expr, next)

#::

val triggerTimesStream = findTriggerTimes("0 0 17 L-3W 6-9 ? *") println(triggerTimesStream) //Stream(Thu Jun 27 17:00:00 CEST 2013, ?) val firstThree = triggerTimesStream take 3 println(firstThree.toList) //List(Thu Jun 27 17:00:00 CEST 2013, Mon Jul 29 17:00:00 CEST 2013, Wed Aug 28 17:00:00 CEST 2013) println(triggerTimesStream) //Stream(Thu Jun 27 17:00:00 CEST 2013, Mon Jul 29 17:00:00 CEST 2013, Wed Aug 28 17:00:00 CEST 2013, ?)

Stream.toString

List

take(3)

val hundredth = triggerTimesStream.drop(99).head val calendar = new GregorianCalendar() calendar.add(Calendar.YEAR, 1) val yearFromNow = calendar.getTime val countWithinYear = triggerTimesStream.takeWhile(_ before yearFromNow).size

triggerTimesStream

takeWhile(...).size

filter(...).size

count(...)

Clojure

lazy-seq

(defn find-trigger-times [expr after] (let [next (. expr getNextValidTimeAfter after)] (case next nil [] (cons next (lazy-seq (find-trigger-times expr next))))))

let

getNextValidTimeAfter()

if-let

(defn find-trigger-times [expr after] (if-let [next (. expr getNextValidTimeAfter after)] (cons next (lazy-seq (find-trigger-times expr next))) []))

if-let

next

nil

(def expr (new CronExpression "0 0 17 L-3W 6-9 ? *")) (def trigger-times (find-trigger-times expr (new Date))) (def hundredth (first (drop 99 trigger-times))) (def year-from-now (let [calendar (new GregorianCalendar)] (. calendar add Calendar/YEAR 1) (. calendar getTime))) (take-while #(.before % year-from-now) trigger-times)

take-while

filter

Space and time complexity

filter()

takeWhile()

filter()

Stream

size

Stream

Stream[T]

Stream

val largeStream: Stream[Int] = //,.. //... val smallerStream = largeStream drop 1000000

smallerStream

largeStream

largeStream

iterate

iterate

def infiniteFindTriggerTimes(expr: CronExpression, after: Date) = Stream.iterate(expr getNextValidTimeAfter after){last => expr getNextValidTimeAfter last }

(defn find-trigger-times [expr after] (iterate #(. expr getNextValidTimeAfter %) (. expr getNextValidTimeAfter after)))

x

f

[x, f(x), f(f(x)), f(f(f(x))), ...]

iterate

prime?

(defn- divisors [x] (filter #(zero? (rem x %)) (range 2 (inc (Math/sqrt x))))) (defn- prime? [x] (empty? (divisors x))) (defn- next-prime [after] (loop [x (inc after)] (if (prime? x) x (recur (inc x))))) (def primes (iterate next-prime 2))

next-prime

(next-prime 2)

3

(next-prime 3)

5

primes

next-prime

Conclusion

primes.find(_ == 10)

getNextValidTimeAfter()