Table of Contents
~~SLIDESHOW~~
Closures and Files
The slides and notes in this presentation are adapted from Groovy Programming (See Recommended Reading).
An index to the source code for all the examples in this lecture is available.
Closures
Groovy closures are a powerful way of representing blocks of executable code.
- Closures are objects: they can be passed as method parameters
- Closures are code blocks: they can be executed when required
- Closures can have parameters
- Closures can also access any variable that is in scope where the closure is defined.
Closures make processing all elements of a collection very easy.
Defining a closure
{ comma-separated-formal-parameter-list -> statement-list }
If there are no formal parameters, the parameter list and the ' ->
' separator are omitted.
A closure and its invocation
- 1 | Example 1: (at-m42/Examples/lecture06/example1.groovy)
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture06/example1.groovy
Example 1 is a closure with no parameters which consists of a single println
statement. The closure is referenced by the identifier clos
. The code block referenced by the identifier can be executed with the call statement, as shown in the slide. The result is to print the message:
Hello world
Parametrized closure
- 1 | Example 2: Parametrized closure (at-m42/Examples/lecture06/example2.groovy)
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture06/example2.groovy
By introducing formal parameters into closure definitions, we can make them more useful, as we did with methods. On this slide we have the same closure with the name of the individual receiving the greeting as a parameter (line 3). When we execute this script, the output produced is:
Hello world Hello again Hello shortcut
Observe the abbreviated form of the third invocation (line 7) in which call
has been omitted.
Implicit single parameter
- 1 | Example 3: Implicit single parameter (at-m42/Examples/lecture06/example3.groovy)
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture06/example3.groovy
In Groovy, if you don't define a formal parameter, an implicit parameter named it
will be provided for you. In this slide, we repeat Example 2 using this implicit parameter. You should note that it
is available only if the closure has only one formal parameter.
Closures and enclosing scope
- 1 | Example 4: Closures and enclosing scope (at-m42/Examples/lecture06/example4.groovy)
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture06/example4.groovy
State information can be accessed by closures. More formally, this means that any variable in scope when the closure is defined, can be accessed within the body of the closure. This is very powerful feature, but it can also be somewhat confusing!
We illustrate this behaviour in Example 4. Here the variable greeting
defines the salutation. Because greeting
is in scope (line 3) before clos
is defined (line 4), it is this variable that is used when the closure is called at line 5. At this point it has the value 'Hello'
. Before the second call of the closure, the value of greeting
is set to 'Welcome'
(line 8) so the call of the closure at line 9 will result in Welcome world
being printed. When we run the script, the output is as predicted:
Hello world Welcome world
Effect of scope
- 1 | Example 5: Effect of scope (at-m42/Examples/lecture06/example5.groovy)
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture06/example5.groovy
In Example 5, we augment the previous code with a method called demo
. It is passed a single argument, clo
, representing a closure. The method calls the closure with argument 'Chris'
at line 13. The method introduces a new scope in which another variable called greeting
is defined which is bound to the value 'Bonjour'
(line 12). However, when the method is called it generates the output:
Welcome Chris
which indicates that the state accessible to the closure is that which was in scope at the point of definition (that is line 4, modified at line 8) rather than at the point of the call.
The complete output is:
Hello world Welcome world Welcome Chris
A simplification of syntax
When closure is last formal parameter of a method, it can be placed after the parenthesis:
demo(clos) // Closure parameter within the parenthesis demo() clos // Parameter removed from the parenthesis
This can simplify the closure call and make statements using closures easier to read.
In the previous example, the method demo
was called and the actual parameter was the closure. In this case, the closure can be removed from the list of formal parameters and placed immediately after the closing parenthesis as shown in this slide.
Leave the closure outside of the actual argument list
- 1 | Example 6: Leave the closure outside of the actual argument list (at-m42/Examples/lecture06/example6.groovy)
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture06/example6.groovy
We illustrate this new form of closure call in Example 6 in which the closure has been removed from the actual parameters in the call to demo
and the empty parameter list is deleted.
The output is :
Welcome Chris Hello Chris Welcome Chris
The final two program statements (lines 14 and 15) call the method demo
and pass a closure as the actual parameter. The first of these two method calls uses a reference to a closure object, while the second uses a closure literal. In both cases, the parenthesis for the method call are omitted.
Note also the statements at lines 11 and 12 in the example. The second (line 12) uses a closure literal and is acceptable syntax. However, the first (line 11) uses a closure reference and is not successfully indentified as part of the statement. This causes an execution error which reports that the method was passed a null
parameter. In this special case demo(clos)
should have been used instead.
Iterators with closures
Example – Numbers have a method upto
defined as:
void upto(Number to, Closure closue)
Used like this:
More usually, closures are applied to collections. Effectively we can iterate over the elements in the collection and apply a closure to each element. For example, all numeric types support a method called upto
that has the signature shown in the slide. The programmer might call the method as in the slide (line 1) and the closure literal would be called 10 times. If the closure had a formal parameter i
as shown in line 2, then on each iteration, the parameter has the value 1, 2, …, 10.
factorial(n) with closures
- 1 | Example 7: factorial(n) with closures (at-m42/Examples/lecture06/example7.groovy)
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture06/example7.groovy
The method upto
iterates from the numerical value of the recipient number (1) to the given parameter value (10), calling the closure on each occasion. We can useful employ this method to provide a way of computing the factorial of some value. We use upto
to generate the series of integers 1, 2, 3, … to some given limit. For each value we compute the partial factorial 1 x 1, 1 x 2, 2 x 3, … until the series is complete. The code is illustrated in this slide. And the output is :
factorial(5): 120
Closures, Collections and Strings
- Several
List
,Map
andString
methods accept a closure as an argument. - These provide some elegant solutions to common programming problems.
- The most useful of these are
each
,find
,findAll
,any
,every
,collect
andinject
. - We illustrate these in the following examples.
The each method
void each(Closure closure)
- used to iterate through a
List
,Map
orString
and apply the closure on every element.
Illustrations of the method each and a closure
- 1 | Example 8: Illustrations of the method each and a closure (at-m42/Examples/lecture06/example8.groovy)
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture06/example8.groovy
Example 8 presents a number of simple examples of the each
method and a closure.
The first example (line 3) prints the values 1, 2, 3 and 4 on separate lines. The last example (line 8) prints each letter of a name on a separate line. In the second example (line 5), the keys and values in a Map are printed in the format Chris=51
. The third example (line 6) separately accesses the key and the value of the Map
element and prints them as Chris maps to: 49
.
The output is :
1 2 3 4 Chris=49 Renate=51 Gary=51 Chris maps to: 49 Renate maps to: 51 Gary maps to: 51 C h r i s
Conditional elements
- 1 | Example 9: Conditional elements (at-m42/Examples/lecture06/example9.groovy)
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture06/example9.groovy
Often, we may wish to iterate across the members of a collection and apply some logic only when the elements meat some criterion. This is readily handled with a conditional statement of the closure as shown in this slide.
The output from this script is :
2 4 Renate Gary Renate Gary h r i s
the two examples to find those staff members who are at least 50 years old. In both cases, we iterate over the Map
and apply a closure to each member of the Map
. In the first (lines 7–9), the closure parameter staff
is a Map.Entry
that includes the key and the value pair. Hence, to check the age, we use staff.value
in the Boolean
expression. In the second example (lines 10–12), the closure has two parameters representing the two Map.entry
elements, namely the key (staffName
) and the value (staffAge
).
The find method
- finds the first element in a collection that matches some criterion.
- condition to be met is defined in the closure that must be some
Boolean
expression. - the find method returns the first element that matches the condition or
null
if no match is found. - the signature of the
find
method is:
Object find(Closure closure)
Illustrations of the find method and closures
- 1 | Example 10: Illustrations of the find method and closures (at-m42/Examples/lecture06/example10.groovy)
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture06/example10.groovy
The output is :
found: 7 found: null found: Renate=51
Notice that when we apply find
to a Map
, the return object is a Map.Entry
. It would not, in this case, be appropriate to use a pair of parameters for the key and the value as in
value = ['Chris' : 49, 'Renate' : 51, 'Gary' : 51].find { key, value -> value > 50 }
since we are then not able to specify what is returned, the key or the value.
The findAll method
findAll
finds all elements in a collection and returns them in aList
List findAll(Closure closure)
findAll
finds all the elements in the receiving object that match the conditions defined by the closure.
Illustrations of the findAll method and closures
- 1 | Example 11: Illustrations of the findAll method and closures (at-m42/Examples/lecture06/example11.groovy)
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture06/example11.groovy
Example 11 gives some examples of using findAll
. The second example (line 9) reveals how simple closures can be combined to implement more complex algorithms. The merit of this approach is that each closure is relatively easy to express.
Again, applying findAll
to a Map
returns a List
of Map.Entry
elements. This is illustrated in the final two lines of the script's output:
7 9 7 9 Renate=51 Gary=51
Methods any and every
boolean any(Closure closure) boolean every(Closure closure)
Two other methods that take a closure argument are any
and every
. Method any
iterates through each element of a collection checking whether the Boolean
predicate is valid for at least one element. The predicate is provided by the closure. Method very
checks whether a predicate (a closure that returns a true
or false
value) is valid for all the elements of a collection, returning true
if they do and false
otherwise.
Examples of methods any and every
- 1 | Example 12: Examples of methods any and every (at-m42/Examples/lecture06/example12.groovy)
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture06/example12.groovy
Example 12 gives some representative examples of any
and every
. The output is :
anyElement: true allElements: true anyStaff: false
The collect method
- iterates through a collection, converting each value into a new value, using the closure as the transformer.
- returns a
List
of the transformed values
List collect(Closure closure)
Simple uses of the collect method
- 1 | Example 13: Simple uses of the collect method (at-m42/Examples/lecture06/example13.groovy)
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture06/example13.groovy
Example 13 shows some uses for collect
. The output is:
list: [1, 4, 9, 16] list: [1, 4, 9, 16] list: [0, 2, 4, 6, 8] staff: [Chris:51, Renate:53, Gary:53] list: [50, 52, 52] olderStaff: [Chris=51, Renate=53, Gary=53]
The third example of method collect
(line 12) is applied to a Range
. This is permissible since the Range
interface extends the List
interface and can, therefore be used in place of a List
. Consider also the example (line 17) that iterates across the staff
collection, increasing the age by 1. The returned value is a List
of the new age values from the Map
. The recipient Map
object referred to a s staff
is also modified by the closure (++entry.value
). The final example that assigns to olderStaff
builds a List
of Map.Entry
s, with the age increased again.
Further examples of collect
- 1 | Example 14: Further examples of collect (at-m42/Examples/lecture06/example14.groovy)
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture06/example14.groovy
Example 14 is a further illustration of the collect
method. Note that the method map
, which applies the closure parameter to the collect
method over a List
parameter. The map
method is used for doubling, tripling, and for finding those that are even-valued elements of a list of integers.
The output of this script is:
Doubling: [2, 4, 6, 8] Tripling: [3, 6, 9, 12] Evens: [false, true, false, true]
The inject method
- iterates through a collection:
- passes the initial value to the closure along with the first element of the
List
- passes into the next iteration the computed value from the previous closure, and the next element
Object inject(Object value, Closure closure)
Factorial of 5
- 1 | Example 15: Factorial of 5 (at-m42/Examples/lecture06/example15.groovy)
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture06/example15.groovy
In Example 15 we illustrate the use of inject
to find factorials.
The output is :
Factorial(5): 120 fact: 120 factorial(5): 120 factorial(5): 120
The segment of the code that uses the variable fact
(line 9) aims to show that the result of the method inject
can be achieved using and each
iterator method. First the variable fact
is assigned the value of the first parameter to inject (here 1). Then we iterate through each element of the List
. For the first value (number = 2
), the closure evaluates fact *= number
, that is, fact = 1 * 2 = 2
. For the second value (number = 3
), the closure again evaluates fact *= number
, that is, fact = 2 * 3 = 6
, and so on.
Other Closure Features
Closures as method parameters
- 1 | Example 16: Closures as method parameters (at-m42/Examples/lecture06/example16.groovy)
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture06/example16.groovy
Since a closure is an Object
, it can be passed as parameter into a method. In Example 16, the simple filter
method expects two parameters, a List
and a closure. The method finds all those elements of the list that satisfies the condition specified by the closure using the method findAll
. The program then demonstrates tow uses for the method.
The output reveals that the variable evens
is a List
of all the even integers from the table
. And, of course odds
is a List
of the odd numbers in table
.
evens: [12, 14] odds: [11, 13]
Closures as parameters to closures
- 1 | Example 17: Closures as parameters to closures (at-m42/Examples/lecture06/example17.groovy)
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture06/example17.groovy
Closures can be parameters to other closures. Example 17 is introduces a closure takeWhile
that delivers those elements from the beginning of a List
that meets some criteria defined by the closure parameter named predicate
.
The variable evens
has the even-valued integer values from table1
but not the last one: the closure returns when the first odd number is found. Similarly, isOdd
contains the first three values of table2
: takeWhile
terminates when the first even number (16) is seen.
The output is :
evens: [12, 14] odds: [11, 13, 15]
Closures as return values
- 1 | Example 18: Closures as return values (at-m42/Examples/lecture06/example18.groovy)
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture06/example18.groovy
In Example 18, the method multiply
is defined (lines 4–6). It accepts a single parameter and returns a closure. This closure multiplies two values, one of which is pre-set to the value of the value of the actual parameter passed to the method call (line 8). Thus, the variable twice
is now a closure that (always) returns double the value of its single parameter. In a similar manner, the closure multiplication
(defined on line 12) accepts a single parameter and returns a closure. Like method multiply
, the closure multiplication
returns multiplies its parameter by some predefined value. The closure quadruple
(defined on line 14) multiplies its single parameter by the value 4.
The output demonstrates that the closure twice
does indeed double its parameter while the closure quadruple
multiplies its value by 4:
twice(4): 8 quadruple(3): 12
Selection sort
- 1 | Example 19: Selection sort (at-m42/Examples/lecture06/example19.groovy)
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture06/example19.groovy
In the final example of closures we demonstrate that a closure may contain other nested closure definitions. In Example 19, we define a closure selectionSort
, which sorts a list of integers into ascending order. To implement this closure, we are required to locate the smallest item of the unsorted tail region of the list and move it to the front. moving the item to the front of the tail region actually involves swapping the front item with the smallest item. Hence we implement the closure selectionSort
with two local closures, minimumPosition
and swap
. The latter does the exchange we require, and the former finds the smallest item of the tail region of the list.
Running the program produces the desired result:
sorted: [11, 12, 13, 14, 14]
Files
- Simple programs print their output to the console (standard output) and read input from the keyboard (standard input).
- Such programs are useful when learning a new programming language, but are unrealistic.
- Most real programs use permanent storage of data in a computer file on disk, in a database or on the network.
- For these applications we need to use file IO.
Command line arguments
- 1 | Example 20: Command line arguments (at-m42/Examples/lecture06/example20.groovy)
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture06/example20.groovy
A Groovy program exists in an environment established by the operating system. The environment supports passing command line arguments to a program when it begins execution. In a Groovy script, such as that in Example 20, these arguments can be accessed by the args
variable, which is an array of Strings
.
When running this program1) the result is:
<html>
<pre>
<b><i>groovy example20.groovy aaa bbb ccc</i></b>
args: [aaa, bbb, ccc]
size: 3
First arg: aaa
</pre>
</html>
Note that args
only includes the actual arguments, not groovy
or the program name example20.groovy
. The method size
can be used to obtain the number of items in the args
array. The elements are indexed like any other List
.
The File class
- Paradoxically, in Java and therefore in Groovy, a
File
is not actually a file, rather it represents an abstraction of a file system. File
class presents an abstract, system-independent view of hierarchical pathnames.- An abstract pathname is a sequence of zero or more
String
names:- The last represents an either an actual file or a directory
- All but the last represent directories
- Directories are separated by a separator character
Some example pathnames
myfile.txt // simple file docs/report.doc // file in docs subdirectory src/groovy/example01.groovy // file in nested subdirectory src/groovy // directory /dev/at-m42/src/groovy // absolute path to a directory (unix) e:\dev\at-m42\src\groovy // MS windows directory on drive e
Purpose of the File class
- Represents either a file or a directory2)
File
methods (Java):- does the
File
exist?, is it a file or a directory? is it readable and/or writeable, what size is it?
- Groovy adds file methods, most of which accept a closure, which are useful for traversing the contents of a file or directory and processing it.
- The more commonly used methods are summarized in the notes.
Table 1 Commonly used File
methods. Methods added by Groovy shown marked asterisk.
Name | Signature | Description |
---|---|---|
append * | void append(String text) | Append the text to the end of this file. |
createNewFile | Boolean creatNewFile() | Create a new, empty file named by this abstract parameter if and only if a file with this name does not yet exist. |
delete | Boolean delete() | Delete the file or (empty) directory denoted by this abstract pathname. |
eachFile * | void eachFile(Closure closure) | Invoke the closure for each file in the given directory. |
eachFileRecurse * | void eachFileRecurse(Closure closure) | Invoke the closure for each file in the given directory, and recursively to each subdirectory. |
eachLine * | void eachLine(Closure closure) | Iterate through the given (text) file line by line. |
exists | Boolean exists | Test whether the file or directory denoted by this abstract pathname exists. |
getPath | String getPath | Convert this abstract pathname into a (platform dependant) pathname String |
getText * | String getText | Convert the content of this (text) file and return it as a String . |
isDirectory | Boolean isDirectory | Test whether the file denoted by this abstract pathname is a directory. |
mkdir | Boolean mkdir() | Create the directoty named by this abstract pathname. |
withPrintWriter * | void withPrintWriter(Closure closure) | Helper method to create a PrintWriter for this file, pass it into the closure, and ensure that the file is closed again afterwards. |
Full documentation of the File
class is to be found in the Groovy JDK documentation online.
Read and display a file, line-at-a-time
- 1 | Example 21: Read and display a file, line-at-a-time (at-m42/Examples/lecture06/cat.groovy)
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture06/cat.groovy
Example 21 is program to list the content of a file (equivalent to unix cat command). It uses the eachLine
closure to simply print each line. Note the use of the size of the args
array as an error checking mechanism: the script has to be called with one argument. If it isn't, the program prints a usage method then exits. On line 9, a File
object is created from the first argument: the actual file, the name of which is passed as args[0]
will be automatically opened, processed and closed afterwards. Since we do not otherwise refer to the File
object, it is not assigned to a variable.
An example of using this program (to list example1.groovy
) is:
<html>
<pre>
<b><i>groovy cat.groovy example1.groovy</i></b>
A closure and its invocation
def clos = { println 'Hello world' }
clos.call()
</pre>
</html>
As an exercise, you might want to compare how difficult the equivalent code would be to code in Java.
===== WC utility =====
<code groovy 1 | Example 22: WC utility (at-m42/Examples/lecture06/wc.groovy)>
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture06/wc.groovy
</code>
—-
A useful utility from the Unix operating system is a program called wc. It takes a text file and obtains counts for the number of characters, number of words and number of lines in a text file. This has been reimplemented in the example shown on this slide. The real program, which is written in C, is significantly more complex (and not just because the real thing has more error checking, a richer set of command line arguments, and better run-time documentation)!
The output for the same file is:
<html>
<pre>
<b><i>groovy wc.groovy example1.groovy</i></b>
chars: 82; words: 15; lines: 4
</pre>
</html>
===== List the content of a directory file =====
<code groovy 1 | Example 23: List the content of a directory file (at-m42/Examples/lecture06/ls.groovy)>
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture06/ls.groovy
</code>
—-
Class File
also includes the method eachFile
. Normally it is used for a File
object that represents a directory. Once again it accepts a closure as a parameter and invokes the closure for each file in the directory. In the example in this slide, method printDir
accepts the name as a directory as a parameter. It simply invokes the method listDir
which expects a File
object as the first parameter and an integer as the second parameter. The File
object represents a directory. The method listDir
calls the eachFile
method on that File
object and the closure prints the names of the file in the diretcory. If any of these represent a subdirectory then listDir
recursively calls itself. The integer is be used to define how much indentation to used when representing the files in a subdirectory. Each recursive call increases the value.
The output looks something like this3):
<cli prompt=“>”>
e:\dev\at-m42-2009\Examples\lecture06>groovy ls.groovy .
.svn
all-wcprops
entries
prop-base
props
text-base
cat.groovy.svn-base
example1.groovy.svn-base
example10.groovy.svn-base
example11.groovy.svn-base
…
index.php.svn-base
README.txt.svn-base
wc.groovy.svn-base
tmp
prop-base
props
text-base
cat.groovy
example1.groovy
example10.groovy
…
example8.groovy
example9.groovy
index.php
ls.groovy
README.txt
wc.groovy
</cli>
Side note: The script is called ls.groovy
and is named for the equivalent unix command ls (windows dir). However, the behaviour of ls.grovvy
is more akin to unix commands ls -R
(windows dir /s
) and find . -print
.
===== Recursively traversing a directory =====
<code groovy 1 | Example 24: Recursively traversing a directory (at-m42/Examples/lecture06/example24.groovy)>
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture06/example24.groovy
</code>
—-
Class File
also supports the method eachFileRecurse
. As its name suggests, the method iterates through all files in a directory and recursively through any subdirectory4). in this example, we use eachFileRecurse
to identify those files that exceed a particular length.
Output:
<cli prompt=“>”>
e:\dev\at-m42-2009\Examples\lecture06>groovy example24.groovy . 512
.svn
all-wcprops
entries
text-base
example11.groovy.svn-base
example13.groovy.svn-base
…
example6.groovy.svn-base
ls.groovy.svn-base
README.txt.svn-base
tmp
text-base
example11.groovy
example13.groovy
…
example6.groovy
ls.groovy
README.txt
</cli>
===== File copying =====
<code groovy 1 | Example 25: File copying (at-m42/Examples/lecture06/cp.groovy)>
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture06/cp.groovy
</code>
—-
With the aid of a Printwriter
object, we can copy the contents of one file to another. The PrintWriter
class is used to print formatted representations of objects to a file. Combining PrintWriter
with File
and eachLine
produces an elegant implementation. First we check for the existence of the destination file (line 10). If it exists, it is first removed (line 11). Class File
has a newPrintWriter
method that delivers a PrintWriter
object for the given destination file. we then copy each line from the source file to the destination file.
Here is a typical use of this script:
<cli prompt=“>”>
e:\dev\at-m42-2009\Examples\lecture06>groovy cp.groovy example1.groovy “Copy of example1.groovy”
e:\dev\at-m42-2009\Examples\lecture06>diff example1.groovy “Copy of example1.groovy”
e:\dev\at-m42-2009\Examples\lecture06>
</cli>
===== File copying with a PrintWriter =====
<code groovy 1 | Example 26: File copying with a PrintWriter (at-m42/Examples/lecture06/example26.groovy)>
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture06/example26.groovy
</code>
—-
Class File
also provides a number of helper methods to simplify input/ouput (See GDK documentation). For example, the method withPrintWriter
creates a new PrintWriter
object for a file and then passes into it a closure and then ensures that the file is closed afterwards. Similar helper methods are withInputStream
, withOutputStream
, withReader
, and withWriter
. Example 26, repeats the file copy example using a PrintWriter
.
===== Sorting a file =====
<code groovy 1 | Example 27: Sorting a file (at-m42/Examples/lecture06/sort.groovy)>
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture06/sort.groovy
</code>
—-
A common task is to sort a text file. This is really easy in Groovy for small to medium-sized text files since we already have the sort
method for List
s. We can read each line from the file into a List
, perform an internal sort, and then write the result back into the same file. An implementation is give in Example 27.
The typical output (for the not particularly useful case of sorting a Groovy script) is given below:
<cli prompt=“>”>
e:\dev\at-m42-2009\Examples\lecture06>groovy cp.groovy sort.groovy sort2.groovy
e:\dev\at-m42-2009\Examples\lecture06>groovy sort.groovy sort2.groovy
e:\dev\at-m42-2009\Examples\lecture06>groovy cat.groovy sort2.groovy
printWriter.println(line)
Read from the text file
// Sort the text // Write back to the text file lines << line lines.each { line -> } def lines = [] lines.sort() new File(args[0]).eachline { line -> new File(args[0]).withPrintWriter { printWriter -> println 'Usage: groovy sort.groovy filename' } }
Sorting a file
if (args.size() != 1) {
import java.io.File
}
} else {
e:\dev\at-m42-2009\Examples\lecture06>
</cli>
===== A Final Example =====
Consider a file of the form:
<code>
John 2:30pM
Jon 11:30AM
</code>
used to maintain a simple text-based daily diary.
For this file we wish to produce a report of a day's events in time order.
===== Solution =====
* put each entry of the file into a List
* use a closure to implement a suitable comparator for ordering times
* use a regular expression to extract the time from each line
<code groovy>
def TIME_PATTERN = /(\w*)\s5)/
</code>
—-
The bracketed terms allow us to extract each part of the expression:
* (\w*)\s
represents the (last word) in the event title
* (\d{1,2})
captures the hour – one or two digits which should be followed by :
* (\d{2}) captures the minutes – two digits
* and
([AP]M) captures AM or PM.
These pieces will be present in the matcher array and can be used by the comparator to sort AM before PM, and hours before minutes.
The code for this is shown in the next slide. It uses the
compareTo operator
⇔'' to make the time-sort work. The details are left as an exercise for the reader.
===== Diary report =====
<code groovy 1 | Example 28: Diary report (at-m42/Examples/lecture06/example28.groovy)>
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture06/example28.groovy
</code>
—-
Example diary file:
<code | Example diary file (at-m42/Examples/lecture06/diary.txt)>
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture06/diary.txt
</code>
Sorted diary:
<cli prompt=“>”>
e:\dev\at-m42-2009\Examples\lecture06>groovy example28.groovy diary.txt
Diary events
Meeting with the boss 9:30AM
Tea break 10:30AM
Chris 2:30PM
Coffee 3:30PM
Go home 5:30PM
</cli>
===== Case Study =====
Case Study 2 further illustrates the use of methods and closures while continuing the development of the adventure game application. You should read through the case study and examine the source code provided in preparation for the Mini Project.
===== Summary of this Lecture ====
* Closures
* Files
* Case Study
===== Lab Exercises =====
* Lab 1 all exercises Part 6 and Part 7 .
—-
Home | Previous Lecture | Lectures | Next Lecture
.
is a commonly used convention to represent the current directory