This chapter discusses structures, which are the most complex
and most useful of the complex data types. You learn what structures
are, how to use them, and when to use them.
What is a Structure?
Simply put, a structure is a container for other variables. Structures
are like arrays in that they contain programmatically addressable elements,
but structures name each element rather than simply assigning
them numeric positions. Graphically, a structure looks as shown
in Figure 15-1.
Figure 15-1: A graphical representation of
a structure.
Notice that each element in a structure has a name. Always remember
that each element in an array has a number, while structure elements
have names. You see how naming each element is useful
throughout the chapter.
Creating a Structure
As are arrays, structures are complex objects with a specific initialization
function, as the following example shows:
<cfset myStruct = StructNew()>
StructNew() doesn’t take any arguments, because structures do not
have a simple dimension. As you will see in the section “Nested
Structures” later in this chapter, structures may have arbitrarily complex
dimensions.
If myStruct currently contains a value, StructNew()destroys the old
value and replaces it with an empty structure.
124 trang |
Chia sẻ: tlsuongmuoi | Lượt xem: 2237 | Lượt tải: 0
Bạn đang xem trước 20 trang tài liệu Working with Structures, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
18546228 ch14.F 1/30/03 10:51 AM Page 332
Working with
Structures
This chapter discusses structures, which are the most complexand most useful of the complex data types. You learn what struc-
tures are, how to use them, and when to use them.
What is a Structure?
Simply put, a structure is a container for other variables. Structures
are like arrays in that they contain programmatically addressable ele-
ments, but structures name each element rather than simply assign-
ing them numeric positions. Graphically, a structure looks as shown
in Figure 15-1.
Figure 15-1: A graphical representation of
a structure.
Notice that each element in a structure has a name. Always remem-
ber that each element in an array has a number, while structure ele-
ments have names. You see how naming each element is useful
throughout the chapter.
Creating a Structure
As are arrays, structures are complex objects with a specific initial-
ization function, as the following example shows:
StructNew() doesn’t take any arguments, because structures do not
have a simple dimension. As you will see in the section “Nested
Structures” later in this chapter, structures may have arbitrarily com-
plex dimensions.
If myStruct currently contains a value, StructNew()destroys the old
value and replaces it with an empty structure.
15C H A P T E R
✦ ✦ ✦ ✦
In This Chapter
Understanding
structures
Manipulating structure
keys
Visualizing complex
structures
The various notations
used in referencing
structures
Structure copying
caveats and solutions
✦ ✦ ✦ ✦
19546228 ch15.F 1/30/03 10:51 AM Page 333
334 Part III ✦ The ColdFusion MX Language
Adding an Element to a Structure
An element in a structure consists of a key and a value. The key is the name of the element,
and the value is what the element contains. Think of the package as if each element were a
variable and the structure a named container of those variables.
You can add an element to a structure in multiple ways, all of which we discuss in the follow-
ing sections.
StructInsert() vs. StructUpdate()
One way to add an element to a structure is by using StructInsert(), as follows:
<cfset success = StructInsert(myStruct, “FavoriteFruit”, “apple”,
“TRUE”)>
The return value of StructInsert() is TRUE if the operation is successful; if unsuccessful,
the function call throws an error.
After the preceding call to StructInsert(), myStruct would look as follows:
FavoriteFruit: apple
The first argument to StructInsert() is the structure that you are inserting a key into. The
second argument to StructInsert() is the name of the new key, and the third argument is
the new key’s value. The fourth argument tells ColdFusion whether to permit the overwriting
of an already existing key: If it is FALSE and the key already exists, ColdFusion throws an
error. Note that the fourth argument defaults to FALSE if omitted.
To modify an element that’s already in the structure, you can use StructUpdate(), as
follows:
StructUpdate() throws an error if the specified key doesn’t already exist in the structure.
After you use StructUpdate(), myStruct looks as follows:
FavoriteFruit: pineapple
We usually use StructInsert() by passing TRUE for the fourth parameter, because that way,
you don’t need to know whether the specified key already exists before creating it.
Using dot notation
StructInsert and StructUpdate() are unwieldy. Take, for example, the following
statement:
<cfset success = StructInsert(myStruct, “SweetestFruit”, “peach”,
“TRUE”)>
Now compare it to the following:
The second version is simpler and easier to read, and it does the same thing. The only draw-
back to using dot notation is that you can’t embed special characters in the name of the key,
as you can by using StructInsert(), as shown in the following snippet:
19546228 ch15.F 1/30/03 10:51 AM Page 334
335Chapter 15 ✦ Working with Structures
<cfset success = StructInsert(myStruct, “Biggest Fruit”, “watermelon”,
“TRUE”)>
Notice that the second CFSET statement would cause a syntax error. You also can’t use dot
notation to create a key name that is a number, as follows:
This statement throws an error. You can use StructInsert() or StructUpdate() to create
this key.
Another problem with dot notation is that it does not preserve the case of the key. Suppose
that you do the following:
Here, someStruct contains a key named SOMEKEY, because ColdFusion converts all key
names created by using dot notation to uppercase. It is unclear why this occurs, but the good
news is that the uppercase keys will rarely affect your code because ColdFusion doesn’t con-
sider case when looking up a key name.
Using associative array notation
You have yet another syntax that you can use for creating a structure key, as the following
example shows:
That syntax is equivalent to the following:
This new syntax is called associative-array notation. For an explanation, look at Figure 15-2.
Figure 15-2: Side-by-side comparison of an array and a
structure.
Notice that the structure readout in the figure looks almost like two arrays sandwiched
together — one array containing the element names and the other containing the element val-
ues. These two “arrays” are associated with one another so that referring to an element in
one array retrieves the element in the other array. (Note, however, that the names and values
are not actually arrays; it’s just convenient to use arrays as a comparison.)
The first advantage to this notation is that you can now embed special characters in the key
name, as follows:
Another advantage is that associative-array notation preserves the case of the key name. This
syntax truly is the best of both worlds — it’s as easy to use as dot notation but as flexible as
StructInsert(). Given the choice between dot notation and associative-array notation, the
latter is usually your best bet for creating structure keys.
19546228 ch15.F 1/30/03 10:51 AM Page 335
336 Part III ✦ The ColdFusion MX Language
The real advantage to using an associative array, however, is the capability to use dynamic
key names, as shown in the following example:
After that last call, myStruct would look as follows:
FavoriteFruit: pineapple
SweetestFruit: peach
Biggest Fruit: watermelon
NotARealFruit: zucchini
Coolest Fruit: cherry
Sourest Fruit: lemon
Notice that, instead of storing lemon in an element named myKeyName, ColdFusion evaluates
myKeyName and stores lemon in an element named Sourest Fruit.
Retrieving an Element From a Structure
You have two ways to retrieve an element from a structure. The simplest is to use dot nota-
tion or associative-array notation, as follows:
#myStruct.NotARealFruit#
#myStruct[“Coolest Fruit”]#
If you’d rather use a function, use StructFind() as follows:
#StructFind(myStruct, “NotARealFruit”)#
#StructFind(myStruct, “Coolest Fruit”)#
Both syntaxes throw an error if the specified key does not exist. You derive no benefit from
one or the other syntax, because both do the same thing, so pick whichever one you that like
and use it. Using associative-array or dot notation, however, is usually much more readable.
Notice that you can set an element by using associative-array notation and retrieve it by
using dot notation; this is what we usually do, as the following example shows:
#myStruct.TangiestFruit#
We set elements by using associative-array notation to preserve the case of the key, but we
retrieve the element by using dot notation because it’s typically more readable. Remember,
however, that you aren’t required to use only one syntax — use whichever syntax best fits the
situation.
Removing an Element From a Structure
You delete a key from a structure by using StructDelete(), as follows:
After you call StructDelete(), myStruct looks as follows:
19546228 ch15.F 1/30/03 10:51 AM Page 336
337Chapter 15 ✦ Working with Structures
FavoriteFruit: pineapple
SweetestFruit: peach
Biggest Fruit: watermelon
NotARealFruit: zucchini
Sourest Fruit: lemon
TangiestFruit: orange
Coolest Fruit is gone. Notice that you never have undefined keys in a structure, which is
another way that structures are different from arrays.
To quickly remove all elements from a structure, use StructClear(), as follows:
After you call StructClear, myStruct still exists, but it contains no elements.
Getting Information About a Structure
StructIsEmpty() tells you whether any elements are in a given structure, as the following
example shows:
To get a specific count of how many top-level (that is, nonnested) elements are in a structure,
use StructCount(), as follows:
#StructCount(myStruct)#
To tell whether a given variable contains a structure, use IsStruct(), as follows:
IsStruct returns TRUE if the passed variable is a structure and FALSE if it does not.
Looping Over a Structure
One form of CFLOOP enables you to loop over the keys in a structure, as the following exam-
ple shows:
The value of the #theItem# key is #myStruct[theItem]#.
The collection attribute tells ColdFusion which structure to loop over, and the item
attribute is what ColdFusion names the variable that contains the key name. ColdFusion
loops over every element in the structure and puts the key’s name in theItem each time that
the loop iterates.
If you don’t need to loop over the structure but you need a list or array containing the struc-
ture’s keys, use StructKeyList() or StructKeyArray(), as follows:
19546228 ch15.F 1/30/03 10:51 AM Page 337
338 Part III ✦ The ColdFusion MX Language
StructKeyList() takes an optional second parameter describing the delimiter to use for the
list. The following line of code, for example, creates a semicolon-delimited list containing the
names of the top-level keys in myStruct:
Nested Structures
So far you’ve created keys containing simple string values, but the most powerful feature of
structures is their capability to nest inside another structure. Nesting enables you to create
hierarchical data structures that closely resemble real-world data models.
The simple structure that we created earlier looks like what’s shown in Figure 15-3.
Figure 15-3: A simple structure.
A nested structure would look like what’s shown in Figure 15-4.
Figure 15-4: Structures nested inside one another.
Notice that in Figure 15-4, smaller structures are nested inside the enclosing structure.
Creating a nested structure may seem complicated, but it really is quite simple. In the follow-
ing example, you create a new key named FruitCosts in myStruct that contains a substruc-
ture that, in turn, contains the names of fruits and their respective costs:
19546228 ch15.F 1/30/03 10:51 AM Page 338
339Chapter 15 ✦ Working with Structures
Now myStruct looks like what’s shown in Figure 15-5.
Figure 15-5: A substructure inside myStruct.
Structures can be nested many levels deep in any configuration.
You can also use associative-array notation to create nested structures, as follows:
Our personal preference is to use dot notation for every nesting level except the final one, as
in the following example:
Just make sure that you don’t have any special characters or spaces in your key names — or
have key names made entirely of numbers — in the dot-path portion of your notation.
Complex nesting
After you understand the general concept of nesting structures, you can delve a little deeper
into more complex nesting schemes, where one complex variable is nested within another.
You can, for example, also nest arrays inside of structures, as follows:
After you add the array, myStruct looks like what’s shown in Figure 15-6.
19546228 ch15.F 1/30/03 10:51 AM Page 339
340 Part III ✦ The ColdFusion MX Language
Figure 15-6: An array nested inside myStruct.
Structures can be nested any number of levels deep. In addition, array elements can contain
structures, as the following example shows:
#myArray[1].MyTestKey#
Structures become truly useful if you use them as containers for complex data that models
the real world. You can create a shopping cart by using a structure, for example, as shown in
Listing 15-1.
Listing 15-1: Creating a shopping cart in a structure
19546228 ch15.F 1/30/03 10:51 AM Page 340
341Chapter 15 ✦ Working with Structures
Later on, after your shopping cart contains line items in its arrays, you can easily produce
familiar values, as the following list describes:
✦ The subtotal is simply ArraySum(myCart.arExtended),.
✦ The sales-tax amount is simply (myCart.salesTaxRate * myCart.subtotal).
✦ The total is (myCart.subtotal + myCart.salesTaxAmount +
myCart.shippingAmount).
You face no limits on how much information that your structure can store, nor do you have
any limits as to how complex it can be. The only thing that you must watch out for is making
your structure too complicated for your needs. You can usually keep track of what goes
where quite easily if you make the format of your structures emulate the real-world data that
you are storing.
How dot notation automatically creates a nested
structure
Until now, you’ve created structures only by using StructNew(). You can also create struc-
tures just by using dot notation, as follows:
That line creates a structure named aNewStruct with a substructure named SomeKey that
has an element named SomeValue with a value of 1. You can visualize it as what’s shown in
Figure 15-7.
Figure 15-7: A structure created by using dot notation.
Notice that, in the figure, the names of all newly created structures and keys are all upper-
case, regardless of the capitalization that you use in the code.
As convenient as this method may seem, it does have a drawback. Consider the following
snippet:
After running the preceding snippet, two new keys, named myKey and yourKey, are created,
and aNewStruct looks as shown in Figure 15-8.
Figure 15-8: aNewStruct.
19546228 ch15.F 1/30/03 10:51 AM Page 341
342 Part III ✦ The ColdFusion MX Language
Suppose that you now attempt to call the following snippet to create a nested structure
named myKey:
If the previous line had worked, it would have overwritten the previous simple value of myKey
with a substructure named myKey, and aNewStruct would look as shown in Figure 15-9.
Figure 15-9: aNewStruct with a nested substructure.
Fortunately, however, attempting to overwrite a simple key with a substructure throws an
error in ColdFusion MX.
You do face a limitation in creating structure keys by using this method. ColdFusion MX can
create nested structures only up to three levels deep in a single call, as in this example:
Attempting to create a key more than three levels deep gives you unexpected results.
ColdFusion ignores all but the last three keys. Running the following code, for example, out-
puts “some value”:
#a.e.f.g#
You can, however, create structures nested as deeply as you want as long as you don’t
attempt to go more than three levels deeper than currently exists.
Sorting a Structure
Sometimes data is useful only if it is sorted in a specific order. Although this is easy to do
with tabular data in a database, sorting data in a structure takes a little more understanding
and effort.
Say that you have a structure containing the prices per pound of different fruits, as following:
You can loop over these prices and output them by using the code in Listing 15-2.
Caution
19546228 ch15.F 1/30/03 10:51 AM Page 342
343Chapter 15 ✦ Working with Structures
Listing 15-2: Looping over a structure of fruit prices
#Fruit#: #FruitCosts[Fruit]# / lb.
Listing 15-2 would produce the output shown in Figure 15-10.
Figure 15-10: The output of Listing 15-2.
The problem here is that structures don’t have any kind of inherent order. Structure keys are
stored in an internal order that only ColdFusion Server understands, and if you loop over the
structure, that’s the order that you see. The order in which you insert the keys doesn’t even
matter.
StructSort() returns an array of key names sorted by their values (not their key names).
Listing 15-3 uses StructSort() to put the fruit costs out in order.
Listing 15-3: Ordering the structure before looping
#keyArray[i]#: #FruitCosts[keyArray[i]]# / lb.
Listing 15-3 produces the output shown in Figure 15-11.
Figure 15-11: The output of Listing 15-3.
The biggest difference between Listings 15-2 and 15-3 is the approach that each takes to loop-
ing over the structure. In Listing 15-2, we just use a standard collection loop, relying on what-
ever order ColdFusion stored the structure in. In Listing 15-3, we call StructSort() first,
which returns an array of key names that look as follows:
19546228 ch15.F 1/30/03 10:51 AM Page 343
344 Part III ✦ The ColdFusion MX Language
1: Apples
2: Lemons
3: Peaches
4: Oranges
5: Cherries
These elements still may not seem to be in any particular order, but look at the following
prices associated with each element (although the values in parentheses are not actually part
of the array):
1: Apples (1.50)
2: Lemons (1.65)
3: Peaches (1.75)
4: Oranges (1.99)
5: Cherries (2.25)
Although the key names appear in the array, the values remain back in the structure.
After calling StructSort(), Listing 15-3 loops through keyArray, which contains the sorted
key names. During this loop, keyArray[i] contains the current key name, which can in turn
be used to supply the key name to the FruitCosts structure. If you follow the ColdFusion
processing engine along step-by-step, the resolution of this reference is as follows:
Step 1: #FruitCosts[keyArray[i]]#
Step 2: #FruitCosts[keyArray[1]]#
Step 3: #FruitCosts[“Apples”]#
Result: 1.50
But what if you want to sort by a key in a nested structure? We’ve modified the FruitCosts
structure from earlier in the chapter by using the following code:
Each element in the FruitCosts structure is a substructure containing two keys: “lb”
(price per pound) and “sack” (price per sack). To help you visualize this structure, look at
Figure 15-12.
19546228 ch15.F 1/30/03 10:51 AM Page 344
345Chapter 15 ✦ Working with Structures
Figure 15-12: A structure with fruit prices by the pound and by the sack.
So now that you have this set of nested structures, how do you sort by price per pound?
A fourth attribute of StructSort()describes a dot path to the sort value, as shown in
Listing 15-4.
Listing 15-4: Sorting by a nested key
#keyArray[i]#: #FruitCosts[keyArray[i]].lb# / lb.
The good thing about this method is that you can very easily switch to sorting by price per
sack, as shown in Listing 15-5.
Listing 15-5: Sorting by a different nested key
#keyArray[i]#: #FruitCosts[keyArray[i]].sack# / sack.
19546228 ch15.F 1/30/03 10:51 AM Page 345
346 Part III ✦ The ColdFusion MX Language
You can sort by a key any number of levels deep by adding elements to the dot path:
<cfset keyArray = StructSort(FruitCosts, “numeric”, “asc”,
“sack.10lb.fresh”)>
Be aware that, if the specified subkey doesn’t exist for every top-level element in the main
structure, ColdFusion throws an error.
Copying a Structure
Now you come to one of the most confusing parts of dealing with structures: What happens
after you copy a structure and then start modifying the copy? The answer is not what you
may think, so pay close attention to the details.
Consider the following snippet:
In these three lines, you have two simple variables: myVar, containing 1, and yourVar, con-
taining 2. If you set yourVar equal to myVar, you make a copy of myVar, so setting myVar to a
different value doesn’t affect yourVar. This is true for almost every variable type in
ColdFusion — assigning one variable to another makes a copy and then divorces the two vari-
ables so that making a change in one doesn’t make a change in the other.
Structures, however, are the one variable type where such simple copying doesn’t apply. Say
that you call the following snippet, which creates a structure named myStruct, populates it
with a couple values, and then copies myStruct into a new structure named yourStruct:
You would expect to end up with myStruct.aKey equal to 3 and yourStruct.aKey still equal
to the original value of 1, but Figure 15-13 shows what really happens.
Figure 15-13: Two structures after attempting to copy and modify one.
What’s going on here? Both structures show the same value for aKey even though you
changed it only in myStruct. This happens because of the way that you attempted to copy
myStruct, as follows:
19546228 ch15.F 1/30/03 10:51 AM Page 346
347Chapter 15 ✦ Working with Structures
Unlike any other ColdFusion data type, structures are accessed by reference and not by value.
If you access a variable by value, the variable is the actual value. If you access a variable by
reference, the variable is a pointer to the value, which is stored somewhere else in memory.
Suppose that you call the following:
You aren’t copying the value — you’re copying the reference. yourStruct, therefore, is now
pointing to the same object as myStruct, so changing something about myStruct changes
yourStruct as well — because they’re really the same structure. In fact, the opposite holds
true as well: Changing the value of yourStruct.aKey changes the value of myStruct.aKey to
the same value.
If you want to copy myStruct to yourStruct so that the values in each structure are no
longer linked to one another, use StructCopy(), as follows:
Now, making a modification to myStruct doesn’t affect the contents of yourStruct.
Well, not exactly. Things get a little confusing here, so hold on tight . . .
Say that you change the contents of myStruct to include a nested structure, as follows:
You would expect that, because you copied the structure by using StructCopy() instead of
just copying the reference, modifying myStruct.aKey wouldn’t affect yourStruct.aKey, and
you are correct. The change to myStruct.aSubStruct.subKey, however, does modify
yourStruct’s value. This situation is shown in Figure 15-14.
Figure 15-14: Copying structures by using StructCopy().
19546228 ch15.F 1/30/03 10:51 AM Page 347
348 Part III ✦ The ColdFusion MX Language
This result happens because of how StructCopy() copies a structure’s members. As the
code copied myStruct to yourStruct, it copied aKey and bKey by value. (Remember that
this means that aKey and bKey refer to actual values in both structures.) StructCopy(),
however, still copies nested structures by reference, meaning that it copies the pointer to the
structure rather than the actual structure itself. As such, both myStruct.aSubStruct and
yourStruct.aSubStruct still point to the same structure after a StructCopy().
To completely divorce myStruct and all its substructures from yourStruct and all its sub-
structures, you must use Duplicate(), as follows:
Now all keys in both myStruct and yourStruct are completely separate, regardless of
whether they contain simple values or nested structures, as shown in Figure 15-15.
Figure 15-15: Using Duplicate() to make a “deep copy.”
Nine times out of ten, you use Duplicate() to copy a structure. At times, the other two
methods can prove useful, but those times are rare and typically very advanced. Just be
aware of exactly what your code does in duplicating a structure.
Using Variable Scopes as Structures
You may not realize it, but you were using structures as far back as Chapter 2! You may
remember the following code from that chapter:
<cfquery name=”DeleteCompany”
datasource=”#Request.MainDSN#”>
DELETE FROM Company
WHERE
CompanyID = #Val(Form.CompanyID)#
19546228 ch15.F 1/30/03 10:51 AM Page 348
349Chapter 15 ✦ Working with Structures
<cflocation url=”Finished.cfm?msg=#URLEncodedFormat(‘Company ID
#Form.CompanyID# has been deleted from the database.’)#” addtoken=”No”>
Form.CompanyID is actually an element in a structure! All the ColdFusion variable scopes are
actually structures: Form, URL, Client, and so on. This may not seem very interesting until you
think about what you can do with structures. You can get a list of all of a request’s form vari-
ables by using StructKeyList(). You can delete a local variable by using StructDelete().
You can even sort all of a request’s incoming URL variables by value by using StructSort().
You should, however, never do one thing with a variable scope: Never call StructClear() on
the Session scope. Calling StructClear() on the Session scope clears the CFID, CFTOKEN, and
URLToken variables, possibly causing problems within ColdFusion server. Refer to Macromedia
TechNote 14143 in the Macromedia KnowledgeBase for more information on this issue.
Other Structure Operations
The techniques that you learn in the preceding sections of this chapter are all useful, and
you’re likely to use most if not all of them at some point in your development career. The
techniques in this section are advanced, but that’s not to say that you shouldn’t learn them.
You may eventually run into a situation where these techniques are just what the doctor
ordered.
Merging two structures
If you have two related structures, you may decide to group them together. You can create a
superstructure, which simply sets the two structures as subkeys of a larger one. You can also
take the keys from one structure and transplant them into the other.
The simplest technique is to create a superstructure. Consider the following snippet:
ourStruct has grouped together the contents of myStruct and yourStruct. ourStruct
looks like what’s shown in Figure 15-16.
Figure 15-16: ourStruct as a superstructure.
19546228 ch15.F 1/30/03 10:51 AM Page 349
350 Part III ✦ The ColdFusion MX Language
Another option is to roll the keys from one structure into another by using StructAppend(),
as follows:
myStruct now contains the keys from yourStruct, as shown in Figure 15-17.
Figure 15-17: myStruct after you call StructAppend().
Finding a key or value deep in a nested substructure
Two functions enable you to search for a key or value within a structure. StructFindKey()
and StructFindValue() return an array with elements that are structures describing the
matched key or value.
Consider the following call to the FruitCosts array from the section “[name section],” ear-
lier in this chapter:
resultsArray would look as shown in Figure 15-18:.
19546228 ch15.F 1/30/03 10:51 AM Page 350
351Chapter 15 ✦ Working with Structures
Figure 15-18: The results of calling StructFindKey() to find all
prices per pound.
19546228 ch15.F 1/30/03 10:51 AM Page 351
352 Part III ✦ The ColdFusion MX Language
In the figure, value is the value of the found key; owner is a reference to the structure that
“owns” the found key; and path is the dot path to the found key. StructFindValue() returns
the same results, but it searches by the value of the key rather than by the name of the key.
Summary
We can think of no way to simplify the details of creating and manipulating structures any
more than how we’ve described them in this chapter, and the subject is still a hard one to
master. Go back and read the stuff that’s still a bit hazy, and by all means, play with the exam-
ple code that we discussed in the chapter.
Structures are widely regarded as the most difficult feature of ColdFusion, but they are the
basis for some of the best programming techniques in ColdFusion. In this chapter, you
learned what you can do with structures and how to use them effectively.
The next chapter covers CFSCRIPT, which enables you to use ColdFusion as an efficient
scripting language.
✦ ✦ ✦
19546228 ch15.F 1/30/03 10:51 AM Page 352
Scripting
ColdFusion
with CFSCRIPT
One of the great things about ColdFusion is its easy-to-use, tag-based syntax. Having the capability to intersperse CFML and
HTML tags without needing to open and close script blocks or needing
to remember what syntax you’re currently working in is a wonderful
thing.
At times, however, using a scriptable syntax would be nice — for
example, if you’re doing heavy data processing on a page. As nice as
CFML’s tag-based syntax is, number crunching is best expressed in
script.
CFSCRIPT is a server-side scripting language that works with CFML to
give you the best of both worlds: an elegant, tag-based syntax when-
ever you need it and a flexible scripting syntax if you don’t.
User-defined functions are discussed in Chapter 17. We do not
include them in this chapter because of ColdFusion MX’s capabil-
ity to define UDFs by using either CFSCRIPT or CFML syntax. This
chapter describes the basics of CFSCRIPT.
What is CFSCRIPT?
Essentially, CFSCRIPT is an instruction to the ColdFusion processing
engine to treat a block of code as scripting-based syntax rather than
as tag-based syntax. Why mess with a perfectly good thing (CFML) by
adding another complication (scripting)? Because the latter is easier
to code and faster to process.
Consider the following snippet, for example, which loops from 1 to
10, adding each index to a running total:
Following is the same code expressed in CFSCRIPT’s scripting syntax:
TheSum = 0;
for(i = 1; i LTE 10; i = i + 1) {
Cross-
Reference
16C H A P T E R
✦ ✦ ✦ ✦
In This Chapter
The differences between
tag-based ColdFusion
syntax and scripting
syntax
Setting variables by
using CFSCRIPT
Flow control by using
scripting syntax
✦ ✦ ✦ ✦
20546228 ch16.F 1/30/03 10:51 AM Page 353
354 Part III ✦ The ColdFusion MX Language
TheSum = TheSum + 1;
}
CFSCRIPT doesn’t support the ++ syntax for incrementing a variable that may be familiar to
you if you have experience with Java, JavaScript, or C++.
Look familiar? If you’re having JavaScript déjà vu, that’s normal, because CFSCRIPT is almost
identical to JavaScript. CFSCRIPT blocks mostly instantiate variables and perform calcula-
tions, but they can produce output, too. Consider, for example, the following extension of the
preceding code snippet:
#TheSum#
Normally, anything that isn’t part of a CFML tag is output to the page. CFSCRIPT, however,
uses a function named WriteOutput() for page output, as the following example shows:
TheSum = 0;
for(i = 1; i LTE 10; i = i + 1) {
TheSum = TheSum + 1;
}
WriteOutput(TheSum);
Think of WriteOutput() as the server-side equivalent of JavaScript’s client-side
document.write() method.
The difference between WriteOutput() and document.write() shows the different mindset
of CFSCRIPT as compared to CFML. Rather than the text just “being there,” as it is in CFML,
CFSCRIPT must be directed to output the text.
Notice the use of semicolons, which terminate CFSCRIPT statements. CFSCRIPT is less forgiv-
ing than JavaScript with respect to semicolon termination; forget one, and ColdFusion throws
an error.
Another thing that you need to get used to in CFSCRIPT is the use of curly braces. Curly
braces surround blocks of CFSCRIPT code similar to the way that opening and closing tags
surround blocks of CFML code, but their use is actually more critical. A simple if construct
in CFSCRIPT looks as follows:
if(this EQ that)
doThis();
else
doThat();
The preceding snippet runs error free, but suppose that you need to add another statement
after doThis(), as in the following example:
Note
20546228 ch16.F 1/30/03 10:51 AM Page 354
355Chapter 16 ✦ Scripting ColdFusion with CFSCRIPT
if(this EQ that)
doThis();
doSomeOtherThingToo();
else
doThat();
ColdFusion throws an error, because it sees an else clause without a corresponding if
clause. This happens because the doSomeOtherThingToo() statement is considered the con-
tinuation of statements after the if statement finishes executing the doThis() statement. For
CFSCRIPT to execute both statements when the if tests TRUE, you must enclose them within
curly braces as follows:
if(this EQ that) {
doThis();
doSomeOtherThingToo();
} else
doThat();
In fact, a best practice is to always include the curly braces, regardless of whether you need
them or not, as in the following example:
if(this EQ that) {
doThis();
doSomeOtherThingToo();
} else {
doThat();
}
Why? Because you never know when you’re going to add another statement to an existing if
test. We can’t tell you how many times that we threw errors in the early days by adding a sec-
ond statement to an existing, unenclosed if construct, simply because we didn’t pay atten-
tion to the enclosure mechanism. If you always enclose, you never throw errors. The same
holds true for any language that requires such enclosure.
How to use CFSCRIPT
All CFSCRIPT code is contained between CFSCRIPT tags, as shown in the following example:
... Regular CFML code goes here ...
... CFSCRIPT code goes here
... Regular CFML code goes here ...
Notice that the content of a CFSCRIPT block must be a complete statement. You cannot, for
example, do the following:
if(myVar GT yourVar) {
20546228 ch16.F 1/30/03 10:51 AM Page 355
356 Part III ✦ The ColdFusion MX Language
My text goes here.
}
That type of construction is valid in some other scripting languages, but not in CFSCRIPT.
CFSCRIPT operations must be self-contained in a single code block, as follows:
if(myVar GT yourVar) {
WriteOutput(“My text goes here.”);
}
You can have several CFSCRIPT blocks in a single ColdFusion template, but each CFSCRIPT
block must be a self-standing block of executable code.
Setting variables in CFSCRIPT
The simplest operation in CFSCRIPT is setting a variable, as follows:
myVar = 1;
Setting a variable in CFSCRIPT requires no CFSET tag, and the statement ends with a semi-
colon. You can now use myVar just as you would any other ColdFusion variable. Notice that
you can share any variable between CFSCRIPT and regular CFML, as follows:
myVar = myVar + 1;
yourVar = 3;
#yourVar + myVar#
You are not limited to setting and reading variables in the Variables scope, either. Any vari-
able that you can set by using tag-based CFML can also be set by using CFSCRIPT’s scripting
syntax.
If constructs in CFSCRIPT
The if construct in CFSCRIPT works exactly the same as its CFML counterpart, CFIF.
ColdFusion evaluates a condition and executes a dependant statement based on whether the
condition is true or false.
Compare the following CFML and CFSCRIPT if constructs:
✦ CFML:
20546228 ch16.F 1/30/03 10:51 AM Page 356
357Chapter 16 ✦ Scripting ColdFusion with CFSCRIPT
... execute if true ...
✦ CFSCRIPT:
if(myVar GT yourVar) {
... execute if true ...
}
CFSCRIPT also has an equivalent to CFELSE, as the following example shows:
if(myVar GT yourVar) {
... execute if true ...
}
else {
... execute if false ...
}
You also find an equivalent to CFELSEIF in CFSCRIPT, as follows:
if(myVar GT yourVar) {
... execute if true ...
}
else if (myVar EQ yourVar) {
... execute if true ...
}
else {
... execute if all conditions false ...
}
Switch constructs in CFSCRIPT
CFSCRIPT has an equivalent to CFSWITCH, but you find some important differences. Take, for
example, the following CFSWITCH statement:
... Execute if myVar equals 1 ...
... Execute if myVar equals 2 or 3 ...
... Execute if no other case executes ...
Now compare it to its CFSCRIPT counterpart, as follows:
switch(myVar) {
case 1:
... Execute if myVar equals 1 ...
break;
case 2:
case 3:
20546228 ch16.F 1/30/03 10:51 AM Page 357
358 Part III ✦ The ColdFusion MX Language
... Execute if myVar equals 2 or 3 ...
break;
default:
... Execute if no other case executes ...
}
These snippets are very different, but they do the same thing. Both evaluate myVar and then
execute whichever case statement matches. If none of the statements match, ColdFusion exe-
cutes the default section.
Look at the differences in syntax between the tag-based and script-based versions. Rather
than each case being in its own enclosed in a paired tag (), each case
declaration (for example, case 1:) is followed by dependent statements and terminated by a
break statement. Without that break statement, the case declaration remains open, so exe-
cution of that case’s dependent statements continues.
The use of explicit break statements in CFSCRIPT emulates CFCASE’s capability to use a
comma-delimited list of match values. If myVar equals either 2 or 3 in the preceding snippet,
for example, the same dependent statements are executed until a break is reached.
Looping constructs in CFSCRIPT
Looping constructs in CFSCRIPT are rather different from those in CFML. You have the follow-
ing five types of CFML loops:
✦ Index loops (also known as For loops), which loop a specified number of times.
✦ Conditional loops, which loop as long as a condition tests true.
✦ Query loops, which loop over each row in a query.
✦ List loops, which loop over each element in a list.
✦ Structure loops, which loop over each top-level key in a structure.
You have only the following four types of CFSCRIPT loops:
1. For loops, which loop a specified number of times.
2. While loops, which loop as long as a condition tests true.
3. Do-While loops, which loop at least once and then continue looping as long as a condi-
tion tests true.
4. Structure loops, which loop over each top-level key in a structure.
CFSCRIPT’s While loop is the direct equivalent of CFML’s Conditional loop, but CFML has no
direct equivalent to the CFSCRIPT Do-While loop.
The following sections look at each of these four types of loops.
Looping a set number of times
The For loop is akin to CFLOOP using the from, to, index, and step attributes. In CFSCRIPT it
is structured like this:
20546228 ch16.F 1/30/03 10:51 AM Page 358
359Chapter 16 ✦ Scripting ColdFusion with CFSCRIPT
for(indexVar = 1; indexVar LTE 10; indexVar = indexVar + 1) {
... body of loop ...
}
One of the disorienting things about CFSCRIPT is its lack of attribute names. Whereas in
CFML, you can clearly identity the index variable, step increment, and other attributes of
the loop, you now must positionally deconstruct the syntax in CFSCRIPT. Doing so is easier
than you think, however. The following code snippet uses CFLOOP attribute names in
CFSCRIPT syntax to help you compare them to one another:
from = 1;
to = 10;
step = 1;
for(index = from; index LTE to; index = index + step) {
... body of loop ...
}
The CFSCRIPT version initializes the index variable with the first loop count, loops while the
index variable is less than or equal to the last loop count, and each time that the loop exe-
cutes, it increments index by one.
Looping while a condition is true
You have two different types of conditional loop in CFSCRIPT. The first is a While loop, as
shown in the following example:
bLoop = TRUE;
while(bLoop EQ TRUE) {
WriteOutput(“This is one iteration through the loop.”);
if(RandRange(1, 10) EQ 10) {
bLoop = FALSE;
}
}
The other type is a Do-While loop, as shown in the following code:
do {
WriteOutput(“This is one iteration through the loop.”);
if(RandRange(1, 10) EQ 10) {
bLoop = FALSE;
}
else {
bLoop = TRUE;
}
} while(bLoop EQ TRUE);
These two loops are both conditional loops, but they work differently. The While loop evalu-
ates its condition before executing the body of the loop, meaning that, if the condition is false
before the loop begins, the loop never executes.
Conversely, the Do-While loop evaluates its condition after the loop body, meaning that the
loop body always executes at least once.
20546228 ch16.F 1/30/03 10:51 AM Page 359
360 Part III ✦ The ColdFusion MX Language
Looping over a structure
Another type of loop that CFSCRIPT carries over from CFML is the Structure loop, which
loops over the top-level keys in a structure and looks as follows in CFML:
#theKey#: #myStruct[theKey]#
The CFSCRIPT version is similar, as the following example shows:
for(theKey in myStruct) {
WriteOutput(“#theKey#: #myStruct[theKey]#”);
}
Breaking out of a loop
CFSCRIPT also has an equivalent to CFBREAK, as the following example shows:
for(i = 1; i LTE 10; i = i + 1) {
if(RandRange(1,10) EQ 10) {
break;
}
WriteOutput(“#i#”);
}
In this example, ColdFusion normally ends the loop after iterating ten times. If RandRange()
ever returns 10 during the execution of this loop, however, the break statement ends the
loop prematurely, immediately skipping past the end of the loop.
Skipping an iteration of a loop
CFSCRIPT’s continue statement instructs ColdFusion to skip the rest of the current loop iter-
ation and immediately begin the next iteration of the loop, as shown in the following example:
for(i = 1; i LTE 10; i = i + 1) {
if(RandRange(1,10) EQ 10) {
continue;
}
WriteOutput(“#i#”);
}
In this example, whenever ColdFusion calculates a random value equal to 10, it skips the
WriteOutput() statement and instead skips to the beginning of the next iteration of the loop,
if any iterations remain.
Comments in CFSCRIPT
You cannot use CFML comments within a CFSCRIPT block. Instead, you must use one of the
CFSCRIPT comment types, as follows:
// This is a single-line comment before some code.
myVar = 1;
20546228 ch16.F 1/30/03 10:51 AM Page 360
361Chapter 16 ✦ Scripting ColdFusion with CFSCRIPT
/*
This is a multi-line comment.
Both of these lines are commented.
*/
Single-line comments begin with two forward slashes — the entire line after the slash marks is
commented out, meaning that any code in that line does not execute.
Multiline comments are surrounded by /* and */— everything in between those two marks
is a comment. You cannot nest multiline comments inside one another, but you can nest a
single-line comment inside a multiline comment.
Advantages of CFSCRIPT
CFSCRIPT has the following two primary advantages:
✦ It is often more understandable to use CFSCRIPT than CFML in processing data,
because CFSCRIPT’s procedural syntax is often clearer than that of CFML.
✦ You get a healthy performance gain form using CFSCRIPT. CFML is basically an XML-
based language and, as such, must be parsed by an XML parser and executed by an
interpreter, and both operations are relatively slow. CFSCRIPT requires only a single
XML-based tag pair to be parsed, after which its contents — essentially a simple list of
preparsed commands — are passed to the processing engine all at once.
Sometimes entire pages of code are written in CFSCRIPT. The best practice is to analyze your
templates to determine whether you can group together large blocks of code that don’t
require CFML tags and then write that code as a single CFSCRIPT block. Sometimes you must
interweave CFSCRIPT blocks with CFML, in which case you should try to minimize the num-
ber of CFSCRIPT blocks that you create in a single template.
Summary
In this chapter, you learn that CFSCRIPT is a powerful addition to the ColdFusion language and
is useful for use in large blocks of code that do not require the use of tag-based CFML. The syn-
tax is much simpler than that of tag-based CFML for variable assignments and function calls,
and although the syntax for flow-control constructs like if, switch, and the various loops may
seem difficult to grasp at first, your payoff is faster execution of more efficient code.
✦ ✦ ✦
20546228 ch16.F 1/30/03 10:51 AM Page 361
20546228 ch16.F 1/30/03 10:51 AM Page 362
Building User-
Defined Functions
ColdFusion has more than 250 functions in its language. It hasstring-manipulation functions, array functions, structure func-
tions, and many other types of functions. Even with this wide variety
of functions, however sometimes you may want to define your own
function that does things that the built-in ColdFusion functions can’t.
This chapter does not describe how to use CFSCRIPT — only how
to use CFSCRIPT in conjunction with user-defined functions.
CFSCRIPT itself is discussed as a separate subject in Chapter 16.
Building UDFs by Using CFSCRIPT
A user-defined function, or UDF, can be built in CFSCRIPT, CFML, or a
combination of the two. The following sections describe how to build
UDFs by using CFSCRIPT. CFSCRIPT UDFs are very natural because
they mimic function creation in JavaScript, Java, and many other pro-
gramming languages which you may already be familiar with.
UDF structure
In general, functions receive one or more arguments and return a sin-
gle result. Some functions (such as ColdFusion’s Now() function) do
not take arguments, but almost all functions do return a result.
A basic UDF built by using CFSCRIPT looks as follows:
function GetCurrentTime() {
return TimeFormat(Now(), “h:mm:ss tt”);
}
The only four things required for every function are the function
keyword, the name of the function, the parentheses after the name,
and the curly braces around the body of the function. Although the
return statement is technically optional, return is what makes a
function truly useful, as this statement represents the result returned
to the code that calls the function. We call the function that we just
created as we would any built-in ColdFusion function, as follows:
#GetCurrentTime()#
Cross-
Reference
17C H A P T E R
✦ ✦ ✦ ✦
In This Chapter
Understanding user-
defined functions (UDFs)
Building UDFs by using
CFSCRIPT
Building UDFs by using
CFFUNCTION
Working with function
arguments
Choosing a function
invocation method
Planning function
libraries
✦ ✦ ✦ ✦
21546228 ch17.F 1/30/03 10:51 AM Page 363
364 Part III ✦ The ColdFusion MX Language
We can expand my function by defining a local variable, as follows:
function GetCurrentTime() {
var szTime = TimeFormat(Now(), “h:mm:ss tt”);
return szTime;
}
More on the var keyword in the following section. We can also add code to our function
between the variable declaration and the return statement, as follows:
function GetCurrentTime() {
var szTime = TimeFormat(Now(), “h:mm:ss tt”);
szTime = ReplaceNoCase(szTime, “am”, “in the morning”);
szTime = ReplaceNoCase(szTime, “pm”, “in the evening”);
return szTime;
}
This UDF is a very simple example that doesn’t take any arguments. You learn how to build
UDFs that take arguments in the section “Defining function arguments,” a little later on in this
chapter.
The var keyword
Now to take a look at var in more detail. var declares a variable that’s local to a function. If
we didn’t use var, for example, we could do the following:
function myFn() {
myVar = 1;
return TRUE;
}
#myFn()#
#myVar#
After you call myFn(), myVar is available to code outside the function. That’s because any
variable defined inside CFSCRIPT is also available to CFML. This is very sloppy programming
because you could be inadvertently creating or overwriting variables you weren’t intending
to affect.
To keep myVar local to myFn() such that it can’t leak outside the function, use var, as follows:
function myFn() {
var myVar = 1;
return TRUE;
}
21546228 ch17.F 1/30/03 10:51 AM Page 364
365Chapter 17 ✦ Building User-Defined Functions
Now, attempting to use myVar outside myFn() throws an error, which is exactly what you
want it to do.
All variables created by using var must be initialized; ColdFusion throws an error if they are
not. And notice, too, that you cannot place the var keyword anywhere other than at the very
top of a function declaration.
The return keyword
Functions return a single value, as the following example shows:
function myFn() {
return TimeFormat(Now(), “h:mm:ss tt”);
}
As soon as CFSCRIPT encounters a return statement, ColdFusion stops executing the func-
tion and returns the value following the return keyword to the calling code.
You can also conditionally return different values based on different circumstances, as in the
following code:
function myFn() {
if(IsDefined(“Client.myVar”)) {
return Client.myVar;
} else {
return TimeFormat(Now(), “h:mm:ss tt”);
}
}
Every control path in a function must return a value or you get inconsistent results. A best
practice, therefore, is to define a default return value and use only one return statement in
your function declaration, as follows:
function myFn() {
var result = TimeFormat(Now(), “h:mm:ss tt”);
if(IsDefined(“Client.myVar”)) {
result = Client.myVar;
}
return result;
}
Defining function arguments
Most functions take one or more arguments, as the following example shows:
function add2(firstNumber, secondNumber) {
return firstNumber + secondNumber;
}
#add2(1,2)#
21546228 ch17.F 1/30/03 10:51 AM Page 365
366 Part III ✦ The ColdFusion MX Language
The argument names are defined in a list inside the parentheses after the function declara-
tion. You see more advanced uses of arguments in the section “Using the Arguments
Collection,” later in this chapter.
The preceding example used positional arguments, meaning that the first argument in the
function call was passed to the first argument in the function, the second in the call became
the second in the function, and so on. You can also name the arguments in the call if you want
to pass them in a different order, as follows:
#add2(secondNumber=2,firstNumber=1)#
This syntax is not widely used because it is slightly harder to read; it can help you, however,
if you don’t remember the order of a function’s parameters. If any of a function call’s argu-
ments are named, however, all the arguments must be named.
Calling one function from another
Functions can be called anywhere within a ColdFusion template, even from within another
ColdFusion function. Take the following example:
function myFn(myNum, yourNum) {
return myNum * yourNum;
}
function myOtherFn() {
var num1 = RandRange(1,10);
var num2 = RandRange(1,10);
return myFn(num1, num2);
}
Calling functions recursively
A function can also call itself. We wrote the following function, for example, to calculate the
factorial of a number (a factorial will be defined in a moment):
function Fa
Các file đính kèm theo tài liệu này:
- Working with Structures.pdf