JudoScript.COM Design principles of Judo the sport and the language
HomeJudo LanguageJuSP PlatformJamaica Language 
Judo ReferenceJuSP ReferenceWiki/WeblogTutorials/PresentationsDownloadsGoodiesFeedback  
Article: Values, Variables and Data Strcutures
 








This article is old and is being consolidated into the book.
Please refer to the corresponding chapter(s) therein.
If the chapters or sections are not completed yet, you can use this article.
Refer to the examples as they are tested against the latest code.

Table Of Content

  1. Variables and Constants
  2. Primitive Data Types
    » Conversions and Formatting
    » String Processing
    » Enhanced Here-Doc
    » Date and Time
    » Secret Value
  3. Special-Purpose String Methods
  4. Maths
  5. Array and Linked List
    » Statistics
    » Sort, Filter and Convert
  6. Object and Ordered Map
  7. Advanced Data Structures
    » Stack and Queue
    » Set
    » Tree
    » Tabular Data
  8. Summary
  9. Code Listings

Values, Variables and Data Strcutures

By James Jianbo Huang    October 2001  UPDATED: July 2003     printer-friendly version

Abstract   JudoScript has primitive data types and data structures including arrays, linked lists, structs, ordered maps, stacks and queues, and table data. Primitive data types include number, string and date/time and secret. String can also represent URL and file. Arrays can be sorted and filtered with custom comparator and filter functions. The Objects' keys can be obtained in array, either in undetermined order, or sorted and/or filtered by keys or by the values. Comparator and filter functions are frequently defined as lambda functions.


 

1. Variables and Constants

JudoScript is a dynamically typed language, meaning that its variables and constants are not typed, but their values, however, belong to one of these supported types: number, string, date and time, secret, compound data types (data structures), Java objects and arrays, and built-in object types. Variables are not required to be declared first. Value types are interpreted in the program based on the context at runtime. For instance, a boolean expression will interpret the values as integers. This articles discusses the primitive types and data structures.

Variable names start with a letter, underscore or dollar sign ($); and the following can include any of these as well as digits. They are defined by being assigned a value:

a = 1;
Constant names start with a pound sign (#). They are defined like this:
const #PI = 3.1415927;
To check whether a constant has been defined or not, use the defined operator:
if defined #PI { println #PI; }
The println command can be abbreviated as a dot (.).

 

»»» Top |  This Section «««

 

2. Primitive Data Types

JudoScript supports these primitive data types: integer, floating-point number, string and date/time. Boolean values are just numbers: 0 stands for false, non-zero for true. They can be assigned a true or false, which are just 1 and 0. A character is a length-1 string. The null value is numerically equivalent to 0 and textually empty. Numbers and strings are called simple values; they have a set of common methods.

The following program demonstrates how values are used:

Listing 1. simple_vars.judo
 1: println 'A) a = 1: ':<18,       a = 1;
 2: println 'B) a = 1.00: ':<18,    a = 1.00;
 3: println "C) a = '1.00': ":<18,  a = '1.00';
 4: println 'D) a = 1.5e5: ':<18,   a = 1.5e5;
 5: println 'E) a = null: ':<18,    a = null;
 6: println '   a = 1.0:':<18,      a = 1.0;
 7: println 'F) a += 2: ':<18,      a += 2;
 8: println '   a = 1.1':<18,       a = '1.1';
 9: println 'G) a *= 4: ':<18,      a *= 4;
10: println '   a = 10:':<18,       a = 10;
11: println 'H) b = a / 2.5: ':<18, b = a / 2.5;
12: println '   a = 3.5:':<18,      a = 3.5;
13: println '   b = 2.5:':<18,      b = 2.5;
14: println 'I) a % b = ':<18,      a % b;
15: println '   a = 5.0:':<18,      a = 5.0;
16: println 'J) a - c = ':<18,      a - c; // c is not defined.
17: println 'K) a + "abc" = ':<18,  a + "abc";
18: println 'L) "abc" + a = ':<18,  "abc" + a;
19: println 'M) "abc" @ a = ':<18,  "abc" @ a; // concatenation
20: println '   a @= "xyz": ':<18,  a @= "xyz"; // self concatenation
21: println 'N) a + "0x0a" = ':<18, a + "0x0a";
22: println 'O) a + "0x0m" = ':<18, a + "0x0m";
23: println 'P) a + "007" = ':<18,  a + "008";
24: println 'Q) a + "008" = ':<18,  a + "008";
25: println '   a = 5:':<18,        a = 5;
26: println 'R) a + "0x0a" = ':<18, a + "0x0a";
27: println 'S) a + "0x0m" = ':<18, a + "0x0m";
28: println 'T) a + "007" = ':<18,  a + "007";
29: println 'U) a + "008" = ':<18,  a + "008";
30:
31: catch:
32:   println 'EXCEPTION: ', $_.message;
33:   resume;

If this is your first glance of any JudoScript code, it may look too much. But if you look closely, it is really very natural. The start of script and lines 31 and 34 form a try-catch block; when an exception happens, its message is printed and the execution is resumed (line 33). I wish Java had resume statement, too; you never know, someday it might. The major mechanism used in this program is println, or a dot. It takes any number of parameters and prints them one by one to the system standard output. The <18 notion is for formatting: it means "left-aligned with a width of 18". For right- and center-alignment, it is >18 and *18. An assignment expression returns the value itself.

Let us see the result of the run and dive into the gory details of numbers, strings and expressions:

% java judo simple_vars.judo
A) a = 1:        1
B) a = 1.00:     1.0
C) a = '1.00':   1.00
D) a = 1.5e5:    150000.0
E) a = null:
   a = 1.0:      1.0
