






































Study with the several resources on Docsity
Earn points by helping other students or get them with a premium plan
Prepare for your exams
Study with the several resources on Docsity
Earn points to download
Earn points by helping other students or get them with a premium plan
Community
Ask the community for help and clear up your study doubts
Discover the best universities in your country according to Docsity users
Free resources
Download our free guides on studying techniques, anxiety management strategies, and thesis advice from Docsity tutors
An overview of first-class functions in LK, a programming language. It covers the basics of functions, including their definition, usage, and the difference between functions and procedures. The document also discusses the concept of constants, built-in functions, and how to write and use libraries of functions. Additionally, it explains how to work with variables and input/output in LK.
What you will learn
Typology: Lecture notes
1 / 46
This page cannot be seen from the preview
Don't miss anything!
Abstract The LK language is a simple but powerful scripting language that is designed to be small, fast, and easily embedded in other applications. It allows users to extend the built-in function- ality of programs, and provides a cross-platform standard library of function calls. The core LK engine, including lexical analyzer, parser, compiler, and virtual machine comprises roughly 7000 lines of ISO-standard C++ code, and is only dependent on the Standard C++ Library (STL), making it extremely tight and portable to various platforms. LK also provides a C language API for writing extensions that can be dynamically loaded at runtime. Note: LK can utilize standard 8-bit ASCII strings via the built-in std::string class, or can be configured to utilize an std::string-compliant string class from an external library. In this way, Unicode text can be supported natively. For example, direct integration and linkage with the wxWidgets C++ GUI library string class is provided as an option.
As with any new language, the traditional first program is to print the words ”Hello, world!” on the screen, and the LK version is listed below.
out( "Hello, world!\n" );
Notable features:
To run Hello world, type it into a new text file with the .lk extension, and load the script into the appropriate the LK script input form in your application.
There are many scripting languages available that have all of LK’s features, and many more. However, LK is unique in its code footprint (less than 4000 lines of C++), portability, and simplicity of integration into other programs. It is also very straightforward to write libraries of functions called extensions that can be dynamically loaded into the script engine at run-time.
Some notable aspects of LK include:
In LK, a program statement is generally placed on a line by itself, and a semicolon ; marks the end of the statement. A very long program statement can be split across multiple lines to improve readability, provided that the line breaks do not occur in the middle of a word or number.
Blank lines may be inserted between statements. While they have no meaning, they can help make a script easier to read. Spaces can also be added or removed nearly anywhere, except of course in the middle of a word. The following statements all have the same meaning.
out("Hello 1\n"); out ( "Hello 1\n"); out ( "Hello 1\n" );
Comments are lines in the program code that are ignored by LK. They serve as a form of documen- tation, and can help other people (and you!) more easily understand what the script does. There a two types of comments. The first type begins with two forward slashes // , and continues to the end of the line.
// this program creates a greeting out( "Hello, world!\n" ) // display the greeting to the user
The second type of comment is usually used when a large section of code needs to be ignored, or a paragraph of documentation is included with the program text. In this case, the comment begins with /* and ends with */ , as in the example below.
/* this program creates a greeting there are many interesting things to note:
Variables store information while your script is running. LK variables share many characteristics with other computer languages.
Variable names may contain letters, digit, and the underscore symbol. A limitation is that variables cannot start with a digit. Like some languages like C and Perl, LK does distinguish between upper
degraded_output = degraded_output - degraded_output * 0.1;
// use parentheses to subtract before multiplication cash_amount = total_cost * ( 1 - debt_fraction/100.0 );
You can use the built-in out and outln functions to write textual data to the console window. The difference is that outln automatically appends a newline character to the output. To output multiple text strings or variables, use the + operator, or separate them with a comma. Note that text strings may be enclosed by either single or double quotes - both have the exact same meaning.
array_power = 4.3k; array_eff = 0.11; outln("Array power is " + array_power + ’ Watts.’); outln("It is " + (array_eff100) + " percent efficient."); outln(’It is ’, array_eff100, ’ percent efficient.’); // same as above
The console output generated is:
Array power is 4300 Watts. It is 11 percent efficient.
Use the in function to read input from the user. You can optionally pass a message to in to display to the user when the input popup appears. The in function always returns a string, and you may need to convert to a different type to perform mathematical operations on the result.
cost_per_watt = to_real(in("Enter cost per watt:")); // Show a message. in() also is fine. notice( "Total cost is: " + cost_per_watt * 4k + " dollars"); // 4kW system
The notice function works like out, except that it displays a pop up message box on the computer screen.
LK supports two basic types of data: numbers and text. Numbers can be integers or real numbers. Text strings are stored as a sequence of individual characters, and there is no specific limit on the length of a string. LK does not try to automatically convert between data types in most cases, and will issue an error if you try to multiply a number by a string, even if the string contains the textual representation of a valid number. To convert between data types, LK has several functions in the standard library for this purpose.
Boolean (true/false) data is also stored as a number - there is no separate boolean data type. All non-zero numbers are interpreted as true, while zero is false. This convention follows the C programming language.
There is also a special data value used in LK to indicate the absence of any value, known as the null value. It useful when working with arrays and tables of variables, which will be discussed later in this document. When a variable’s value is null, it cannot be multiplied, added, or otherwise used in a calculation.
Data Type Conversion Function Valid Values Integer Number to int() approx. +/- 1e-308 to 1e Real Number to real() +/- 1e-308 to 1e308, not an number (NaN) Boolean to bool() "true" or "false" ( 1 or 0 ) Text Strings to string() Any length text string
Table 2: Intrinsic Data Types
Sometimes you have two numbers in text strings that you would like to multiply. This can happen if you read data in from a text file, for example. Since it does not make sense to try to multiply text strings, you need to first convert the strings to numbers. To convert a variable to a double-precision decimal number, use the to real function, as below.
a = "3.5"; b = "-2"; c1 = a*b; // this will cause an error when you click ’Run’ c2 = to_real(a) * to_real(b); // this will assign c2 the number value of -
The assignment to c1 above will cause the error error: access violation to non-numeric data, while the assignment to c2 makes sense and executes correctly.
You can also use to int to convert a string to an integer or truncate a decimal number, or the to string function to explicitly convert a number to a string variable.
If you need to find out what type a variable currently has, use the typeof function to get a text description.
a = 3.5; b = -2; c1 = a+b; // this will set c1 to -1. c2 = to_string( to_int(a) ) + to_int( b ); // c2 set to text "3-2"
outln( typeof(a) ); // will display "number" outln( typeof(c2) ); // will display "string"
To change the value of an existing variable by a certain amount, you can use the combined arithmetic and assignment operators +=, -=, *=, /=. The += is unique in that it can add a numeric value, concatenate strings, and append values to an array (discussed later). The other ones only work on numbers. Examples:
a = 1; a += 2; // same as writing a=a+ a = 2.5; // same as writing a=a2. a -= 1; // same as writing a=a- a /= 3; // after this line, a = 2.
const value = 51; // will cause an error (value already exists)
const names = [ "Linus", "David", "Aron" ]; names[2] = "Ari"; // allowed because it changes the contents of array ’names’ // but not the data type or size of ’names’
names[3] = "Nora"; // not allowed because it changes the size of the ’names’ array names = "Patrick"; // will cause an error
const sqr = define(x) { return x*x; }; // function sqr can’t being changed
Sometimes it is desirable to specify many constants, perhaps to define various states in a pro- gram. For this purpose, an enum, or enumerate statement exists. The state of a motor could be indicated with the constants below, instead of with numbers, which makes a program much more readable.
enum { STARTING, RUNNING, STOPPED, ERROR };
motor_state = STOPPED;
Enumerations start assigning integer values to the names in the list, increasing by one. It is possible to assign a custom values to names in the following ways:
enum { STARTING, RUNNING, STOPPED, EFF=+20, EFF1, EFF2, ERROR1 = 100, ERROR2 };
In this case, everything is the same as before until the EFF variable, for which the +20 specifies that it should have a value 20 greater than the previous name in the enumeration: 22. EFF1 and EFF2 have values 23 and 24. It is also possible to jump to a known value, as with ERROR1.
The enum statement is simply a more convenient syntax to make to multiple const assignments. The enumeration above is semantically equivalent to:
const local STARTING = 0; const local RUNNING = STARTING + 1; const local STOPPED = RUNNING + 1; const local EFF = STOPPED + 20; const local EFF1 = EFF + 1; const local EFF2 = EFF1 + 1; const local ERROR1 = 100; const local ERROR2 = ERROR1 + 1;
The meaning of the local specifier will be explained in later sections.
LK supports many ways of comparing data. These types of tests can control the program flow with branching and looping constructs that we will discuss later.
There are six standard comparison operators that can be used on most types of data. For text strings, “less than” and “greater than” are with respect to alphabetical order.
Comparison Operator Equal == Not Equal != Less Than < Less Than or Equal <= Greater Than > Greater Than or Equal >=
Table 4: Comparison Operators
Examples of comparisons:
divisor != 0 state == "oregon" error <= -0. "pv" > "csp"
Single comparisons can be combined by boolean operators into more complicated tests.
The boolean operators can be combined to make even more complex tests. The operators are listed above in order of highest precedence to lowest. If you are unsure of which test will be evaluated first, use parentheses to group tests. Note that the following statements have very different meanings.
state_count > 0 && state_abbrev == "CA" || state_abbrev == "OR" state_count > 0 && (state_abbrev == "CA" || state_abbrev == "OR")
3.2.3 Multiple if Tests
Sometimes you wish to test many conditions in a sequence, and take appropriate action depending on which test is successful. In this situation, use the elseif clause. Be careful to spell it as a single word, as both else if and elseif can be syntactically correct, but have different meanings.
if ( angle >= 0 && angle < 90) { text = "first quadrant"; } elseif ( angle >= 90 && angle < 180 ) { text = "second quadrant"; } elseif ( angle >= 180 && angle < 270 ) { text = "third quadrant"; } else { text = "fourth quadrant"; }
You do not need to end a sequence of elseif statements with the else clause, although in most cases it is appropriate so that every situation can be handled. You can also nest if constructs if needed. Again, we recommend indenting each ”level” of nesting to improve your script’s readability. For example:
if ( angle >= 0 && angle < 90 ) {
if ( print_value == true ) { outln( "first quadrant: " + angle ); } else { outln( "first quadrant" ); } }
Also note that because LK does not care about spaces and tabs when reading the program text, it is possible to use multiple lines for a long if statement test to make it more readable. The curly braces denoting the code block can also follow on the same line as the if or on the next line.
3.2.4 Single statement ifs
Sometimes you only want to take a single action when an if statement succeeds. To reduce the amount of code you must type, LK accepts single line if statements that do not include the { and } block delimiters, as shown below.
if ( azimuth < 0 ) outln( "Warning: azimuth < 0, continuing..." );
if ( tilt > 90 ) tilt = 90; // set maximum tilt value
You can also use a elseif and/or else statement on single line if. Like the if, it only accepts one program statement, and must be typed on the same program line. Example:
if ( value > average ) outln("Above average"); else outln("Not above average");
3.2.5 Inline Switch Statement
Sometimes you need to select between multiple values based on a number. A quick way to do this in LK is to use an inline switch statement. A value out of a list of expressions is returned based on the numeric value of the test expression. Examples:
choice = 2; value =? choice [ ’aron’, ’peter’, ’rodger’ ]; // returns rodger
a = 2.4; b = 5.6; operator = 1; value =? operator [ a+b, a-b, a*b, a/b ]; // returns -3.
Note that the inline switch statement is simply shorthand for writing a full if-then-else statement. Using the inline switch can make your code more compact when used appropriately.
A loop is a way of repeating the same commands over and over. You may need to process each line of a file in the same way, or sort a list of names. To achieve such tasks, LK provides two types of loop constructs, the while and for loops.
Like if statements, loops contain a ”body” of program statements followed by a closing curly bracke } to denote where the loop construct ends.
3.3.1 while Loops
The while loop is the simplest loop. It repeats one or more program statements as long as a logical test holds true. When the test fails, the loop ends, and the program continues execution of the statements following the loop construct. For example:
while ( done == false ) { // process some data // check if we are finished and update the ’done’ variable }
Just like the if statement, LK allows for loops that contain only one program statement in the body to be written on one line without curly braces. For example:
for ( val=57; val > 1; val = val / 2 ) outln("Value is " + val );
3.3.4 Loop Control Statements
In some cases you may want to end a loop prematurely. Suppose under normal conditions, you would iterate 10 times, but because of some rare circumstance, you must break the loop’s normal path of execution after the third iteration. To do this, use the break statement.
value = to_real( in("Enter a starting value") ); for ( i=0; i<10; i=i+1 ) { if (value < 0.01) { break; } outln("Value is " + value ); value = value / 3.0; }
In another situation, you may not want to altogether break the loop, but skip the rest of program statements left in the current iteration. For example, you may be processing a list of files, but each one is only processed if it starts with a specific line. The continue keyword provides this functionality.
for ( i=0; i<file_count; i++ ) { file_header_ok = false;
// check if whether current file has the correct header
if (!file_header_ok) continue;
// process this file }
The break and continue statements can be used with both for and while loops. If you have nested loops, the statements will act in relation to the nearest loop structure. In other words, a break statement in the body of the inner-most loop will only break the execution of the inner-most loop.
LK script execution normally ends when there are no more statements to run at the end of the script. However, sometimes you may need to halt early, if the user chooses not to continue an
operation.
The exit statement will end the script immediately. For example:
if ( yesno("Do you want to quit?") ) { outln("Aborted."); exit; }
The yesno function call displays a message box on the user’s screen with yes and no buttons, showing the given message. It returns true if the user clicked yes, or false otherwise.
Often you need to store a list of related values. For example, you may need to refer to the price of energy in different years. Or you might have a list of state names and capital cities. In LK, you can use arrays to store these types of collections of data.
An array is simply a variable that has many values, and each value is indexed by a number. Each variable in the array is called an element of the array, and the position of the element within the array is called the element’s index. The index of the first element in an array is always 0.
To access array elements, enclose the index number in square brackets immediately following the variable name. You do not need to declare or allocate space for the array data in advance. However, if you refer to an element at a high index number first, all of the elements up to that index are reserved and given the null value.
names[0] = "Sean"; names[1] = "Walter"; names[2] = "Pam"; names[3] = "Claire"; names[4] = "Patrick";
outln( names[3] ); // output is "Claire" my_index = 2; outln( names[my_index] ); // output is "Pam"
You can also define an array using the [ ] initializer syntax. Simply separate each element with a comma. There is no limit to the number of elements you can list in an array initializer list.
names = ["Sean", "Walter", "Pam", "Claire", "Patrick"]; outln( "First: " + names[0] ); outln( "All: " + names );
names -@ 1; // remove the item at index 1 pos2 = names ?@ ’patrick’; // return -
As previously noted, LK is not strict with the types of elements stored in an array. Therefore, a single array element can even be another array. This allows you to define matrices with both row and column indexes, and also three (or greater) dimensional arrays.
To create a multi-dimensional array, simply separate the indices with commas between the square brackets. For example:
data[0][0] = 3 data[0][1] = - data[1][0] = 5 data[2][0] = 1
nrows = #data; // result is 4 ncols = #data[0] // result is 2
row1 = data[0]; // extract the first row
x = row1[0]; // value is 3 y = row1[1]; // value is -
The array initializer syntax [ ] can also be used to declare arrays in multiple dimensions. This is often useful when declaring a matrix of numbers, or a list of states and associated capitals, for example.
matrix = [ [2,3,4], [4,5,6], [4,2,1] ];
vector = [ 2, 4, 5 ];
outln( matrix[0] ); // prints the first row [2,3,4]
list = [ ["oregon", "salem"], ["colorado", "denver"], ["new york", "albany"] ];
When you define an array, LK automatically allocates sufficient computer memory to store the elements. If you know in advance that your array will contain 100 elements, for example, it can be much faster to allocate the computer memory before filling the array with data. Use the alloc command to make space for 1 or 2 dimensional arrays. The array elements are filled in with the null value.
data = alloc(3,2); // a matrix with 3 rows and 2 columns data[2][1] = 3;
prices = alloc( 5 ); // a simple 5 element array
As before, you can extend the array simply by using higher indexes.
In addition to arrays, LK provides another built-in data storage structure called a table. A table is useful when storing data associated with a specific key. Similar to how arrays are indexed by numbers, tables are indexed by text strings. The value stored with a key can be a number, text string, array, or even another table. A good example when it would be appropriate to use a table is to store a dictionary in memory, where each word is a key that has associated with it the definition of the word. In other languages, tables are sometimes called dictionaries, or hashes or hash tables.
To refer to data elements in a table, enclose the text key string in curly braces immediately following the variable name. You do not need to declare or allocate space for the table.
wordlen{"name"} = 4; wordlen{"dog"} = 3; wordlen{"simple"} = 5; wordlen{"lk"} = 2;
outln(wordlen); // prints ’{ name=4 dog=3 simple=5 lk=2 }’ outln( wordlen{"simple"} ); // prints 5 outln( typeof( wordlen{"can"} ) ); // prints ’null’ (the key is not in the table)
key = "dog"; num_letters = wordlen{key}; // num_letters is 3
Calling the typeof function on a table variable will return ”table” as the type description. LK is not strict about the types of variables stored in a table, and does not require all of the elements to be of the same type.