Functions are like questions that you ask of Revolution. You give Revolution
some information using a function, and a value is returned to you based on that
information. Some functions, like the date and the
time, return a value reflecting the current state of the system.
The value of a function can change depending on circumstances; Revolution calculates a function's
current value at the time and under the conditions it is called.
Revolution provides many built-in functions. They can appear in two forms: one in which the arguments are enclosed in parentheses and one with no parentheses. So function calls in this format:
put random(15) into myRandom
put sqrt(16) into mySq
are functionally identical to function calls in this format:
put the random of 15 into myRandom
put the sqrt of 16 into mySq
You can also write your own, custom-made functions to do specialized calculations
and data manipulation. Writing a function handler is very similar to writing
a message handler, except that it begins with the term function
instead of on. It must also include a return
statement as the last statement in the handler. Here is an example:
function getStackPath stFileName set the itemDelimiter to "/" delete last item of stFileName return stFileName end getStackPath
More in depth explainations and examples are available in the stack FunctionsLecture.rev, available in the Lectures folder on the class web server. You may download it directly by right-clicking (Windows) or Control-clicking (Mac) on the stack name above and choosing Download link or Save link to disk (or whatever wording your web browser uses) from the popup menu. Or, as always, you can open Revolution and enter
go stack url "http://chum310.byu.edu/Lectures/FunctionsLecture.rev"
into your message box.
Let’s look at a practical problem from a recent exercise that we could solve with a well-written function. In the Web-based Content Assignment you were to devise a way to resize the graphics being displayed so that they would fit into the available space on the card. This means that for each referenced image you display, you need to determine how much it would need to be resized in order to fit. The process is similar for each image and can be solved using a generalized algorithm (a set of well-defined steps)—in other words, this is a perfect candidate for a function.
The algorithm for proportionally resizing an image—or indeed any rectangular object—is straightforward:
the formattedHeight and the formattedWidth in the Revolution dictionary.)newHeight = origHeight X resizeFactor
newWidth = origWidth X resizeFactor
In Revolution the code would look like this:
put the formattedWidth of image "mypic.jpg" into nativeWidth
put the formattedHeight of image "mypic.jpg" into nativeHeight
put 2 into resizeFactor -- assuming you want to enlarge the image by 2X
set the width of image "mypic.jpg" to nativeWidth * resizeFactor
set the height of image "mypic.jpg" to nativeHeight * resizeFactor
We’re part of the way there. In the example above we arbitrarily chose a resize factor—a bad practice if you want to write flexible code. What we need instead is a way to figure out the ideal dimensions, height and width, that we want to constrain the image to. One way to do this is to create a rectangle graphic and size it to exactly the maximum height and width that no image should ever exceed. Then we can calculate how much the image would have to grow or shrink to fit within that area.
In the illustration at right the image is both too wide and too tall to fit within the maximum dimensions outlined by the graphic "maxDim". We’ll work first with the width. By dividing the width of the graphic by the width of the image we can come up with the resize factor. Then we simply use that factor in our algorithm:
put the formattedWidth of image "windmills.jpg" into nativeWidth
put the formattedHeight of image "windmills.jpg" into nativeHeight
put the width of grc "maxDim" / nativeWidth into resizeFactor
set the width of image "windmills.jpg" to nativeWidth * resizeFactor
set the height of image "windmills.jpg" to nativeHeight * resizeFactor
Now, we could just insert this algorithm into our code, and then repeat it with slight variations to adjust the height, as needed. But we want to create a function that will be more useful in a general sense. So imagine a function that would take as input parameters the image dimensions and the maximum dimensions and return the new dimensions for both the height and the width of the image object. It might look something like this:
on mouseUp
put the formattedWidth of image "windmills.jpg" into nativeWdth
put the formattedHeight of image "windmills.jpg" into nativeHgt
put the width of graphic "maxDim" into maxWdth
put the height of graphic "maxDim" into maxHgt
# here is the function call
put findNewDims(nativeWdth,nativeHgt,maxWdth,maxHgt) into newDimensions
set the width of image "windmills.jpg" to item 1 of newDimensions
set the height of image "windmills.jpg" to item 2 of newDimensions
set the loc of image "windmills.jpg" to the loc of graphic "maxDim"
end mouseUp
## this function handler would probably be in the stack or card script
function findNewDims oldW,oldH,maxW,maxH
# check to see if width is different from the max width
if oldW <> maxW then
put maxW / oldW into resizeFactor
put oldW * resizeFactor into newW
put oldH * resizeFactor into newH
end if
# now check to see if the height is too large
if newH > maxH then
put maxH / newH into resizeFactor
put newW * resizeFactor into newW
put newH * resizeFactor into newH
end if
# return the new dimensions
return newW,newH
end findNewDims
While at first glance this may appear a little more complicated than the original code, notice that the new function is written in such a way that you could use it to proportionally resize any object to fit into an ideal-sized area. For example, if I wanted to fit a field into the area defined by graphic "maxDim", while still retaining the field's original height to width ratio, all I would have to do is to call the same function:
put findNewDims(the width of fld "myFld",the height of fld "myFld",the width of grc "maxDim",the height of grc "maxDim") \ into newDims set the width of fld "myFld" to item 1 of newDims set the height of fld "myFld" to item 2 of newDims
A well-written function handler can save you work in the long run by allowing you to reuse code in different settings.
1. Complete the Functions Exercise as outlined in the FunctionsExTemplate.rev stackfile (at http://chum310.byu.edu/Templates/FunctionsExTemplate.rev). Download your own copy from the Templates folder and rename it so your name is on the file.
2. Read Geoff Canyon's article at the Inspired Logic website about functions. While I don't agree with his ideas in every detail, it is an excellent discussion of how to write good functions and when to use functions rather than handlers. At the end of the article is a section called Postscript -- Examples where he gives some ideas for cases in which you might want to write a function. Take the third example, Quoting Text, and write a function in your Functions Exercise stack that will do what the example describes. On the last card at the end of your Functions Exercise stack create a demonstration of the use of this new function.
Turn it in to the homework drop folder when you're done. As always, the key to this exericse is in the Keys folder for your reference if you get stuck.