> )`$2Mbjbj .E%???8?n@d],@@"AAAAAA]]]]]]]$^hax>]AAAAA>]AAS]PPPAAA]PA]PPrZT[A@p`?B`
([]i]0]6[arOa[[a[0AAPAAAAA>]>]PdAAA]AAAA(..Lazy evaluation and functions composition in Visual FoxPro
Josip Zohil, Koper, Slovenija, august 2013
In functional programming languages (FP) lazy evaluation is a basic way to evaluate functions. In VFP there are two main fields where lazy evaluation matter: VFP Evaluate (and similar) function and SQL queries. Composing functions in lazy evaluation means composing amplified functions or actions. Let us present some examples to illustrate the methods and the problems in composing VFP actions.
SQL and lazy evaluation
The functions in VFP depend on data types but also from the order of evaluation:
Example 1. Normal (strict or eager) evaluation
G(x)=x+5
Z=G(x)**2,
We call these two functions for a value x=2 and get Z=49.
X=2, g(x)=7, z=49
We can compose the two functions in this way: fc(x)=(x+1)**2.
Example 2. Lazy evaluation in SQL
In VFP SQL we can pass functions as an argument with some restrictions. The next two queries don't generate correct results. X is a numeric field in a table test. Its value in the first record is 2 and in the second record is 3.
SELECT f(g(x)) as res FROM test INTO CURSOR xxx &&& 1>1 x>f(x)
This SQL computes g=2 (for x=1) and then ignore this value and computes f(1)=1**2=1, which is not correct.
SELECT f(x+1) as res FROM test INTO CURSOR xxx &&& 1>1 x>f(x)
Also this query doesn't behave in an expected way: instead of x+1 it takes x as the argument and returns f (1) =1.
SELECT (x+1)**2 as res FROM test INTO CURSOR xxx &&& 1>4
In this example the function (x+1)**2 is without name, we can call it an anonymous function.
SELECT evaluate((x+1)**2) as res FROM test INTO CURSOR xxx &&& 1>4
Sometimes we pass a function as an expression enclosed in quotation marks.
SELECT fu(x) as res FROM test INTO CURSOR xxx &&& 1>4
The last three queries behave in an expected way. The VFP engine understand this method of passing functions.
Example 3.
Use test
?s1(5) && 2 (1+1)
The function s1 ignores its argument and returns 2. It takes the value from the table. We shall explain this behavior later.
From this example we can pose a question: How to compose functions in a SQL query (especially complicated functions)?
Evaluate
Let us study the VFP function evaluate. It is more than a function, it is a mapping. Its arguments are amplified functions in various forms and it returns values in a form of values (or functions). We frequently use this function in composing lazy evaluated functions.
The simplest way to pass functions is to use closures, for example:
Example 4.
Function f(x)
Return x**2
Endfunc
Function g() && a closure
?"x",x
Return x+1
Endfunc
F(g()) (A)
We pass a function g() as an expression to evaluate. This expression is similar to a string, but we have not said it is a string!!! It is simply an expression (a VFP function).
A construction (A) is only logic, no values. It is similar to the construction in SQL statements. We can compose functions in a similar way as SQL statements.
Note. Behind the scene SQL composition is based on compositions of functions of a special kind.
Will VFP understand this construction? How will execute it?
We modify the function g(x):
Function glazy() && a closure
?"x",x
Return "x+1"
Endfunc
Now we can run them:
Example 5.
CLEAR
CLOSE data
X=4
g=glazy() && give us a function enclosed in quotation marks
? f(g()) && 25, x=4 && is evaluated here
x=5
?F(EVALUATE(g)) && 36,x=5 glazy is evaluated here
The function (f(g()) takes a value x=4 and returns 25. If we call a function f on a lazy evaluated function glazy, it takes the value x=5 and compute 36.
The function glazy or the composition f(glazy()) is like an application, for example, we apply it on a value x=6:
X=6
? F(EVALUATE(g)) && 49,x=6 glazy is evaluated here
And the result is 49. It computes, when we give it a value. We call this kind of computation: on demand. You can manage infinate streams using lazy evaluation.
You can look at a function f(glazy()) as a macro command: you create a macro and use it in appropriate places. This "macro" command can be seen as a VFP language inside extension.
A difficult exercise: Create a lazy evaluated variant of the function f(x). Compose it with the function glazy. Observe the errors, when you use it in the SQL. Later we shall solve this problem.
Many times in VFP we prefer to manipulate functions as anonymous (without names), for example:
F (x+1), where a function x+1 is without name.
Usually, the expressions are more readable when written as anonymous functions.
In=5
Evaluate(5)
give us an error, because evaluate is lookig for a function to evaluate.
The expression can be written as a function, for example:
Xexpr=long expresion
Function f()
Return xexpr
Endfunc
Eval(f()) = eval(xexpr)
Inside the expressions there can be other functions composed in a correct way.
We call evaluate a function, but it is a mapping and not a function. By definition, a function maps a set (subset) of real numbers to a set (subset) of real numbers( F: R>R). An example:
Function M()
Return 'x+1'
Endfunc
Evaluate(m()) && return a function (string) 'x+1', not a value.
Function N()
If vartype(x)=X
Return null
Else
Return x
Endfunc
A function N() returns a new type (an option), for example an integer or null. Also evaluate(N()) return an option. A mapping N is an amplified function.
X=5
?EVALUATE("N()") && return 5
X=null
?EVALUATE("N()") &&return null
Evaluate has become a new mapping (Funktor on a type option(integer,null)). You can understand a funktor without the exact mathematical definition. It is an amplified function on a type option (integer,null). Evaluate don't manipulate only functions, but also amplified function (For a VFP programmer is more difficult to read the last sentences, than to use them). The VFP SQL engine has to manipulate values in a way similar to a function N(): it has also null values in a table.
The function evaluates operate in two ways:
Strict evaluation, for example evaluate (f (g (x)).
Lazy evaluation. When we use it in the lazy evaluation environment, it doesn't accept nested (higher order) functions in a classic way. Sometimes, if you use nested function as in 1) you get wrong results. How to manage high order functions in VFP?
Composing functions
From a programmer point of view we have to answer this question: Why pay attention to function composition?
We know a function composition, for example, S1(s(x) is a composition of two functions s1 and s. We will compose the two functions in another way and the result will be an equivalent function. The mathematical formula is from monad theory:
Function bindm(f1,f) && for a numeric type
x=evaluate(f1) && extract the monadic value
x=LTRIM(STR(x)) && transform it into a correct form
Return f+"("+x+")" && f+"("+LTRIM(str(eval(f1)))+")"
endfunc
Function composition2(f1,f) &&helper function
Return bindm(f1,f)
endfunc
Function composition3(f1,f,f2) &&helper function
Return bindm(composition2(f1,f),f2)
endfunc
FUNCTION w(x)
wait
return x
endfunc
Example 6.
CLEAR
x=5
?EVALUATE(composition2("s(x)","s1")) &&26
?EVALUATE(composition3("s(x)","w","s1")) && 626
A function bindm is a binder, it composes two functions: it evaluates the first function (extract the monadic value) and on this value applies a second function. The result is a new function, which we evaluate on demand (when we need the result).
The functions composition2 and composition3 are helper functions (syntactic sugar). A composition2 compose two functions and composition3 three functions.
We can look at a function composition3("s(x)","w","s1") as a sequence of actions: first action is s(x) and then w (wait) and then s1. Between the functions s1 and s we have injected an action. At every , we can inject another action. In OO version of this composition we normally use a . ( something like: this.s.w.s1). The , or . are like bind operators. We can read them and then: s and then w and then s1.
Instead of s1(w(s(x))) we can now write an equivalent function composition3("s(x)","w","s1"). Sometimes this notation is better than the anonymous function, for example x**2+1.
Our composition composition3 doesn't contain only functions, for example a function w contains a VFP command wait, so it is better, if we speak about actions' composition.
Composing lazy evaluated functions
Normally we write lazy evaluated functions as closures:
Function glazy() && a closure
?"x",x
m.x=x+1 && create a memory variable (x can be a field in a table)
Return "m.x"
Endfunc
FUNCTION s1lazy()
m.x=m.x+1 && take a computed value (not a field in a table)
?"s1",x,m.x
RETURN "m.x"
Endfunc
In the functions glazy and s1lazy we have added a m. notation, to instruct VFP from where to take values in case x is a field in an open table. In glazy it takes x from a table, creates a memory variable m.x and returns its value. The second function s1lazy takes a computed m.x and returns a m.x. At the end of this article we shall show the problems a programer can have using a m. notation.
Function Lcomposition2(f1,f)
Return Lbindm(f1,f)
endfunc
Function Lbindm(f1,f) && for a numeric type
*xx=EVALUATE(evaluate(f1)) && extract the monadic value
xx=EVALUATE(Lident(f1))
?"xx",xx,f1
Return f+"()" && f+"("+LTRIM(str(eval(f1)))+")"
ENDFUNC
We have a little bit adjust the function bindm into Lbindm. We have a new binder (similar to bindm) to bind functions written as an expressions enclosed in quotation marks.
FUNCTION Lident(k)
RETURN EVALUATE(k) && extract from a container ( a string)
We have introduced a new function (a neutral element). It extracts the functions from the container (a string).
X=2
?EVALUATE(EVALUATE(Lcomposition2("glazy()","s1lazy"))) && 4
This function EVALUATE(EVALUATE(Lcomposition2)) is executed in a lazy way.
Infinite streams
Lazy evaluation helps us in managing infinite streams. When we don't know in advance how long will be a process, we model it as an infinite stream and we stopped it on demand.
An example.
Let us create a program that reads a field's id value of a record in a table test with 10.000 records. It reads the value id from a record till id is less than 1000.
Function idread() && takes a value from its environment a table test
?id
Return id
Endfunc
Use test
Procedure runall
Do while .t.
Idread()
Skip
Enddo
Endproc
The procedure runall will run forever, if the table test has a very large number of records.
Function idread()
?id
If id=1000 or eof()
Exit
else
Return id
endif
Endfunc
We add an if statement to a function idread to end the process, when id is 1000 or end of file.
A function idread is a closure, it is like a lazy evaluated function. It takes its arguments values from the environment where it is called. Observe this two functions applied on a first record
Function idlazy()
?id
Return id
Use test
?idread()
?evaluate(idlazy())
They bahave in the same way and give us equal results. In VFP (sometimes) an experssion can be equal to a closure, p.e. x+1 (expressin) and return x+1 (closure).
Futures or promises
With lazy evaluation you can manage promises (futures). Let us explain what it is in the code from example 4.
1)X=4
2)g=glazy() && give us a promise (future)
3)? f(g()) && 25, x=4 && is evaluated here
4)*A computation
5)x=5 && value is produced (is arrived)
6)?F(EVALUATE(g)) && 36,x=5
In the line 2 we have solved a problem involving values that didn't exist yet. We know only the logic (the function g) and its signature (a type of input and output) we have to apply for a value, eventually will arrive in the future. We read a function glazy() as a promise we will have a value. We don't know, if the value will arrive (be produced) and when will happen this. The last problem when we usually solve by a time out: If the event doesn't happen in a time interval, we conclude, it will never happen and we react: a promise is broken.
On the other side, when the value arrive (line 5), we evaluate it (line 6) and it returns a value.
In line 4 we have switch the application to another program (thread of execution). When it ends, we eventually evaluate (6).
To be more realistic, we put the last three statements in a loop with a time out.
K=1
X=null
Do while k<10
K=k+1
computation(k) && A computation that produces eventualy x
If vartype(x)=N
?F(EVALUATE(g))
Endif
Enddo
If k>=10
Messagebox(Time out)
Endif
Function computation(x)
If k=3
Return X
Endif
endfunc
This program in a loop runs an application compute(1) and after that test, if x is produced. If it is not, it repeats the loop by running another application and so on.
The command F(EVALUATE(g)) in the last program execute asynchrounosly. We have created the logic in line 2). It will be eventually executed asynchrounously when the value of x will be a numeric value. This program has an added value: The asynchrounous process don't block. If the computer has another task to execute (function to compute), it can do that, instead of waiting for an asynchrounous message.
Lazy evaluation in programs like the last and promises or future are appropriate code to run asynchronous code in VFP.
Note. Promises (futures) are lazy evaluated function composition.
Continuation function
A function composition2("s(x)","s1") can be read also in this way: the function s1 is a continuation of the function s.
Let us analyze this question: What are the conditions a function s1 can be a continuation of s? There are multiple response to this question. Let us illustrate two:
The types of the two functions must match. The result of the first function s is numeric and the input of the function s1 have to be of type numeric.
How to solve the problem, if the result is of type logical or is null or void. For example:
Function f(x)
X=x+1
Endfunc
Function callback(obj,func) as void
?func
endfunc
These two functions return:
F a logical value .T.
callback void.
The return values of these two functions are unusable. You can't compose them with a new function. It is better, if a function has a return value.
Conclusion
In this article we have shown a method of function composition when we evaluate them in a lazy way. In some programming languages this is called actions composition. Technically we transform a composition of nested functions in a sequence of actions. Between two actions we can inject new actions: injecting new actions become a very simple job. The code is readable.
From the point of view of the language this action sequencing and injecting is a language inside extension that gives a programmer the opportunity to create programs in a new way.
Addition: Composing functions in VFP a problem
Let us see a problem we have in lazy evaluation of of composed functions.
Example.
FUNCTION s(x)
?"before",x,m.x,x**2
xx=x**2
?"after",xx,x,m.x
return xx
endfunc
FUNCTION s1(x)
RETURN x +1
ENDFUNC
A function s2(x) is a new version of a function s1(x). We replace the variable x with xx.
FUNCTION s2(xx)
RETURN xx +1
Endfunc
clear
CLOSE DATABASES
m.x=999
?"s1",s1(x) &&1000
?"r01",s1(s(x)) &&998002
USE test
SKIP
SKIP
*x=3, quantity=8 two field values in a third record
?VARTYPE(m.x),m.x && N 999
?"r1",EVALUATE("s1(s(x))") && 4
?"rq",EVALUATE("s1(s(quantity))") && 4 ignore the argument
?"r2",EVALUATE("s2(s(x))") && 10 && correct result
The functions s1 and s2 ignore their arguments: they are not functions in a mathematical sense. When we evaluate s1(s(x)) on a record with a field name x and value x=3, we get a wrong result 4. If we change the argument to quantity (another field with a value 8), we get the wrong result 4. We can conclude that evaluate ignore the arguments and use variables from the environment (a closure).
Note. In VFP help, we can find the explanation that a filed's variable, in our case x, has precedence over a memory variable x. But millions of people over the world knows that s1(9) is 10. This VFP behavior is in contradiction with a mathematical definition what is a function.
The workarounds to this problem are:
1) use a new function s3. In it we force the function to take the values from its memory (m.x +1); the computed values. But this can't be a general solution. For example, if you reverse the order of the two functions s(s3(x)), you will get wrong results, because s will not take the correct computed values.
Don't use composed functions, but write them in an explicit way:s1(s(x))=x**2 +1. Sometimes this is not a clever solution.
The function s1 is a continuation of the function s. It's argument names must be different from the opened table fields names, for example s1(k).
Note. In mathematics f(x)=X**2, give us always f(2)=4. The functions applied to values in a VFP table, don't behave in this way. It seems they behave as a closure, ignoring arguments. The solution to this problem is in using this VFP mathematic functions as closures (without parameters). In it you must explicitly manage the order of execution. In non functional languages are very difficult to manage high order functions in a lazy way.
Note. VFP start for example with this variables m.x, x (a field). It computes a first function s(x), and creates a new closure with a new computed value mm.x. In the new closure it can access only the variable x and a new mm.x. The second function s1(x) can take as variables (concurrent) x and mm.x. Because of conventions it takes x (if it has not an exact instruction to take m.x that is, in this closure, mm.x). If the function s1(x) has a notation m.x (p.e. m.x=x+1), a variable m.x is the computed m.x, not the original.
PAGE
PAGE 8
);<hi =
$
*
`
g
k
./69PQr"(57y(
M
U
տոձΪêǡ՚՚ՓǡՓh&5Mhh&5Mh&5Mh&5Mh$%0Jh&5MhDr<h&5Mh}qIhh}qIh[nh&5Mhh$%h&5Mh3h&5Mh$%hlzhhlzh&5Mhlzh"h$%hhlzh$%:;<gh
`
./Q67y(
3zgdgd$%gdgd$%gd$%M1M
%&3z"*LWmnuwy "dnXi/5й
hD0JhtJh$%h&5Mh` 4#h$%B*CJOJQJ^JaJphhhDr<h&5MhDr<0Jh&5Mh}qIh&5MhDr<hh$%h&5Mhh&5Mh&5Mh&5Mh$%0Jh&5Mh&5Mh$%6zny
6AI`PgdtJ 7$8$H$gd$%gd$%gd56P[`OPghm FGst),KPab(ްްީޥޥޥްްӞhyh$%h&5Mh&5MhKh&5Mh&5Mh&5Mh&5M0J
h[n0Jh&5MhttE
hD0JhtJh$%h[nh&5Mh$%h&5Mh"0Jh&5Mh$%0JhtJh$%0JBNPUYjN`apz01>G]c̶̶̶̶̼̼̼hyh
hD0Jhyh!0Jhyh!hyh$%0Jhyh"htJh$%h[nhyhyh$%hyhyhyh&5MEUly*>NUbj&N1] gd3gdtJ ' !!("*"W"Y"""#####$$S$T$$$%%%%%7&8&&&&&F'G''''''''(ɺɱݣݟݟݘҘh3hh3hyh3h3hyhyhDr<hyhttEhtJh30Jhyhy0J
hD0Jhyh30JhtJh3h3hyh3htJh$%hyhhyh$%hyhy3
!!("W"""""##K#o#w##########$$%%G'gdtJgd3G''(((()!)*)p)})))))))+++++(,B,P,,,67gd$%gdtJgdtJ((((((())!)#)))*),)))))))))))))%++++++(,*,B,D,O,P,,,,,"4567иЯ٨Дzh&5MhhhtJh77m0J
h+;0Jh3h77m0JhtJh&bh[nh3h&bh3h&b0Jh3hC.0JhtJhh;0J
hD0Jh3hh;0Jh3hh;hC.h3hC.htJh/h3h3h3h307J8.9......H/T//A0G0Q0Y0Z0c0t000000gd$%gdtJgdh;gdtJ8.9.G.f.h..........G/H/T/////$0%0A0C0X0Y0Z0000smg^mh3h:NV0J
h=0J
h+;0Jh3h30Jh3h=0Jh3h=h4h3h4h3hSihSih3h$%h3h/hh;#hh;B*CJOJQJ^JaJph
h0Jh3h0JhtJhh;h3h77m0JhtJh&b0JhtJh77mh3h&bh3#000000000001111"1+1,1.1314191;1E1F1S1T1[1n1111111A2v2w22d3f3g3m3z3{333333333ɼݸݸݰh3hh3hh3h/h3hMh3h}qIhh'h}qIh:NVhCeh3hSi
hCe0Jh3hdk0Jh=h3h3hh3h=
h+;0Jh3h=0Jh3h}qI0J400011,13191E1L1T11w2222222e3f3g3{33334gdtJgd}qIgd}qIgddkgd$%3333333344444I4J444444444445Z5]5g5q555566666666666666666777'7/70717777ˤҤҤҤĤĤĤh3hYwh3h:NVh3hBh:NVh3h3h3hhtJhh3hN*htJhttEh3hJYh3hMh3httEhtJh}qIh3h}qIh3hh3;4J4[4444617788
88#8a8t88888888888889:;gdtJ777788
888#8%898S8T8U8_8a8b8t8w8888888888888888$949?9C9D9h9o99999:%:&:/:>:S:k:9;:;t;z;л߲h3hUBh3h3htJhqhi%hDh3hqh3h:NV0J
hq0J
hD0Jh3h30Jh3hq0J
h+;0J
h:NV0Jh3hN*0Jh3h3hN*htJhN*8z;;;;;;;;<<<<<<<==$=)=*=m=u=x=z=====>>>*>,>V>X>^>`>n>s>>>>>>>A?C?N?O??ټٵٮåُhh3hh
h3h
h3h
0J
h+;0Jh3h0Jh3hh3hOCh3h3htJh/h3h*hDh/h3h/htJhN*htJhUBh3h3hJh3hUB1:;;;;<<<*==>*>2>:>^>f>n>>>>B?C?N?O?@sAAAgd3gdtJgd
gd$%gdSigdtJ????@@@@@@@ A)AFAQAsAA}AAAAAAAAAAB BBB B(B*B;BC?CgCüh3h0JhtJh$%h3hhtJh$%0J
h+;0Jh3h$%0Jh3h$%htJhGzhGzh3h3h3hGzh3h*h3hhh3h3h3h3h[5AAAABB(BI?IIIIIIIII&J5JaJqJ{JJJJJJJJJJJJ9KLM M
MȺֳ묨묡htJhi%h@hAhi%h$%hlzhJhlzh!hlzhlzh$%htJhhtJh6hlzh*hlzh` 4hlzhZhlzhhtJhGzhlzhhlzhlzhlzh1hlzh62
MMM
MMMMMMMMMM M!M"M#M$M*M+M,MM.M/M1M2Mh+;0JmHnHuhD
hD0JjhD0JUhzHjhzHUh6htJh$%,1h. A!"#$%@@@$%NormalCJ_HaJmH$sH$tH$\@\$% Heading 2$<@& 56CJOJQJ\]^JaJV@V$% Heading 3$<@&5CJOJQJ\^JaJDA@DDefault Paragraph FontRi@RTable Normal4
l4a(k@(No ListdOd$%Heading 2 Char056CJOJQJ\]^J_HaJmH$sH$tH$>b@>tJ HTML CodeCJOJQJ^JaJZY@Z[nDocument MapD M
CJOJQJ^JaJ4 @"4DFooter
p#.)@1.DPage Number2E;<gh`./Q67y(3zny

6
A
I
`
Pm
G
K
}
b=nUly*>NUbj&N1]
(WKowG !!!*!p!}!!!!!!!#####($B$P$$$6%7%J%%%%%8&9&&&&&&H'T''A(G(Q(Y(Z(c(t(((((((()),)3)9)E)L)T))w*******e+f+g+{++++,J,[,,,,.1//00
00#0a0t00000000000001:3333444*556*626:6^6f6n6666B7C7N7O78s999999::(:<:F:N:]:j:r:::::: ;;$;?;H;M;R;;;;<6<=>>0@@?AB
EEEEEEEEEEE!E"E#E.E/E0E3E00000(000000000000000000000000000000(00C0C0C0C0C00C0C00C0C0C00C0C0C00C00C0C0C0C0C0C0C00C0C0C0C0C0C0C0C0C0C00C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C00000C0C0C0C0C0C0C0C0C0C0@0=0=0=0=000=0=0=0=0=0=0=0=0=00=0=0=0=0=0=0=0=0=0=00=0=0=0=00 0 0 000 0 0 000 0 00 0 0 0 0000 0 00 0 0 0 0 0 0 0 00 0 00%0%0%0%0%00%000%0%0%000%0%00%0%0000000%00000000000%00(00(0(0(0(0(0(0(00(0(0(0(00000000(0(0(0000000000(00(00/00/00/0/00/0/00/0/0/0/0/0/00&30&30&300V50V50V50V50V50000V50V50V500V50V50V500V50V50V50V50V50V50V50V50V50V50V50V50V50V50V50V50V500V50V5000V50V5%00%00%00%00%00%00@0%00@0%00%00@0@0%00%00;<gh`.Q67y(3z 
6
A
I
m
G
K
}
bUbj&(WKwG !!!*!p!}!!!!!B$P$$J%%%8&&&()9)L)T){+,J,[,,,,00#0a0t000013344n6j::::H;M;R;;;;<3E@0%0%00@0@0@0@0@0@0@0@0@0@0@0@0@0@0@0@0@0@0@0@0@0@0@0@0@0@0%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00
%00%00%00%00%00%00%00%00
%00%00%00%00%00%0B0Cd%0B0%0B0%0B0%0B0%0G0Hd%0G0%0G0%0B0%0B0Cd%0B0%0B0@0%0F0Gd%0F0%0F0@0%0J0%0P0
@0
@0
@0%0N0%0N0%0P0!Qd%0P0 %0P0%0O0%0T0!UXd%0T0 %0T0%0O0%0X0&Yȟd%0X0%%0N0%0N0%0N0%0N0%0N0%0N0%0N0%0O0Pd%0O0%0O0%0N0
%0N0%0N0
%00%00%0i0%00
%00
%0Y0%0m0nd%0m0%0m0%0p0%00%0r0sP\%0r0%0r0%00%0i0%0i0%00%00
%00
%00
%00%00
%00%00%00%00%00
%00%00
%00
0&0 $$$'
5((037z;?gCG
M2M'*,.03568:;=?ABzP G'704:;A6D2M(+/12479<>@1M) '!!(()))),).)3)4)9);)E)F)00#0%0a0b0t0w000000000*6,6^6`6: :: :(:*:]:^:::3E:<fh
_`/PQ57xy'(23yzmnxy
,
/
5
6
@
A
H
I
_
`
OPlm
F
G
J
K

}
ab<=mnTUklxy),=BMPTYabij%&,MN01\]
'*VYJKnovw
FG !! !#!)!,!o!p!!}!!!!!!!!!!!!!##########'$*$A$D$O$P$$$$$5%7%I%J%%%%%%%7&9&&&&&G'H'S'T'''@(C(F(G(P(Q(X(Z(b(c(s(t((((((((((((())))+).)2)4)8);)D)F)K)L)S)T)))v*w*************d+g+z++++++,,I,J,Z,[,,,,,..0/1///00000
000"0%0`0b0s0w000000000000000000000001193:33333
4444)5*55566)6,6162696:6]6`6e6f6m6n6666666A7C7M7O788r9s9999999:: ::: :':(:*:;:<:E:F:M:N:\:]:^:i:j:q:r::::::::::::; ;;;#;$;>;?;G;H;L;M;Q;R;;;;;;;<<5<6<==>>>>/@0@@@>A?ABB EEEEEEEEEEEEE0E3E(()))),).)3)4)9);)E)F)00#0%0a0b0t0w000000000*6,6^6`6: :: :(:*:]:^:::E3E::EEEEEEEEEEEE0E3Ef p1i(LW9O:^`o()
^`hH.
pLp^p`LhH.
@@^@`hH.
^`hH.
L^`LhH.
^`hH.
^`hH.
PLP^P`LhH.hh^h`o()
88^8`hH.
L^`LhH.
^ `hH.
^`hH.
xLx^x`LhH.
HH^H`hH.
^`hH.
L^`LhH.^`o()
^`hH.
pLp^p`LhH.
@@^@`hH.
^`hH.
L^`LhH.
^`hH.
^`hH.
PLP^P`LhH.^`o()
^`hH.
pLp^p`LhH.
@@^@`hH.
^`hH.
L^`LhH.
^`hH.
^`hH.
PLP^P`LhH.f 19O(L\X]$$$$$$$$$$$$$$$$$$$$$$$$$[V$$$$$$$$%&LFEA
/1*H>,` 4!9Dr<=UBOCDttEzH}qI&5MO>:P:NVJYZ&bbCeSidk77m}uYwGzi%tJdqlzKC.3Mj`+;6[nh;y"BmN*[J$% O@'q43!@::4::2E`@UnknownGz Times New Roman5Symbol3&z Arial?5 z Courier New5&zaTahoma"1b''{ N
:#}N
:#}4dDD2QHX $%2 Lazy evaluation in Visual FoxProjosipokOh+'0 $
DP
\hpx$Lazy evaluation in Visual FoxProjosipNormal.dotok9Microsoft Office Word@"Y@#@:N
:՜.+,0hp
}#D'!Lazy evaluation in Visual FoxProTitle
!"#$%&'()*+,./0123456789:;<=>?@ABCEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstvwxyz{~Root Entry F0v1TableDaWordDocument.SummaryInformation(uDocumentSummaryInformation8}CompObjq
FMicrosoft Office Word Document
MSWordDocWord.Document.89q