Name

eval — Evaluate the contents of string as an expression.

Syntax

eval(string)
              

The eval function has the following argument:

string

A literal string or an expression that returns a string value that will be interpreted as a Patchworks Query Language expression.

Description

This function will attempt to compile the string value that is passed in as a Patchworks Query Language expression. If the compilation phase fails then an error is raised. If compilation succeeds then the expression is evaluated and the result returned.

The string is evaluated and parsed when the expression is defined in order to determine the data type of the result. If input argument is not a literal string (or cannot be reduced to a literal string) then a type hint will have to be provided for the semantic analysis to complete (see the type hint functions asboolean, asint, aslist, asnumber, or asstring). Note that the result of compiling the string does not have to be a literal expression, values from database columns may be referenced.

If the expressions contain quoted strings then the quote characters will need to use the q quoting function or be marked with a '\' escape character to indicate that they are to be embedded and do no demarcate the end of the current string, for example the expression

'C'+1

could be contained in a string as

q(^'C'+1^)   // using the q quoting function with a delimiter of ^
'\'C\'+1'    // escaping single quotes with backslash character

The data type of the eval expression will be the data type of the compiled input expression

Examples

Example 1. A simple literal expression

This first example consists of a trivial literal expression.

eval('1+2+3')

The string value is compiled as a new expression and evaluated. Because it the expression is a literal string expression to be compiled is equivalent to the expression

1+2+3

After the parsing phase the compiler performs constant folding, so the final resulting expression is actually a simple constant integer:

6

Example 2. Another simple literal expression

This next example is much the same, concatenate two literals:

eval(q(^'C'+1^))

This reduces to

'C'+1

which is resolved by the compiler to the string literal

'C1'

Example 3. A complex expression literal expression

The next example is a more realistic case and adds significantly more complexity to the problem. In this example we will compose an attribute expression statement on-the-fly by examining the inputs to the ForestModel. This approach may seem a bit complicated at first, but the benefit is that this type of expression will adapt to the contents of the input data set. If you are building a 'one-off' model this the overhead is not worth it, but for those who regularly build models from scratch this style can be used in a 'template' model that automatically and correctly configures to the supplied stratification variables.

For this example let's assume that in the early section of the MatrixBuilder we have loaded curves with these name labels:

Upland.GROW.Ydfl
Upland.GROW.Yces
Upland.GROW.BA
Upland.GROW.Height
Upland.REGE.Ydfs
Upland.REGE.BA
Dry.GROW.Ydfl
Dry.GROW.Biomass

Here is the expression that we will look at:

eval(join(sort(unique(filter(curveNames(),'.*\.Y(\w+)',
  'attribute(\'%f.Yield.{1}\')'))),'+'))

This expression is hard to comprehend because of the many nested functions, so let's break it down in to the constituent pieces:

eval(                                     1
   join(                                  2
     sort(                                3
       unique(                            4
         filter(                          5
           curveNames(),                  6
           '.*\.Y(\w+)',                  7
           'attribute(\'%f.Yield.{1}\')'  8
         )
       )
     ),
     '+'                                  9
   )
 )

Let's look at these functions from the inside working outwards:

6

The innermost curvenames function returns a literal list of all of the curves that have been loaded in to the Matrix builder. This function is always considered to be a literal: for any program execution it will always return the same curve names that have been loaded up to this point in the program.

5 7 8

The filter function has three arguments. It selects some of the curve names from the first argument that match to the regular expression in the second argument. The third argument is a template string that is used to rewrite the matched curve names in to the output strings.

Since all three arguments to the filter function are literal, the function is evaluated at compile time and replaced with a literal result list. The following shows the curve names that would be matched by the filter and the outputs that would result from the applying the matches to the template. Non-matching curve names are discarded, and a list of the remaining items is returned..

Curve name

Output

Upland.GROW.Ydflattribute('%f.Yield.dfl')
Upland.GROW.Ycesattribute('%f.Yield.ces')
Upland.REGE.Ydfsattribute('%f.Yield.dfs')
Dry.GROW.Ydflattribute('%f.Yield.dfl')

4

Notice that the above table has duplicate outputs in the first and last rows. The unique function will remove the duplicated entries, returning a list that only contains unique entries. Again, since the input is a literal list this function is evaluated at compile time and a literal list is returned.

3

The sort function accepts the list of unique items and returns a list of sorted values. The sort function process the literal input and returns a literal list.

2 9

The join function will join the list items together with the specified join character being inserted between each list item. This function will generate the following literal string:

attribute('%f.Yield.ces')+attribute('%f.Yield.dfl')+attribute('%f.Yield.dfs')

1

The eval function compiles a string into an expression that can be efficiently run against each record. Since the input is a literal string, this is compiled once at compile time, and saved for execution against each record being processed. Of course the attribute functions will return different values for each record, but the form of the expression does not change.

The end result of this example is an expression that ends up being identical in effect to the equivalent long form statement, except that this was produced automatically from a recipe and adapts correctly to the curve values. The runtime performance characteristics of this expression are excellent, because it is compiled once and executed for each record.

Example 4. A simple non-literal expression

For this example assume that we are using the Matrix Builder and operating with the following fragment table:

Figure 192. The sample fragment table

The sample fragment table


We will use the values in the SITECLS and SLOPECLS columns to determine which column to obtain a coefficient value from. For the first row, the SITECLS value is 'A' and the SLOPECLS value is 'low', so the coefficient column that we would like to use is 'A_low'. If we simply use the expression

SITECLS+'_'+SLOPECLS

we will calculate the string value 'A_low' which is the column name, not the column value. In order to get the column value we need to take a further step and evaluate the 'A_low' string as a database column. This expression will generate the value that we want for row 1

eval('A_low')

In order to generalized this expression to apply to all rows we need to evaluate the column calculation expression:

eval(SITECLS+'_'+SLOPECLS)

The child expression returns a string, which is what is required for the eval function. Both the SITECLS and SLOPECLS variables have non-literal values, meaning that they have to be evaluated on each row of the input table and cannot be reduced at compile time to a literal value. This means that the child expression to the eval function is evaluated and complied into a runnable expression for each row of the input table. Although the performance characteristics are poorer than the previous example, this is a very powerful way to compose complex lookups.

Of course this example is somewhat contrived. If the lookups values from site and slope class are invariant (e.g. 'A_low' always returns the same value) then a tablemap function would be more appropriate. If the values are not invariant then the coefficient could be precomputed in each row and stored in a 'COEFFICIENT' column.