Lua Scripting for Ecotect

Instructor: Matthew M. Burke
Summer 2008

Lesson 2: Exercises

For these exercises, we will work with a script that displays data for a selected zone from a model. The source code is here.

  1. The script is somewhat fragile as written in that there is opportunity for the user to input invalid data. In this particular instance, the results are not catastrophic: getZoneData will work even if the parameter passed is nil, something other than a number, or a number that does not correspond to a zone (can you figure out for which zone the data is returned if the input is invalid?). But it is not good programming practice to rely on being lucky.

    Modify the script so that it performs error-checking on the user's input. Possible problems include the following: the input is nil (this cold happen if the user clicks cancel); the input is not a number; the input is a number, but is not an integer; the input is an integer, but is less than zero or greater than or equal to the number of zones (recall that zones are numbered from 0 to num_zones-1).

    A few suggestions: the value returned from getUserInput is a string. You will probably want to convert that string to a number, use the Lua function tonumber. If you pass a string that does not represent a number to tonumber, the function returns nil. You can determine the type of a value (string, number, nil, etc.) by using the Lua function type. The return value from this function is a string. A nice touch is to use while or repeat to continue to prompt the user if she enteres invalid data.

  2. The display of the information leaves a lot to be desired. For example: much of the numeric data is displayed out to 14 decimal places; the display is not in columns; zone fields that themselves are tables are not displayed in a useful fashion. Use the printf function to improve the display of the data. A handout describing some of the options to printf can be found here.

    Consider creating several different format string—one for each type of data, and using the type function described above to choose the appropriate format string. We will deal with table fields in the next exercise. If you're ambitious, read the documentation for Lua's gsub command. Use it to improve the display of the field names (capitalize the names, change names such as floorArea to Floor Area).

  3. Now let's improve the display of fields that are themselves tables. Notice that if you pass a table as input to print, the output looks something like table: 0082D468. The number displayed is the memory location where the table data is stored—not all that useful to us. While we are iterating over the fields in the zone_data table, we can perform another for loop if the current field happens to be of type table. In other words, before printing we want to check to see if type(v) == "table" and if so, execute code like the following: for k2, v2 in v do. Ideally, we would make this code that checks the type of the data and prints it appropriately its own function. For the time being, go ahead and code it inline.

  4. One last problem with our display is that the fields are listed in an arbitrary order. This is a direct result of the way Lua represents table data internally. Although Lua does provide a function sort which sorts information in tables, reading the documentation for the function we see that sort only works on those entries in a table that have integer keys (more specifically, only those keys between 1 and getn(table)).

    We can work around this difficulty by building another table of information and using this helper table. There are a couple of ways we can do this and in this exercise we'll step through one solution that is particularly useful. After our call to getZoneData, instead of iterating through the zone_data table, let's first build a table containing the names of the fields in the zone_data table.

    zone_fields = {}
    for k, _ in zone_data do
       tinsert(zone_fields, k)
    end
    

    Note that the table zone_fields has integer indices. So let's use sort on it.

    sort(zone_fields)
    

    Finally, we'll use the zone_fields table to control the order we print out the information from zone_data.

    num_fields = getn(zone_fields)
    
    for i = 1,num_fields do
       field_name = zone_fields[i]
       print(field_name, zone_data[field_name])
    end
    

    When you have this working, try one final elaboration: Rather than having the zone's name show up tucked in between latentGains and occupantActivity, print a nice header including the zone's name and then don't print the zone's name a second time when you are printing out all the fields.


Copyright 2008 Matthew M. Burke. All rights reserved.
Modified: 30 June 2008