Groovy/FAQ/Ссылки и записи

Материал из Wiki.crossplatform.ru

Перейти к: навигация, поиск
Groovy ·

Содержание

[править] 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>
 
//----------------------------------------------------------------------------------