//---------------------------------------------------------------------------------- // In Groovy, most usages of names are references (there are some special // rules for the map shorthand notation and builders). // Objects are inherently anonymous, they don't know what names refer to them. ref = 3 // points ref to an Integer object with value 3. println ref // prints the value that the name ref refers to. myList = [3, 4, 5] // myList is a name for this list anotherRef = myList myMap = ["How": "Now", "Brown": "Cow"] // myMap is a name for this map anArray = [1, 2, 3] as int[] // creates an array of three references to Integer objects list = [[]] // a list containing an empty list list[2] = 'Cat' println list // => [[], null, "Cat"] list[0][2] = 'Dog' println list // => [[null, null, "Dog"], null, "Cat"] a = [2, 1] b = a // b is a reference to the same thing as a a.sort() println b // => [1, 2] nat = [ Name: "Leonhard Euler", Address: "1729 Ramanujan Lane\nMathworld, PI 31416", Birthday: 0x5bb5580 ] println nat // =>["Address":"1729 Ramanujan Lane\nMathworld, PI 31416", "Name":"Leonhard Euler", "Birthday":96163200] //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- aref = myList anonList = [1, 3, 5, 7, 9] anonCopy = anonList implicitCreation = [2, 4, 6, 8, 10] anonList += 11 println anonList // => [1, 3, 5, 7, 9, 11] two = implicitCreation[0] assert two == 2 // To get the last index of a list, you can use size() // but you never would lastIdx = aref.size() - 1 // Normally, though, you'd use an index of -1 for the last // element, -2 for the second last, etc. println implicitCreation[-1] //=> 10 // And if you were looping through (and not using a list closure operator) (0..<aref.size()).each{ /* do something */ } numItems = aref.size() assert anArray instanceof int[] assert anArray.class.isArray() println anArray myList.sort() // sort is in place. myList += "an item" // append item def createList() { return [] } aref1 = createList() aref2 = createList() // aref1 and aref2 point to different lists. println anonList[4] // refers to the 4th item in the list_ref list. // The following two statements are equivalent and return up to 3 elements // at indices 3, 4, and 5 (if they exist). x = anonList[3..5] x = anonList[(3..5).step(1)] // This will insert 3 elements, overwriting elements at indices 3,4, or 5 - if they exist. anonList[3..5] = ["blackberry", "blueberry", "pumpkin"] // non index-based looping for (item in anonList) println item anonList.each{ println it } // index-based looping (0..<anonList.size()).each{ idx -> println anonList[idx] } for (idx in 0..<anonList.size()) println anonList[idx] //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // Making Hashes of Arrays hash = [:] // empty map hash["KEYNAME"] = "new value" hash.each{ key, value -> println key + ' ' + value } hash["a key"] = [3, 4, 5] values = hash["a key"] hash["a key"] += 6 println hash // => ["KEYNAME":"new value", "a key":[3, 4, 5, 6]] // attempting to access a value for a key not in the map yields null assert hash['unknown key'] == null assert hash.get('unknown key', 45) == 45 println hash // => ["unknown key":45, "KEYNAME":"new value", "a key":[3, 4, 5, 6]] //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // Hashes are no different to other objects myHash = [ key1:100, key2:200 ] myHashCopy = myHash.clone() value = myHash['key1'] value = myHash.'key1' slice = myHash[1..3] keys = myHash.keySet() assert myHash instanceof Map [myHash, hash].each{ m -> m.each{ k, v -> println "$k => $v"} } // => // key1 => 100 // key2 => 200 // unknown key => 45 // KEYNAME => new value // a key => [3, 4, 5, 6] values = ['key1','key2'].collect{ myHash[it] } println values // => [100, 200] for (key in ["key1", "key2"]) { myHash[key] += 7 } println myHash // => ["key1":107, "key2":207] //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // you can use closures or the &method notation def joy() { println 'joy' } def sullen() { println 'sullen' } angry = { println 'angry' } commands = [happy: this.&joy, sad: this.&sullen, done: { System.exit(0) }, mad: angry ] print "How are you?" cmd = System.in.readLine() if (cmd in commands.keySet()) commands[cmd]() else println "No such command: $cmd" // a counter of the type referred to in the original cookbook // would be implemented using a class def counterMaker(){ def start = 0 return { -> start++; start-1 } } counter = counterMaker() 5.times{ print "${counter()} " }; println() counter1 = counterMaker() counter2 = counterMaker() 5.times{ println "${counter1()} " } println "${counter1()} ${counter2()}" //=> 0 //=> 1 //=> 2 //=> 3 //=> 4 //=> 5 0 def timestamp() { def start = System.currentTimeMillis() return { (System.currentTimeMillis() - start).intdiv(1000) } } early = timestamp() //sleep(10000) later = timestamp() sleep(2000) println "It's been ${early()} seconds since early." println "It's been ${later()} seconds since later." //=> It's been 12 seconds since early. //=> It's been 2 seconds since later. //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // All variables in Groovy are objects including primitives. Some objects // are immutable. Some operations on objects change mutable objects. // Some operations produce new objects. // 15 is an Integer which is an immutable object. // passing 15 to a method passes a reference to the Integer object. def print(n) { println "${n.toString()}" } print(15) // no need to create any kind of explicit reference // even though Integers are immutable, references to them are not x = 1 y = x println "$x $y" // => 1 1 x += 1 // "x" now refers to a different object than y println "$x $y" // => 2 1 y = 4 // "y" now refers to a different object than it did before println "$x $y" // => 2 4 // Some objects (including ints and strings) are immutable, however, which // can give the illusion of a by-value/by-reference distinction: list = [[1], 1, 's'] list.each{ it += 1 } // plus operator doesn't operate inplace print list //=> [[1] 1 s] list = list.collect{ it + 1 } print list //=> [[1, 1], 2, s1] list = [['Z', 'Y', 'X'], ['C', 'B', 'A'], [5, 3, 1]] list.each{ it.sort() } // sort operation operates inline println list // => [["X", "Y", "Z"], ["A", "B", "C"], [1, 3, 5]] //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // As indicated by the previous section, everything is referenced, so // just create a list as normal, and beware that augmented assignment // works differently with immutable objects to mutable ones and depends // on the semantics of the particular operation invoked: mylist = [1, "s", [1]] print mylist //=> [1, s, [1]] mylist.each{ it *= 2 } print mylist //=> [1, s, [1,1]] mylist[0] *= 2 mylist[-1] *= 2 print mylist //=> [2, s, [1, 1]] // If you need to modify every value in a list, you use collect // which does NOT modify inplace but rather returns a new collection: mylist = 1..4 println mylist.collect{ it**3 * 4/3 * Math.PI } // => [4.188790204681671, 33.510321638395844, 113.09733552923255, 268.0825731062243] //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- def mkcounter(count) { def start = count def bundle = [:] bundle.'NEXT' = { count += 1 } bundle.'PREV' = { count -= 1 } bundle.'RESET' = { count = start } bundle["LAST"] = bundle["PREV"] return bundle } c1 = mkcounter(20) c2 = mkcounter(77) println "next c1: ${c1["NEXT"]()}" // 21 println "next c2: ${c2["NEXT"]()}" // 78 println "next c1: ${c1["NEXT"]()}" // 22 println "last c1: ${c1["PREV"]()}" // 21 println "last c1: ${c1["LAST"]()}" // 20 println "old c2: ${c2["RESET"]()}" // 77 //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- def addAndMultiply(a, b) { println "${a+b} ${a*b}" } methRef = this.&addAndMultiply // or use direct closure multiplyAndAdd = { a,b -> println "${a*b} ${a+b}" } // later ... methRef(2,3) // => 5 6 multiplyAndAdd(2,3) // => 6 5 //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- record = [ "name": "Jason", "empno": 132, "title": "deputy peon", "age": 23, "salary": 37000, "pals": ["Norbert", "Rhys", "Phineas"], ] println "I am ${record.'name'}, and my pals are ${record.'pals'.join(', ')}." // => I am Jason, and my pals are Norbert, Rhys, Phineas. byname = [:] byname[record["name"]] = record rp = byname.get("Aron") if (rp) println "Aron is employee ${rp["empno"]}." byname["Jason"]["pals"] += "Theodore" println "Jason now has ${byname['Jason']['pals'].size()} pals." byname.each{ name, record -> println "$name is employee number ${record['empno']}." } employees = [:] employees[record["empno"]] = record // lookup by id rp = employees[132] if (rp) println "Employee number 132 is ${rp.'name'}." byname["Jason"]["salary"] *= 1.035 println record // => ["pals":["Norbert", "Rhys", "Phineas", "Theodore"], "age":23, // "title":"deputy peon", "name":"Jason", "salary":38295.000, "empno":132] peons = employees.findAll{ k, v -> v.'title' =~ /(?i)peon/ } assert peons.size() == 1 tsevens = employees.findAll{ k, v -> v.'age' == 27 } assert tsevens.size() == 0 // Go through all records println 'Names are: ' + employees.values().collect{r->r.'name'}.join(', ') byAge = {a,b-> a.value().'age' <=> b.value().'age'} employees.values().sort{byAge}.each{ r-> println "${r.'name'} is ${r.'age'}" } // byage, a hash: age => list of records byage = [:] byage[record["age"]] = byage.get(record["age"], []) + [record] byage.each{ age, list -> println "Age $age: ${list.collect{it.'name'}.join(', ')}" } //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // if you are using a Properties (see 8.16) then just use load // and store (or storeToXML) // variation to original cookbook as Groovy can use Java's object serialization map = [1:'Jan', 2:'Feb', 3:'Mar'] // write new File('months.dat').withObjectOutputStream{ oos -> oos.writeObject(map) } // reset map = null // read new File('months.dat').withObjectInputStream{ ois -> map = ois.readObject() } println map // => [1:"Jan", 2:"Feb", 3:"Mar"] //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // Groovy automatically does pretty printing for some of the key types, e.g. mylist = [[1,2,3], [4, [5,6,7], 8,9, [0,3,5]], 7, 8] println mylist // => [[1, 2, 3], [4, [5, 6, 7], 8, 9, [0, 3, 5]], 7, 8] mydict = ["abc": "def", "ghi":[1,2,3]] println mydict // => ["abc":"def", "ghi":[1, 2, 3]] // if you have another type of object you can use the built-in dump() method class PetLover { def name def age def pets } p = new PetLover(name:'Jason', age:23, pets:[dog:'Rover',cat:'Garfield']) println p // => PetLover@b957ea println p.dump() // => <PetLover@b957ea name=Jason age=23 pets=["cat":"Garfield", "dog":"Rover"]> // If that isn't good enough, you can use Boost (http://tara-indigo.org/daisy/geekscape/g2/128) // or Jakarta Commons Lang *ToStringBuilders (jakarta.apache.org/commons) // Here's an example of Boost, just extend the supplied Primordial class import au.net.netstorm.boost.primordial.Primordial class PetLover2 extends Primordial { def name, age, pets } println new PetLover2(name:'Jason', age:23, pets:[dog:'Rover',cat:'Garfield']) // => // PetLover2[ // name=Jason // age=23 // pets={cat=Garfield, dog=Rover} // metaClass=groovy.lang.MetaClassImpl@1d8d39f[class PetLover2] // ] // using Commons Lang ReflectionToStringBuilder (equivalent to dump()) import org.apache.commons.lang.builder.* class PetLover3 { def name, age, pets String toString() { ReflectionToStringBuilder.toString(this) } } println new PetLover3(name:'Jason', age:23, pets:[dog:'Rover',cat:'Garfield']) // => PetLover3@196e136[name=Jason,age=23,pets={cat=Garfield, dog=Rover}] // using Commons Lang ToStringBuilder if you want a custom format class PetLover4 { def name, dob, pets String toString() { def d1 = dob.time; def d2 = (new Date()).time int age = (d2 - d1)/1000/60/60/24/365 // close approx good enough here return new ToStringBuilder(this). append("Pet Lover's name", name). append('Pets', pets). append('Age', age) } } println new PetLover4(name:'Jason', dob:new Date(83,03,04), pets:[dog:'Rover',cat:'Garfield']) // => PetLover4@fdfc58[Pet Lover's name=Jason,Pets={cat=Garfield, dog=Rover},Age=23] //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- oldlist = [1, 2, 3] newlist = new ArrayList(oldlist) // shallow copy newlist = oldlist.clone() // shallow copy oldmap = [a:1, b:2, c:3] newmap = new HashMap(oldmap) // shallow copy newmap = oldmap.clone() // shallow copy oldarray = [1, 2, 3] as int[] newarray = oldarray.clone() // shallow copies copy a data structure, but don't copy the items in those // data structures so if there are nested data structures, both copy and // original will refer to the same object mylist = ["1", "2", "3"] newlist = mylist.clone() mylist[0] = "0" println "$mylist $newlist" //=> ["0", "2", "3"] ["1", "2", "3"] mylist = [["1", "2", "3"], 4] newlist = mylist.clone() mylist[0][0] = "0" println "$mylist $newlist" //=> [["0", "2", "3"], 4] [["0", "2", "3"], 4] // standard deep copy implementation def deepcopy(orig) { bos = new ByteArrayOutputStream() oos = new ObjectOutputStream(bos) oos.writeObject(orig); oos.flush() bin = new ByteArrayInputStream(bos.toByteArray()) ois = new ObjectInputStream(bin) return ois.readObject() } newlist = deepcopy(oldlist) // deep copy newmap = deepcopy(oldmap) // deep copy mylist = [["1", "2", "3"], 4] newlist = deepcopy(mylist) mylist[0][0] = "0" println "$mylist $newlist" //=> [["0", "2", "3"], 4] [["1", "2", "3"], 4] // See also: // http://javatechniques.com/public/java/docs/basics/low-memory-deep-copy.html // http://javatechniques.com/public/java/docs/basics/faster-deep-copy.html //---------------------------------------------------------------------------------- |
//----------------------------------------------------------------------------------
// use Java's serialization capabilities as per 11.10
//----------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------- // There are numerous mechanisms for persisting objects to disk // using Groovy and Java mechanisms. Some are completely transparent, // some require some initialization only, others make the persistence // mechanisms visible. Here is a site that lists over 20 options: // http://www.java-source.net/open-source/persistence // (This list doesn't include EJB offerings which typically // require an application server or XML-based options) // We'll just consider one possibility from prevayler.sf.net. // This package doesn't make changes to persistent data transparent; // instead requiring an explicit call via a transaction object. // It saves all such transaction objects in a journal file so // that it can rollback the system any number of times (or if // you make use of the timestamp feature) to a particular point // in time. It can also be set up to create snapshots which // consolidate all changes made up to a certain point. The // journalling will begin again from that point. import org.prevayler.* class ImportantHash implements Serializable { private map = [:] def putAt(key, value) { map[key] = value } def getAt(key) { map[key] } } class StoreTransaction implements Transaction { private val StoreTransaction(val) { this.val = val } void executeOn(prevayler, Date ignored) { prevayler.putAt(val,val*2) } } def save(n){ store.execute(new StoreTransaction(n)) } store = PrevaylerFactory.createPrevayler(new ImportantHash(), "pleac11") hash = store.prevalentSystem() for (i in 0..1000) { save(i) } println hash[750] // => 1500 store = null; hash = null // *** could shutdown here store = PrevaylerFactory.createPrevayler(new ImportantHash(), "pleac11") hash = store.prevalentSystem() println hash[750] // => 1500 //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // bintree - binary tree demo program class BinaryTree { def value, left, right BinaryTree(val) { value = val left = null right = null } // insert given value into proper point of // provided tree. If no tree provided, // use implicit pass by reference aspect of @_ // to fill one in for our caller. def insert(val) { if (val < value) { if (left) left.insert(val) else left = new BinaryTree(val) } else if (val > value) { if (right) right.insert(val) else right = new BinaryTree(val) } else println "double" // ignore double values } // recurse on left child, // then show current value, // then recurse on right child. def inOrder() { if (left) left.inOrder() print value + ' ' if (right) right.inOrder() } // show current value, // then recurse on left child, // then recurse on right child. def preOrder() { print value + ' ' if (left) left.preOrder() if (right) right.preOrder() } // show current value, // then recurse on left child, // then recurse on right child. def dumpOrder() { print this.dump() + ' ' if (left) left.dumpOrder() if (right) right.dumpOrder() } // recurse on left child, // then recurse on right child, // then show current value. def postOrder() { if (left) left.postOrder() if (right) right.postOrder() print value + ' ' } // find out whether provided value is in the tree. // if so, return the node at which the value was found. // cut down search time by only looking in the correct // branch, based on current value. def search(val) { if (val == value) { return this.dump() } else if (val < value) { return left ? left.search(val) : null } else { return right ? right.search(val) : null } } } // first generate 20 random inserts test = new BinaryTree(500) rand = new Random() 20.times{ test.insert(rand.nextInt(1000)) } // now dump out the tree all three ways print "Pre order: "; test.preOrder(); println "" print "In order: "; test.inOrder(); println "" print "Post order: "; test.postOrder(); println "" println "\nSearch?" while ((item = System.in.readLine()?.trim()) != null) { println test.search(item.toInteger()) println "\nSearch?" } // Randomly produces a tree such as: // -------- 500 ------ // / \ // 181 847 // / \ / \ // 3 204 814 970 // \ / \ / // 126 196 414 800 // / \ / // 353 438 621 // / / \ // 423 604 776 // / / // 517 765 // / // 646 // / // 630 // Pre order: // 500 181 3 126 204 196 414 353 438 423 847 814 800 621 604 517 776 765 646 630 970 // In order: // 3 126 181 196 204 353 414 423 438 500 517 604 621 630 646 765 776 800 814 847 970 // Post order: // 126 3 196 353 423 438 414 204 181 517 604 630 646 765 776 621 800 814 970 847 500 // // Search? // 125 // null // // Search? // 126 // <BinaryTree@ae97c4 value=126 left=null right=null> //---------------------------------------------------------------------------------- |