|
Groovy/FAQ/Ссылки и записи
Материал из Wiki.crossplatform.ru
[править] Introduction
//----------------------------------------------------------------------------------
// 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]
//----------------------------------------------------------------------------------
[править] Taking References to Arrays
//----------------------------------------------------------------------------------
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
//----------------------------------------------------------------------------------
// 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]]
//----------------------------------------------------------------------------------
[править] Taking References to Hashes
//----------------------------------------------------------------------------------
// 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]
//----------------------------------------------------------------------------------
[править] Taking References to Functions
//----------------------------------------------------------------------------------
// 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.
//----------------------------------------------------------------------------------
[править] Taking References to Scalars
//----------------------------------------------------------------------------------
// 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]]
//----------------------------------------------------------------------------------
[править] Creating Arrays of Scalar References
//----------------------------------------------------------------------------------
// 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]
//----------------------------------------------------------------------------------
[править] Using Closures Instead of Objects
//----------------------------------------------------------------------------------
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
//----------------------------------------------------------------------------------
[править] Creating References to Methods
//----------------------------------------------------------------------------------
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
//----------------------------------------------------------------------------------
[править] Constructing Records
//----------------------------------------------------------------------------------
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(', ')}"
}
//----------------------------------------------------------------------------------
[править] Reading and Writing Hash Records to Text Files
//----------------------------------------------------------------------------------
// 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"]
//----------------------------------------------------------------------------------
[править] Printing Data Structures
//----------------------------------------------------------------------------------
// 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]
//----------------------------------------------------------------------------------
[править] Copying Data Structures
//----------------------------------------------------------------------------------
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
//----------------------------------------------------------------------------------
[править] Storing Data Structures to Disk
//----------------------------------------------------------------------------------
// use Java's serialization capabilities as per 11.10
//----------------------------------------------------------------------------------
[править] Transparently Persistent Data Structures
//----------------------------------------------------------------------------------
// 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
//----------------------------------------------------------------------------------
[править] Program: Binary Trees
//----------------------------------------------------------------------------------
// 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>
//----------------------------------------------------------------------------------
|