Skip to content

Instantly share code, notes, and snippets.

@timyates
Created June 27, 2012 09:17
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save timyates/3002724 to your computer and use it in GitHub Desktop.
Save timyates/3002724 to your computer and use it in GitHub Desktop.
match/when implemented with Groovy's GEP-3
// No commas
def a = 'tim'
def nocom = match( a ) {
when 'dave' 'Hi Dave'
when 'tim' 'Hi Tim'
otherwise 'none of the above'
}
assert nocom == 'Hi Tim'
// Commas
a = 'William'
def com = match( a ) {
when 'dave', 'Hi Dave'
when 'tim', 'Hi Tim'
otherwise 'none of the above'
}
assert com == 'none of the above'
// Lists (can't use commas)
a = [1,3]
def list = match( a ) {
when [1,2] 'One and two'
when [1,3] 'One and three'
when [2,1] 'Two and one'
}
assert list == 'One and three'
// Wildcard Lists (can't use commas)
a = [1,2,3]
def wild = match( a ) {
when [1,3,2] 'One, three and two'
when [1,_] 'One and something'
when [1,_,3] 'One, something and three'
when [1,_,_] 'One, something and something'
otherwise 'Something else'
}
assert wild == 'One, something and three'
// Closures as the when/results
String toRoman( int num ) {
match( num ) {
when { it >= 100 } { "C" + toRoman( num - 100 ) }
when { it >= 90 } { "XC" + toRoman( num - 90 ) }
when { it >= 50 } { "L" + toRoman( num - 50 ) }
when { it >= 40 } { "XL" + toRoman( num - 40 ) }
when { it >= 10 } { "X" + toRoman( num - 10 ) }
when { it >= 9 } { "IX" + toRoman( num - 9 ) }
when { it >= 5 } { "V" + toRoman( num - 5 ) }
when { it >= 4 } { "IV" + toRoman( num - 4 ) }
when { it >= 1 } { "I" + toRoman( num - 1 ) }
otherwise ""
}
}
assert "I" == toRoman( 1 )
assert "II" == toRoman( 2 )
assert "IV" == toRoman( 4 )
assert "V" == toRoman( 5 )
assert "VI" == toRoman( 6 )
assert "IX" == toRoman( 9 )
assert "X" == toRoman( 10 )
assert "XVII" == toRoman( 17 )
assert "XXXVIII" == toRoman( 38 )
assert "CCCXCIX" == toRoman( 399 )
///////////////////////////
// Implementation below...
def match( var, c ) {
new Matcher( var:var, closure:c ).match()
}
class Matcher {
def var
Closure closure
List<When> cases = []
Otherwise otherwise
def propertyMissing( name ) {
if( name == '_' ) {
new Any()
}
else {
def w = new When()
cases << w
w
}
}
def when( condition ) { cases << new When( condition:condition ) }
def when( condition, result ) { cases << new When( condition:condition, result:result ) }
def otherwise( result ) { this.otherwise = new Otherwise( result:result ) }
public match() {
closure.delegate = this
closure.resolveStrategy = Closure.DELEGATE_ONLY
closure()
def ret = cases.find {
it.condition instanceof Closure ?
it.condition( var ) :
it.condition == var
}?.result ?: otherwise?.result
ret instanceof Closure ? ret() : ret
}
}
class When {
def condition
def result
def getAt( List a ) { condition = a ; this }
def call( result ) { this.result = result }
def propertyMissing( String result ) { this.result = result }
}
class Otherwise {
def result
}
class Any {
boolean equals( other ) { true }
}
@antsmartian
Copy link

+1 Nice... Will look into this and will try to write a similar one for understanding how it works :D

@antsmartian
Copy link

Can you explain me how the call :

when 'dave' 'Hi Dave' working?

There is a method declaration for when in the class with one parameter condition. And also you have propertyMissing in class When. My question is how does :

when 'dave' 'Hi Dave'

Is calling when method first and then propertyMissing. I'm bit confused!

@timyates
Copy link
Author

timyates commented Aug 2, 2012

@antoaravinth The GEP-3 Groovy parser sees this:

when 'dave' 'Hi Dave'

as:

when( 'dave' ).'Hi Dave'

So it calls propertyMissing on the When object to set the required result :-)
which creates a new When object (adding it to our list)

@antsmartian
Copy link

Oh! That sounds good. Thanks for your reply mate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment