Working with Structures

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.

pdf124 trang | Chia sẻ: tlsuongmuoi | Lượt xem: 2224 | Lượt tải: 0download
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:

  • pdfWorking with Structures.pdf
Tài liệu liên quan