mfollett
mfollett

Reputation: 1814

Memoizing Member Methods in Groovy

I have a method that is a pure function and takes a while to run. I would like to memoize this method so that subsequent calls are much faster. I can see in Groovy's documentation that you can memoize closures via:

foo = {…}.memoize()

However, I cannot find a way to memoize a member method. Is there a way to do this?

Upvotes: 4

Views: 1992

Answers (2)

tim_yates
tim_yates

Reputation: 171114

Edit:

In Groovy 2.2.X there will be a new AST transform called @Memoized which will do this for you.

import groovy.transform.Memoized

class Woo {
  @Memoized
  def sum( int a, int b ) {
    Thread.sleep( 1000 )
    a + b
  }
}

new Woo().with {
  println new Date()
  println sum( 1, 2 )
  println new Date()
  println sum( 1, 2 )
  println new Date()
}

Original answer

Another option would be to write some sort of AST transformation to allow you to annotate the method with @Memoize, and have the memoization done for you.

I can find a couple of examples, one here for adding Redis as a Memoization cache to Grails, and another here which seems to allow memoization of single argument methods by basically manipulating the AST to look like the second part of epidemian's answer.

As you probably want multiple parameters, I'd go for epidemian's second method. However writing an AST transform might be an interesting experiment/side project?

It's something that if done right, I could see going back into the Groovy core source code as well (for fame and glory) :-)

Upvotes: 7

epidemian
epidemian

Reputation: 19219

I don't know of any direct way of memoizing methods just like you can memoize a Closure.

If the method does not receive any parameters (and therefore the return value is always the same, for it being pure), you may do memoization simply by storing the return value in a member attribute. This technique is very common in Ruby and it usually takes the form of def some_method; @value ||= compute_value; end. Translating that to Groovy, you can do something like:

class Computer {
    private answer
    def getAnswer() {
        answer = answer ?: computeAnswer()
    }
    private computeAnswer() {
        println "Computing the Answer to the Ultimate Question of Life, the Universe, and Everything"
        42
    }
}

def c = new Computer()
println c.answer
println c.answer

The output is:

Computing the Answer to the Ultimate Question of Life, the Universe, and Everything
42
42

So the memoization worked :)

If you don't want to define an extra method, you can also write the getAnswer method like:

def getAnswer() {
    if (answer != null) return answer
    println "Computing the Answer to the Ultimate Question of Life, the Universe, and Everything"
    answer = 42        
}

Now, if the method does receive any parameters, it would be quite cumbersome to implement memoization this way. You can instead call a memoized closure from the merhod you want to memoize:

class Calculator {
    def sum(a, b) {
        memoizedSum(a, b)
    }
    private memoizedSum = { a, b ->
        println "Computing the sum of $a and $b"
        a + b
    }.memoize()
}

def c = new Calculator()
println c.sum(4, 7)
println c.sum(4, 7)
println c.sum(4, 2)

The output is:

Computing the sum of 4 and 7
11
11
Computing the sum of 4 and 2
6

Upvotes: 3

Related Questions