F) a += 2:       3.0
   a = 1.1       1.1
G) a *= 4:       4.4
   a = 10:       10
H) b = a / 2.5:  4.0
   a = 3.5:      3.5
   b = 2.5:      2.5
I) a % b =       1
   a = 5.0:      5.0
J) a - c =       5.0
K) a + "abc" =   EXCEPTION: Invalid floating-point value
L) "abc" + a =   EXCEPTION: Invalid floating-point value
M) "abc" @ a =   abc5.0
   a @= "xyz":   5.0xyz
N) a + "0x0a" =  EXCEPTION: Invalid integer value
O) a + "0x0m" =  EXCEPTION: Invalid integer value
P) a + "007" =   EXCEPTION: Invalid integer value
Q) a + "008" =   EXCEPTION: Invalid integer value
   a = 5:        5
R) a + "0x0a" =  15
S) a + "0x0m" =  EXCEPTION: Invalid integer value
T) a + "007" =   12
U) a + "008" =   EXCEPTION: Invalid integer value
First of all, strings can be quoted either by single quotes or double quotes (line 4). When single quotes are used, double quote characters can appear as normal text and vice versa. Its concatenation operator is @ (line 20). Integers can be expressed as decimal (line 3), octal (starts with 0 but not "0x"; line 29) or hexadecimal (starts with "0x" or "0X"; line 27). Floating-point numbers can be decimal and fraction digits (line 7) or in scientific notion (line 5).

Lines 7 through 17 demonstrates the auto-detection of number types: JudoScript always tries to use a higher-precision type, that is, floating points over integers. Based on context, a string is evaluated to a number by its "face value" (lines 22, 27 and 29); if the string is not in a valid number format, an exception is thrown (lines 18, 19, 22 through 25, 28 and 30).

Conversions and Formatting

There are times where a specific data type or value range is intended. The simple values have methods for such controls.

Listing 2. conversions.judo
 1: println 'i1 = 62:       ', i1 = 62;
 2: println 'i2 = 12345678: ', i2 = 12345678;
 3: println 'f1 = 54.12345: ', f1 = 54.12345;
 4: println 'f2 = 54.56789: ', f2 = 54.56789;
 5: println 'f3 = 54.56123: ', f3 = 54.56123;
 6: println 'c = "A":       ', c = "A";
 7: println 's = "abcdefg": ', s = "abcdefg";
 8:
 9: println nl, '=== integer <=> float ===';
10: println 'i1.float() = ', i1.float();
11: println 'f1.int() =   ', f1.int();
12:
13: println nl, '=== character <=> number ===';
14: println 'i1.chr() =    ', i1.chr();
15: println 'i2.chr() =    ', i2.chr();
16: println 'f1.chr() =    ', f1.chr();
17: println 'c.ascii() =   ', c.ascii();
18: println 's.ascii() =   ', s.ascii();
19: println 'c.unicode() = ', c.unicode();
20: println 's.unicode() = ', s.unicode();
21:
22: println nl, '=== float and integer precision control and formatting ===';
23: println 'f1.ceil(), f1.floor(), f1.round() =  ',
24:         f1.ceil(), '  ', f1.floor(), '  ', f1.round();
25: println 'f2.ceil(), f2.floor(), f2.round() =  ',
26:         f2.ceil(), '  ', f2.floor(), '  ', f2.round();
27: println 'i2.groupNumber()      = ', i2.groupNumber();
28: println 'i2.groupNumber(4)     = ', i2.groupNumber(4);
29: println "i2.groupNumber(4,'.') = ", i2.groupNumber(4,'.');
30: println 'f2.fractionDigits()   = ', f2.fractionDigits();
31: println 'f2.fractionDigits(2)  = ', f2.fractionDigits(2);
32: println 'f3.fractionDigits(2)  = ', f3.fractionDigits(2);
33:
34: println nl, '=== numbers => Java objects ===';
35: println 'i1.toBoolean() =   ', i1.toBoolean().getClass().getName();
36: println 'i1.toByte() =      ', i1.toByte().getClass().getName();
37: println 'i1.toCharacter() = ', i1.toCharacter().getClass().getName();
38: println 'i1.toShort() =     ', i1.toShort().getClass().getName();
39: println 'i1.toInteger() =   ', i1.toInteger().getClass().getName();
40: println 'i1.toLong() =      ', i1.toLong().getClass().getName();
41: println 'i1.toFloat() =     ', i1.toFloat().getClass().getName();
42: println 'i1.toDouble() =    ', i1.toDouble().getClass().getName();

On line 9, etc., nl inside println stands for newline. Lines 10 and 11 converts the numbers explicitly to float or integer. In lines 14 through 20, method chr() (or char()) treats a numeric value as a character value, where ascii() and unicode() returns a character's numeric value. A character in JudoScript is a length-1 string, so the character value of "abcdefg" is the same as that of "a". Lines 23 through 26 demonstrate how to round up floating point values to integers, and lines 27 through 32 for formatting numbers. Lines 35 through 42 show the methods that convert a number to a Java object; this is useful when calling a Java method that requires precise data types among overloaded methods.

These methods are specifically for formatting numbers:

  • formatBool() or fmtBool(): returns "true" or "false" if the value is interpreted as boolean true or false.
  • formatHex() or fmtHex(): returns the hexadecimal representation of the integer number.
  • formatOctal() or fmtOctal(): returns the octal representation of the integer number.
  • formatCurrency() or fmtCurrency(): returns the currency representation for this value, using the JVM's default locale.
  • formatDuration() or fmtDuration(): returns the English representation of the millisecond value in days, hours, minutes and seconds.
The follow examples show some uses:
println '1 > 2 is ', (1 > 2).fmtBool();
println (231).fmtHex();
dur = ( (2 * 24 + 3) * 3600 + 35 * 60 + 9 ) * 1000;
println dur.fmtDuration(); // => 2 days 3:35:09
Using JudoScript command-line program execution feature, you have a calculator:
%java judo -x println (231).fmtHex()
E7
%java judo -x println 0xE7
231
The last ';' can be omitted. I create an alias "jx" for "java judo -x", so the above lines become really compact.

String Processing

A JudoScript string supports most Java's java.lang.String methods. In addition, it has a number of its own convenience and special purpose methods, because in JudoScript strings also represent many other things. We discuss string processing methods first.

The csv() method turns a character-separated value into an array. It takes a string as its parameter, all characters are separators; if missing, uses comma. This method returns null for two consecutive separators; this is different than java.util.StringTokenizer which discards such values.

s = '2001/10/01||XYZ|12.00|10000';
a = s.csv('|');
println 'Date:   ', a[0];
println 'Name:   ', a[1]; // => null
println 'Symbol: ', a[2];
println 'Quote:  ', a[3];
println 'Volume: ', a[k];
The isEmpty() and isNotEmpty() checks if a string is empty or not. A string is considered empty if it is null or only contains whitespace characters. Method neverEmpty() takes a default string parameter; if the current string value is empty, that parameter is returned. If no parameters specified, when empty it returns a space (which is "empty" itself but what else?) The replace() and replaceIgnoreCase() methods replace substrings with new ones, which is more powerful than Java counterpart (replace(char,char)) that only replaces characters. The following is an application dealing with HTML content:
expr = " <add_expr> ::= <expr> '+' <expr> ";
exprHtml = expr.replace('<', '&lt;')
               .replace('>', '&gt;')
               .replace('&', '&amp;')
               .replace("'", '&quot;');
println <htmlFile> exprHtml;
println <htmlFile> '<td>', x.neverEmpty('&nbsp;'), '</td>';
The following example shows how to use file system commands to rename all files with extension of ".htm" or ".HTM" to ".html".
list '*.htm, *.HTM'; // result is an array stored in $$fs_result
for x in $$fs_result {
  if x.toLower().endsWith('.htm') {
    rename x to x.replaceIgnoreCase('.htm', '.html');
  }
}
This program prints out an ASCII table for 32 to 255:

Listing 3. ascii.judo
 1: cnt = 0;
 2: println '   ', ' 0' @ (cnt++).fmtHex() repeat 16;
 3: println '   ', ' --' {16};
 4: for x from 2 to 15 {
 5:   local base = x << 4;
 6:   print x.fmtHex(), '0:';
 7:   for y from 0 to 0x0f {
 8:     local ch = base | y;
 9:     print '  ', ch.chr();
10:   }
11:   println;
12: }

Lines 2 and 3 print out the header. On line 2, the repeat clause make that expression evaluated 16 times; on line 3, the shortcut form {16} is used. The result for values less than 127 is listed below.

    00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
    -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20:     !  "  #  $  %  &  '  (  )  *  +  ,  -  .  /
30:  0  1  2  3  4  5  6  7  8  9  :  ;  <  =  >  ?
40:  @  A  B  C  D  E  F  G  H  I  J  K  L  M  N  O
50:  P  Q  R  S  T  U  V  W  X  Y  Z  [  \  ]  ^  _
60:  `  a  b  c  d  e  f  g  h  i  j  k  l  m  n  o
70:  p  q  r  s  t  u  v  w  x  y  z  {  |  }  ~ 

String has other methods. count() counts the occurrances of a character within the string. regionMatches() and regionmatchesIgnoreCase() does the same as java.lang.String. getReader() returns a reader that uses the string as its input. Programs can, for instance, read lines from a string.

Enhanced Here-Doc

In the make utility, feature here-doc is used to specify a chunk of text to be enclosed in two user-defined markers and used verbatim by the script. JudoScript supports the same feature with the extension that expressions can be embedded.

lastname = "Robinson";
prodname = "Dry Cleaner";
representative = "Cleo Rubinstein";
letter = [[*
  Dear Mr. (* lastname *),

  Thank you very much for your interest in our product, (* prodname *).
  For more information, please visit our web site at (* url *).

  Sincerely,

  (* representative *)
*]];
flush letter;
The markers are [[* and *]]. Embedded expressions are enclosed in between (* and *). The lines' indentations are removed, so is the first empty new line following the [[* marker, so the code can appear nice and neat. Use [[[* instead of [[* to turn off automatic indentation stripping.

Date and Time

Three system functions create dates and times: date(), time() and timeToday().

Oct1_2001 = date(2001,10,1);
timestamp = date(2001,10,1,15,30,0);
noon = time(12);
tonight6_30pm = timeToday(18,30);
date() takes parameters of year, month, day, hour, minute and second. time() creates a date object with year, month and day fields all initialized to 0. timeToday() is a convenience method that creates a time in today.

Date values (objects) have a list of attributes, some are settable and others read-only. See language specification for a complete listing. It also have a method, formatDate() or fmtDate(), which takes a java.text.SimpleDateFormat format string and optionally a time zone ID, one of those returned by java.util.TimeZone.getAvailableIDs(). If the format string is null, use the default date/time format. The default date/time format can be obtained or set by getDefaultDateFormat() and setDefaultDateFormat() functions.

Listing 4. date_attr.judo
 1: println 'd = ', d = date();
 2:
 3: // get
 4: println 'd.epoch            = ', d.epoch;
 5: println 'd.year             = ', d.year;
 6: println 'd.month            = ', d.month;
 7: println 'd.date             = ', d.date;
 8: println 'd.hour             = ', d.hour;
 9: println 'd.minute           = ', d.minute;
10: println 'd.second           = ', d.second;
11: println 'd.milliSecond      = ', d.milliSecond;
12: println 'd.zoneOffset       = ', d.zoneOffset;
13: println 'd.dstOffset        = ', d.dstOffset;
14: println 'd.weekOfYear       = ', d.weekOfYear;
15: println 'd.weekOfMonth      = ', d.weekOfMonth;
16: println 'd.dayOfMonth       = ', d.dayOfMonth;
17: println 'd.dayOfYear        = ', d.dayOfYear;
18: println 'd.dayOfWeek        = ', d.dayOfWeek;
19: println 'd.dayOfWeekInMonth = ', d.dayOfWeekInMonth;
20: println 'd.isAM             = ', d.isAM.fmtBool();
21: println 'd.isPM             = ', d.isPM.fmtBool();
22: println 'd.monthName        = ', d.monthName;
23: println 'd.monthShortName   = ', d.monthShortName;
24: println 'd.weekDayName      = ', d.weekDayName;
25: println 'd.weekDayShortName = ', d.weekDayShortName;
26:
27: // set
28: println 'd.epoch       = ', d.epoch = 1000,       '  => ', d;
29: println 'd.year        = ', d.year = 1996,        '  => ', d;
30: println 'd.month       = ', d.month = 5,       '     => ', d;
31: println 'd.date        = ', d.date = 25,        '    => ', d;
32: println 'd.hour        = ', d.hour = 8,        '     => ', d;
33: println 'd.minute      = ', d.minute = 15,      '    => ', d;
34: println 'd.second      = ', d.second = 56,      '    => ', d;
35: println 'd.milliSecond = ', d.milliSecond = 430, '   => ', d;
36: println 'd.zoneOffset  = ', d.zoneOffset = 360000, ' => ', d;
37: println 'd.dstOffset   = ', d.dstOffset = 360000,  ' => ', d;

Review Questions

  • Are variables typed? Do they have to be declared?
  • How to define a constant?
  • What are the primitive value types of JudoScript?
  • What is the result type of date(1999,12,1) + 5.35 * 4 - '0x0FE'? What happens to this expression: 12 + 'who'?
  • How to catch exceptions? What does resume do?
  • Given a number such as 64, how to get a character of its value? How to get a charater's numeric value?
  • What is the difference between a string and a character?
  • Values have methods, too. Can their methods be invoked directly like 231.fmtHex()? If not, how?
  • How to limit the number of fraction digits to 3 for a floating-point number?
  • Are boolean values a unique type in JudoScript? How to assign a boolean value? How to print it?
  • Use JudoScript as a calculator, quickly convert between decimal 2345678 and hex 23CACE.
  • The expression a = ( 'a,b,,,e' ).csv(); returns an array of how many elements? Their values?
  • Do the string methods replace() and replaceIgnoreCase() only replace individual characters?
  • How to assign a block of text to a string variable? Can you align the text with the code nice and neat?
  • How to create a date value for 9:12 AM today? How to create a date value for 2:15 PM tomorrow?
  • How to change a time to the next hour?
  • How to print a time in the GMT time zone with default format?

Secret Value

Secret values appear to be regular values but are used only by known parties, such as statements that take passwords. If it is attempted to be printed, it will throw illegal-access exception. To obtain one, use the system function secret():

secret( encrypted_text [ , decryptor ] )
The decryptor is an object that implements the method decrypt(), which takes a string and returns another. It does not matter whether it is implemented in JudoScript or Java, though most likely it is in Java. The encrypted value must be a text string. How to obtain it is up to your crypto package that your decryptor is part of. If no decryptor is specified, by default the value is returned as-is for now. However, this behavior may change, that is, a default decryptor may be used, which is specified in the runtime environment. Let us see an example. Suppose you have run some utility and encrypted your password to "XI,8aM4/", and the decryptor is a Java class.
const #dbPass = secret('XI,8aM4/', javanew com.xxx.util.MyCrypto);

 

»»» Top |  This Section «««

 

3. Special-Purpose String Methods

Strings also represents URLs and file names. String has urlEncode() (encodeUrl()), urlDecode() (decodeUrl()) methods. It also has a parseUrl() method that parses a string into a number of URL parts, returned in a struct. This program also demonstrates a feature called class transposition, on line 23. Class instances can be transposed into objects of another class, normally with more or new methods than the current one.

Listing 5. parse_url.judo
 1: a = [ 'http://localhost:8080/articles/developer.html#top',
 2:       'http://localhost/cgi-bin/check.pl?id=1234&sess=abc',
 3:       '/index.html',
 4:       'index.html',
 5:       '',
 6:       '/',
 7:       'http://www.judoscript.com',
 8:       'images/bg.gif'
 9:     ];
10: for url in a {
11:   b = url.parseUrl();
12:   println '-----------------------------------------------';
13:   println '            URL: ', url;
14:   println '           root: ', b.root;
15:   println '       protocol: ', b.protocol;
16:   println '         domain: ', b.domain;
17:   println '           host: ', b.host;
18:   println '           port: ', b.port;
19:   println '           path: ', b.path;
20:   println '      file_name: ', b.file_name;
21:   println '   query_string: ', b.query_string;
22:   println '            ref: ', b.ref;
23:   println 'constructed URL: ', b.transpose('UrlInfo');
24: }
25:
26: class UrlInfo
27: {
28:   function toString() {
29:     ret = root @ path;
30:     if query_string != null { ret @= '?' @ query_string; }
31:     if ref != null { ret @= '#' @ ref; }
32:     return ret;
33:   }
34: }

You can treat a string as a file path and get various parts:

  a = [ '/usr/bin/java', 'c:/temp/Test.java', '~/alfa', 'judo.jar' ];
  for x in a {
    println x.getFilePath(), ' : ', x.getFileName(), ' : ', x.getFileExt();
  }
The following example shows string's file methods:

Listing 6. file_status.judo
1: f = #args[0];
2:
3: println 'file name: ':<13,    f;
4: println 'file exists: ':<13,  f.fileExists().fmtBool();
5: println 'file is dir: ':<13,  f.isDir().fmtBool();
6: println 'file is file: ':<13, f.isFile().fmtBool();
7: println 'file time: ':<13,    f.fileTime();
8: println 'file size: ':<13,    f.fileSize();

Review Questions

  • How to get the host and path part of a URL?
  • How to get file extension of a file name?
  • How to check if a file exists? Whether it is a file or directory? Its size?

 

»»» Top |  This Section «««

 

4. Maths

Numeric values have a list of mathematic methods. See language specification for a complete listing. The following is some trigonometric examples:

println '------ trigonometry ------';
const #PI = 3.1415927;
println "60' in radian: angle = ", angle = #PI / 3;
println 'angle.degree() = ', angle.degree().fractionDigits(3);
println 'angle.sin() = ', angle.sin().fractionDigits(3);
println 'angle.cos() = ', angle.cos().fractionDigits(3);
println 'angle.tan() = ', angle.tan().fractionDigits(3);
println 'f = ', f = 1.0;
println 'f.asin() = ', f.asin().fractionDigits(3);
println 'f.acos() = ', f.acos().fractionDigits(3);
println 'f.atan() = ', f.atan().fractionDigits(3);
println "60' in degrees: angle = ", angle = 60;
println 'angle.radian() = ', angle.radian().fractionDigits(3);
println 'angle.sin_d() = ', angle.sin_d().fractionDigits(3);
println 'angle.cos_d() = ', angle.cos_d().fractionDigits(3);
println 'angle.tan_d() = ', angle.tan_d().fractionDigits(3);
println 'f.asin_d() = ', f.asin_d().fractionDigits(3);
println 'f.acos_d() = ', f.acos_d().fractionDigits(3);
println 'f.atan_d() = ', f.atan_d().fractionDigits(3);

Review Questions

  • List all the mathematical methods for numeric values.

 

»»» Top |  This Section «««

 

5. Array and Linked List

Array is one of the most important data structures. A JudoScript can contain any variables, including numbers, strings, Java objects and other data structures including arrays. To define or initialize an array:

arr = []; // empty array
arr = [ 1, 'abc', date(2001,10,1), javanew java.util.Hashtable() ];
arr = [ [ 1, 2 ], [ 3, 4 ] ]; // multi-dimension
arr = new Array; // another way to create.
arr = new Array( 1, 'abc', date(2001,10,1) );

Linked lists are desirable over arrays in certain algorithms for performance reasons. In JuduScript both share a common interface; the only difference is its declaration:

lst = linkedList[]; // empty list
lst = linkedList[ 1, 'abc', date(2001,10,1), javanew java.util.Hashtable() ];
lst = [ linkedList[ 1, 2 ], linkedList[ 3, 4 ] ];
The length or size attribute is the number of elements. To add elements, call its add() or append() with one or more values; use prepend() to insert elements to the front; or use insert() to insert at a particular position. Array index is 0-based. You can merge all the elements from another array by appendArray() or prependArray(). Obtain a portion of an array via subarray(). Method clear() removes all the elements, where remove() removes an element at a specific location. To find the index of an element, use indexOf(). reverse() is a convenience method that reverses all the elements.

To enumerate all the elements in an array, do one of these:

for idx=0; idx < arr.length; ++idx {
  local x = arr[idx];
  println idx, '  ', x;
}
for idx from 0 to arr.lastIndex() {
  local x = arr[idx];
  println idx, '  ', x;
}
for x in arr {
  println x;
}
If the index is not used, the last form is the easiest. If the index is used, the second form is more concise.

Arrays can also be created via new operator. This may be useful where {} is syntactically not allowed.

Statistics

These methods collect some basic statistics about all the numeric elements in the array: sum(), max(), min(), average() or avg() and range() (the difference between the smallest and largest values). If elements are all integers, the result is integer, otherwise it is float.

Sort, Filter and Convert

JudoScript has some powerful sorting capabilities for arrays and linked lists via the sort(). This method can take a user-defined comparator function to control the ordering. By default it sorts according to the string representation of the elements. Sorting is done locally, and the array itself is returned.

Listing 7. custom_sort.judo
 1: a = [ '1.2', '3', '3.9', '1.10', '1.2.1', '2.3', '3' ];
 2: a.sort( &my_comparator );
 3: for i from 0 to a.lastIndex() { println i, '  ', a[i]; }
 4:
 5: function my_comparator(lhs, rhs) {
 6:   la = lhs.csv('.');
 7:   ra = rhs.csv('.');
 8:   for i from 0 to la.size() {
 9:     if la[i].int() < ra[i].int() { return -1; }
10:     if la[i].int() > ra[i].int() { return 1; }
11:   }
12:   if la.size() == ra.size() { return 0; }
13:   return la.size() > ra.size();
14: }

Lines 5 through 14 defines a custom comparator. Comparator functions must take two parameters, and return -1, 0 or 1. On line 1, we have an array of things that look like book sections. Normal string comparison fails to yield correct order. Our comparator takes apart the section number to an array (lines 6 and 7), and compares each parts. Line 2 shows how to pass a function to a method: the ampersand notion. The result is:

0  1.2
1  1.2.1
2  1.10
3  2.3
4  3
5  3
6  3.9
As a convenience, JudoScript has sortAsNumber(), sortAtDate() and sortAsString() methods. The last one is not really necessary, just for the sake of naming consistency.

Sometimes you need to pick "qualified" elements in an array. JudoScript's array filtering feature makes this easy. Array have a filter() that takes a user-defined filter function. It can return a new array for the qualified elements, or change the array locally.

Listing 8. filter.judo
1: a = [ 'a', 'ab', 'abc', 'abcd', 'abcde', 'abcdef', 'abcdefg' ];
2: println a.filter(&size_filter);
3:
4: function size_filter elem { return elem.length() < 5; }
5:
6: a.filter( lambda elem { return elem.length() >= 5; }, true );
7: println a;

Lines 1 through 4 shows how a filter function, which takes a parameter and returns a boolean, is used by the filter() method. Line 2 prints a string representation of the array.

On line 6, the second parameter is true, so filtering is done locally. The first parameter is an anonymous function mechanism, called "lambda" function. Functions can be assigned to a variable, for instance,

f1 = &size_filter;
f2 = lambda elem { return elem.length() >= 5; };
println a.filter(f1);
println a.filter(f2);
Our last topic of this seciton is converting elements in arrays. This is done by the method convert(), which, not surprisingly, takes a conversion function that must accept one parameter and return a value.

Listing 9. array_convert.judo
1: a = [ 'abc', 'Abc', 'ABc', 'ABC' ];
2: println a.convert( lambda elem { return elem.toUpper(); } );

Review Questions

  • How to create an array and linked list? How to initialize them?
  • How many ways to add elements to an array and linked list?
  • What is the easiest way to enumerate an array either forward or backward?
  • What kinds of statistics can you get directly from arrays?
  • What is a comparator function? How can they be used in array sorting?
  • Given an array of integers, how to use filter to obtain all even numbers?
  • What is a lambda function? Can functions be assigned to variables or passed as parameters?
  • How to initialize a multi-dimensional array? How to access its elements?

 

»»» Top |  This Section «««

 

6. Object and Ordered Map

A struct and an ordered map are virtually the same compound data structures that holds variable number of named attributes. In JudoScript there is no need to define a struct; simply assign values to an attribute name. Therefore, "map" may be a more appropriate name for struct. Objects and ordered maps are created by the new operator, and may take 0 or more named initializers. Attribute names do not have to be quoted unless they have non-identifier characters. Ordered maps maintains the order of attributes added when you retrieve them; structs do not. This is the only difference between the two. Ordered map also has an indexOf() method that works the same way as for arrays. An ordered map essentially serves the use of an array (for the names) along with a name-value mapping, which can be very handy in some situations.

a = new Object;
a = new OrderedMap;
a = new Object( name = 'Sanjay Deepak',
                title = 'Software Engineer',
                sex = 'm',
                age = 34
              );
a = new Object( 'last name' = 'Deepak',
                'first name' = 'Sanjay',
                title = 'Software Engineer'
              );
To access an attribute, use the dot operator; if the attribute name has non-identifier characters, have it quoted. If an attribute is not defined, a null is returned.
println a.'first name';      // access by name
field = 'first name';  // access by expression
println a.(field);
To remove an attribute, use remove() or delete(). Method size() returns the number of attributes, and clear() removes them all.

Method keys() returns all the keys in an array. It is used to enumerate all attributes. Method values() returns an array of all values.

a = new Object( alfa = 'A', beta = 'B', gamma = 'C', delta = 'D' );
for x in a.keys() {
  println x, ' => ', a.(x);
}
Keys (attributes) or their values can be sorted or filtered. This is a powerful feature that makes in-memory data processing extremely easy. The methods are keysSorted(), keysFiltered(), keysSortedByValue(), keysFilteredByValue() and keysSortedAndFilteredByValue(). All of them may take a comparator function and/or filter function. The following example shows how to use the latter three to achieve some kind of in-memory queries.

Listing 10. in_memory_query.judo
 1: empl = new Object;
 2: empl.'0001' = new Object( name = 'Tony Nugyen',  age = 33 );
 3: empl.'0002' = new Object( name = 'Kathy Murphy', age = 42 );
 4: empl.'0003' = new Object( name = 'Michael Chu',  age = 29 );
 5: empl.'0004' = new Object( name = 'Rick Policsi', age = 30 );
 6: empl.'0005' = new Object( name = 'Sanjay Buch',  age = 23 );
 7:
 8: age_comp = lambda lhs, rhs { return compare(lhs.age, rhs.age); };
 9: age_flt  = lambda elem { return (elem.age >= 30); };
10:
11: println 'List employees by age in descending order:';
12: listThem( empl.keysSortedByValue(age_comp) );
13:
14: println 'List employees older than 30 years old:';
15: listThem( empl.keysFilteredByValue(age_flt) );
16:
17: println 'List employees older than 30 years old in descending order:';
18: listThem( empl.keysFilteredAndSortedByValue(age_flt,age_comp) );
19:
20: function listThem keys {
21:   for x in keys {
22:     local emp = empl.(x);
23:     println emp.age, ' ', emp.name;
24:   }
25: }

Lines 1 through 6 create an Object whose attributes are keys and values are a struct containing various pieces of information. Line 8 defines a comparator function that compares values with the system function compare(); line 9 defines a filter function that filters the values also. These two are used in lines 12, 15 and 18 for various sorting and filtering combinations. As you see, tabular data in memory can be filtered and sorted based on any "column" or "columns" -- it is all in the comparator and filter function you implement.

Review Questions

  • What is the difference between a struct and an ordered map?
  • If an attribute name starts with a digit, how this attribute can be initialized when creating a struct?
  • What is the syntax to access an attribute given a variable for its name?
  • Are attribute names limited to strings? What about their values?
  • How to enumerate through all the attributes in a struct or an ordered map.
  • For a struct, how do you get all keys in a specific order?
  • How to get an array of keys in the order based on its values and/or qualified based on certain criterion?

Stack and Queue

Stacks ane queues are essential to deal with recursive data structures (such as trees and graphs) and algorithms. To create a stack or queue, use the new operator.

Stacks have these methods: size(), clear(), push(), pop(), peek(), peekAt() and isEmpty(). Queues have these methods: enque() or eng(), deque() or deq(), head(), tail() and isEmpty(). The next program traverses a tree in two ways.

Listing 11. dfs_bfs.judo
 1: class Node
 2: {
 3:   constructor {
 4:     assert(name != null); // should be in the initializer
 5:     children = [];
 6:   }
 7:   function toString { return name; }
 8:   function addChild child { children.add(child); }
 9: }
10:
11: // constuct a tree
12: root = new Node(name='ROOT');
13: a = new Node( name='A' );
14: a.addChild(new Node(name='A1'));
15: a.addChild(new Node(name='A2'));
16: root.addChild(a);               // left subtree
17: a = new Node( name='B');
18: a.addChild(new Node(name='B1'));
19: a.addChild(new Node(name='B2'));
20: a.addChild(new Node(name='B3'));
21: root.addChild(a);               // right subtree
22:
23: dfs(root);
24: bfs(root);
25:
26: function dfs root {
27:   print 'Depth-first traverse: ';
28:   stk = new stack;
29:   stk.push(root);
30:   while !stk.isEmpty() {
31:     node = stk.pop();
32:     print node, ' ';
33:     for x in node.children backward { stk.push(x); }
34:   }
35:   println;
36: }
37:
38: function bfs root {
39:   print 'Breadth-first traverse: ';
40:   que = new queue;
41:   que.enq(root);
42:   while !que.isEmpty() {
43:     node = que.deq();
44:     print node, ' ';
45:     for x in node.children { que.enq(x); }
46:   }
47:   println;
48: }

Lines 1 through 9 defines a class. In the constructor, lines 4 mandates that an attributed called "name" must be initialized, and line 5 creates an array attribute "children". A stack is used for depth-first traversal, and a queue for bread-first. On line 33 the for loop runs in backward order so the "left" child is processed first. The tree and the result of run is shown below:

      ROOT
     /    \
    A      B
   /|    / | \
 A1 A2  B1 B2 B3

Depth-first traverse: ROOT A A1 A2 B B1 B2 B3
Breadth-first traverse: ROOT A B A1 A2 B1 B2 B3

Review Questions

  • Use a stack to reverse an array (without resorting to array's reverse() method).

 

»»» Top |  This Section «««

 

7. Summary

JudoScript variables and constants are weakly typed. The primitive types include integer, floating point number, string and date/time and secret. Data structures include struct, ordered map, array, linked list, stack and queue. Other variables include Java objects (and arrays) and built-in objects. Numbers and strings can be used interchangeably in expressions. They can be explicitly converted to intended types. Numbers and strings have a set of methods that covers conversion, formatting, mathematical, and string processing. Strings also represent URLs and file, they have methods like parseUrl(), fileExists(), etc.

A chunk of text can be used via the enhanced here-doc mechanism. Text can be aligned and the indentation is stripped. Expressions can be embedded.

Date and time values can be created in various ways. They have a number of attributes, some are writable.

Arrays and linked lists share the same interface. The only difference is their creation. They can be initialized; elements can be added to the end or front. Other arrays/lists can be merged in. The best way to enumerate an array is the for..in statement. A number of basic statistics method helps the numeric processing. They can be sorted or filtered with user-defined comparator or filter function. Such functions are often declared as lambda functions.

Objects and ordered maps stores a number of name-value mapping. Ordered maps maintain the order of the keys that were added to it, where structs don't. The keys can be returned as an array, which is the way to enumerate sturcts; they may be returned sorted or filtered (by user-defined functions); they can even be sorted and/or filtered based on the values.

Stacks and queues are also supported.

 

»»» Top |  This Section «««

 

8. Code Listings

  1. simple_vars.judo
  2. conversions.judo
  3. ascii.judo
  4. date_attr.judo
  5. parse_url.judo
  6. file_status.judo
  7. custom_sort.judo
  8. filter.judo
  9. array_convert.judo
  10. in_memory_query.judo
  11. dfs_bfs.judo




Copyright © 2001-2005 JudoScript.COM. All Rights Reserved.