歡迎訪問 Lu程序設計

Lu32.DLL V1.0 用戶指南

目  錄  [頁首]

1  概述
簡單的例子
標識符
表達式簡介
數據類型
常量和變量
賦值語句
算術運算
關系運算符和邏輯運算符
10 逗號(冒號、分號)運算符和括號運算符
11 運算符及優先級
12 函數概述
13 函數的傳值調用和傳址調用
14 用表達式作為函數的參數
15 表達式的完整定義及表達式句柄
16 流程控制

17 字符串
18 數組
19 Lu表
20 動態對象與指針
21 對象成員運算符(函數參數運算符)及對象賦值運算符

22 遞歸調用
23 模塊化編譯
24 模塊命名空間

25 二級函數命名空間
26 常量命名空間
27 訪問命名空間
28 二級函數
29 運算符及函數重載
30 協程
31 動態內存管理
32 表達式的初始化及銷毀
33 錯誤處理
34 標識符解釋規則
35 關鍵字
36 效率
37 主要技術指標

正文

1 概述  [返回頁首] [返回目錄]

    Lu來源于Forcal,可以說,沒有Forcal就沒有Lu,但學習Lu并不需要了解Forcal。

    Lu是對Forcal的完善和發展,但與Forcal相比,Lu更簡潔實用。Lu的運行效率,從單純的數值計算和循環來說,是下降的,其他方面,特別在涉及動態對象的地方,Lu的效率會提高。Lu與Forcal本質的區別在于:Forcal以整數、實數、復數三種簡單數據類型為基礎,在描述復雜對象時,本身不帶有數據類型信息,故是弱類型的;Lu以一種結構體作為基本數據類型,攜帶數據類型信息,故是強類型的。

    Lu是一個可對字符串表達式進行動態編譯和運行的動態鏈接庫(dll),是一種易于擴展的輕量級嵌入式腳本, 支持協程,提供自動內存管理,也可以手動管理內存。Lu用于連接各種語言編寫的模塊。Lu的優勢在于簡單易用和可擴展性強。

    Lu語法由核心庫(Lu32.dll)、擴展庫及主程序提供。本文主要介紹核心庫提供的語法,非核心庫語法部分將會特別說明(標記為紅色部分)。

    可以用任何一個加載Lu核心庫的程序演示本文的代碼,因不同程序對Lu語法的擴展不同,故非核心庫語法部分不能保證一定運行成功,請參考程序的說明。

    推薦的演示程序:(1)簡單演示程序 DemoMLu32.exe;(2)簡單演示程序 DemoLu32.exe;(3)開放式演示程序 OpenLu32.exe。

    Lu核心庫中主要包含一個編譯器、一個執行器和一個鍵樹用以保存各種數據。Lu核心庫系統 結構如圖1所示。

圖1 Lu核心庫系統結構圖

2 簡單的例子  [返回頁首] [返回目錄]

      F(x,y)=x+y;       //函數定義
     
2+F[2,3]+5;       //簡單的計算式

    在這個例子中,分號表示一個表達式的結束,兩個反斜杠//后的內容表示注釋,本文中將一直采用這種表示和注釋方法。
    在第一個表達式中定義了一個函數 F ,小括號( )內的x,y為函數的自變量,等號后為函數體,即可執行的部分,該函數計算并返回兩個參數的和。在第二個表達式中調用了上面定義的函數 F 。
    需要注意,在一般的Lu程序中,只計算無自變量的表達式。因此,對第一個表達式只編譯,不計算。

3 標識符  [返回頁首] [返回目錄]

    在Lu中,一個標識符可以用來表示一個表達式的名字,或者是一個函數的名字,或者是一個變量,或者是一個符號常量。標識符可由一個或多個字符組成,可以任意一個英文字母 、中文字符或者下劃線開頭,后面可以接英文字母、中文字符、數字或者下劃線(下劃線的使用可以增加標識符的可讀性,如first_name)。注意:英文字母的大寫和小寫是不同的,例如:count和COUNT是兩個不同的名字。下面給出一些合法的標識符:

      first    last    Addr1    top  _of_file    name23    _temp    a23e3    MyVar    您好    數1    My數

4 表達式簡介  [返回頁首] [返回目錄]

    表達式是Lu的編譯單元,如果沒有語法錯誤,編譯后將生成可解釋執行的中間代碼。

    Lu表達式可以很簡單,例如只有一個數字;Lu表達式也可以很復雜,例如是一段復雜的程序。以下是一些簡單的表達式例子:

      2;
      2+3;
      2.0+sin(3.0)-ln[6.0];

    Lu表達式有無名表達式和有名表達式兩種,有名表達式即函數,如下例:

      A()=2;
      B()=2+3;
      C()=2.0+sin(3.0)-ln[6.0];

    給表達式起一個名字,使之成為函數,這樣做的好處是:以后可以用函數調用的方式通過該名字調用該表達式。表達式的名字是一個標識符,必須位于表達式的開頭。

    表達式(函數)定義的一般形式是:

      Name(a,b)={a=2,b=10:b=b+a;a+b}

    其中 Name 為表達式的名字,不可與其它已經定義的表達式名字相同,但可以缺省;如果定義了表達式名字,表達式名字后必須跟一對小括號(只能用小括號),用來定義自變量,自變量個數可以為零,也可以有任意多個,有多個自變量時,自變量間以逗號分隔;如果用小括號定義了自變量,小括號后必須跟一個等號,該等號標志表達式可執行部分的開始,等號及等號后的可執行部分均不可缺省。表達式的可執行部分由多個語句組成,多個語句之間用逗號、冒號或分號分隔,表達式總是返回最后一個語句的值;另外花括號不是必須的,但有了花括號后,表達式更易讀。

    下面是一個函數定義及使用的例子。

      相加(加數1,加數2)=加數1+加數2;    //函數定義
      2+相加[2,3]+5;      
             //函數調用

    以下是一些合法的表達式例子:

      2;            //常量(無參)表達式
      ()=2;         //常量(無參)表達式
      A()=2;        //常量(無參)表達式(函數),在其它表達式中可以調用該函數
      B(x)=2;       //有一個自變量的表達式(函數)
      C(x,y)=x+y;   //有二個自變量的表達式(函數)

5 數據類型  [返回頁首] [返回目錄]

    Lu數據類型如下表所示:

表5-1 Lu數據類型

類別 基本數據類型 標識符 例子1 例子2 例子3 說 明
靜態類型 nil nil nil     未定義的Lu數據或某個運算失敗的返回值。
表達式(函數)句柄   @fun HFor("fun")   由編譯符@或函數HFor獲得。
64位整數 int 12 0x1A 0x123D 64位整數(范圍從-9223372036854775808~9223372036854775807),既可以是10進制數,也可以是16進制數,但數字中不能包含小數點,也不能用科學記數法表示數字。16進制整數以0x開頭,并用A~F表示10~16。
64位雙精度實數 real 1.2 3. 10.3E8 64位雙精度實數(范圍大致從±1.7E-308~±1.7E+308),含小數點,可以用科學記數法表示數字。
復數 complex 2i 1+2i (2$3) 復數(實部和虛部都是64位雙精度實數),以i結尾的數字,或者由運算符$產生。
三維向量 vector (1$2$3) [1$(2$3)] (2i$3) 三維向量(三個分量都是64位雙精度實數),由運算符$產生。
長精度實數         未啟用。
邏輯值 true / false true false 2>3 邏輯值,只有邏輯真和邏輯假兩個值,關系運算或邏輯運算將返回邏輯值。
特殊常量 all all     由系統定義,直接在腳本中使用,其意義由使用這些常量的函數解釋。
data data    
public public    
private private    
protected protected    
virtual virtual    
row row    
rank rank    
inf / INF inf INF  
-inf / -INF -inf -INF  
nan / NaN nan NaN  
self self    
method method    
靜態字符串   "..." @"..." @"\[22]" 靜態字符串。
靜態64位一維整數數組   intss("...") intss("\&\[10]")   靜態64位一維整數數組。
靜態64位一維實數數組   realss("...") realss("\&\[10]")   靜態64位一維實數數組。
與字符串對應的32位整數   #abc #ab_c   由字符串生成的唯一的32位整數,用于系統擴展設計,用編譯符#獲得。
系統定義動態類型
動態64位實數數組 reals new[reals,5] new[reals,2,5] new[reals,data:2.0,3.6] 動態64位實數數組。可以是多維數組。
動態64位整數數組 ints new[ints,5] new[ints,2,5] new[ints,data:2,3] 動態64位整數數組。可以是多維數組。
動態字符串 string new[string,5] new[string,5,"..."] new[string,"..."] 動態字符串。
動態Lu數據 lu lu[2,3.5,"..."] lu{2,lu[3.5,"..."]} new[lu,5:2,3.5,"..."] 動態Lu表。可以存放任意類型的數據。
外部擴展動態類型 私有動態數據類型 由擴展用戶提供       用戶自定義的私有動態數據類型。
公有動態數據類型 由擴展用戶提供       用戶自定義的公有動態數據類型。

    核心庫對Lu數據進行的運算符支持參考運算符及優先級

    在Lu表達式中各種數值數據進行混合運算時,其隱式轉換規則如下表:

表5-2 混合運算隱式轉換規則

數據類型

整數 實數 復數 三維向量 長實數
整數 整數 實數 復數   長實數
實數 實數 實數 復數   長實數
復數 復數 復數 復數    
三維向量       三維向量  
長實數 長實數 長實數     長實數

6 常量和變量  [返回頁首] [返回目錄]

    常量是指那些不需要程序計算的固定值。有數值常量和符號常量兩種。如100就是一個數值常量。在Lu中,也可以用標識符表示一個符號常量 。用函數const可以定義一個永久性符號常量或暫時性符號常量,但只有const函數被執行后,定義的常量才會起作用。如下例:

      const("aa",111);      //定義永久性常量,不可刪除
      const("bb",222,true); 
//定義暫時性常量,可以刪除

    編譯運行以上表達式,然后輸入下例代碼并執行:

      aa;                   //aa=111
      bb;                  
//bb=222
      const("aa",0,false);  
//試圖刪除永久性常量,但失敗了
      const("bb",0,false);  //刪除暫時性常量

    有些軟件會利用Lu的輸出函數定義一些符號常量,但在這里無法給出具體的例子。

    變量是指在程序運行時,其值可以改變的量。Lu有五種變量,即:自變量、動態變量、靜態變量、模塊變量和全局變量。自變量、動態變量和靜態變量只能被定義該變量的表達式所訪問;模塊變量可被同一模塊的所有表達式所訪問,其他模塊的表達式無法訪問;全局變量可被所有的表達式所訪問。自變量用于向表達式傳遞參數,因此自變量也稱為形式參數。動態變量只在表達式執行時起作用,一旦表達式執行完畢,動態變量也隨之失效。靜態變量存在于表達式的整個生命周期,每次表達式執行完畢,靜態變量的值被保留。Lu在編譯表達式時, 自變量不進行初始化;動態變量初始化為nil;靜態變量初始化為0;模塊變量和全局變量在第一次生成時初始化為nil,以后使用不再進行初始化。

    如果表達式中的變量名與一個常量名相同,則常量名被忽略。

    在Lu表達式中,通常情況下,變量要先定義后使用,變量在表達式的開頭進行定義,格式如下:

      F(a,b:x,y,static,u:s,t,common,v)=
      {
        x=1,y=2,
        a+b+x+y+static+u+s+t+common+v
      }

    F是表達式的名字,a和b是自變量,x和y是動態變量,static和u是靜態變量,s和t是模塊變量,common和v是全局變量。自變量、動態變量和靜態變量以及模塊變量和全局變量之間用冒號分隔,即第一個冒號前為自變量,兩個冒號之間為動態變量和靜態變量,第二個冒號后為模塊變量和全局變量。兩個冒號之間用關鍵字static分隔動態變量和靜態變量,static之前為動態變量,static及以后變量均為靜態變量,關鍵字static只能用在兩個冒號之間。第二個冒號后用關鍵字common分隔模塊變量和全局變量,common之前為模塊變量,common及以后變量均為全局變量,關鍵字common只能用在第二個冒號后。Lu中的所有變量均可缺省。

    以下都是合法的變量定義的例子:

      F()= ... ...          //沒有使用任何變量,稱無參表達式;
      F(::)= ... ...        //沒有使用任何變量,稱無參表達式;
      F(a,b)= ... ...       //定義了兩個自變量a和b;
      F(:x,y)= ... ...      //定義了兩個動態變量x和y;
      F(:static,u)= ... ... //定義了兩個靜態變量static和u;
      F(::s,t)= ... ...     //定義了兩個模塊變量s和t;
      F(::common,v)= ... ...//定義了兩個全局變量common和v;
      F(a,b:x,y)= ... ...   //定義了兩個自變量a和b,兩個動態變量x和y;
      F(a,b::s,t)= ... ...  //定義了兩個自變量a和b,兩個模塊變量s和t;
      F(:x,y:s,t)= ... ...  //定義了兩個動態變量x和y,兩個模塊變量s和t;

    變量的使用見下面的例子:

      F(a,b:x,y,static,u:s,t,common,v)={a=1,b=2,x=3,y=4,static=5,u=6,s=7,t=8,common=9,v=10};//函數定義及變量賦值;
      A(a,b:x,y,static,u:s,t,common,v)=a;//函數定義;
      B(a,b:x,y,static,u:s,t,common,v)=b;//函數定義;
      X(a,b:x,y,static,u:s,t,common,v)=x;//函數定義;
      Y(a,b:x,y,static,u:s,t,common,v)=y;//函數定義;
      _S(a,b:x,y,static,u:s,t,common,v)=static;//函數定義;
      U(a,b:x,y,static,u:s,t,common,v)=u;//函數定義;
      S(a,b:x,y,static,u:s,t,common,v)=s;//函數定義;
      T(a,b:x,y,static,u:s,t,common,v)=t;//函數定義;
      _C(a,b:x,y,static,u:s,t,common,v)=common;//函數定義;
      V(a,b:x,y,static,u:s,t,common,v)=v;//函數定義;
      F(11,22);//函數調用,進行變量賦值 ,返回值=10;
      A(11,22);//函數調用,返回值=11;
      B(11,22);//函數調用,返回值=22;
      X(11,22);//函數調用,返回值=隨機數值;
      Y(11,22);//函數調用,返回值=隨機數值;
      _S(11,22);//函數調用,返回值=0;
      U(11,22);//函數調用,返回值=0;
      S(11,22);//函數調用,返回值=7;
      T(11,22);//函數調用,返回值=8;
      _C(11,22);//函數調用,返回值=9;
      V(11,22);//函數調用,返回值=10;

    可以在Lu表達式使用未定義的模塊變量。在MLu(MLu是一個Lu擴展動態庫,可對源代碼進行模塊化編譯)中,可使用編譯符mvar:通知編譯器使用未定義的模塊變量,使用編譯符unmvar:通知編譯器取消這種設置,格式如下:

      mvar:        //通知編譯器使用未定義的模塊變量
      s,t,u=5,v=6; //如果不存在同名的常量,就解釋為模塊變量
      s=1, 2+s+u;  //可以正確編譯,返回值=8
      unmvar: 
    //通知編譯器取消使用未定義的模塊變量
      2+s+u;       //編譯出錯,變量不可識別

    根據標識符解釋規則,確定變量或常量的順行是:變量、常量、常量命名空間。若要確保編譯器將未定義的標識符解釋為模塊變量,則應在前面編譯的表達式中,將該標識符明確地定義為模塊變量并至少使用一次,如下例:

      !!!const["v",66,true]; //創建一個常量v=66
      v;                     //v為常量,返回值=66
      mvar:                  //通知編譯器使用未定義的模塊變量
      v;  
                  //v仍為常量,返回值=66
      (::s,t,u,v)= s,t,u=5,v=6; //將s,t,u,v定義為模塊變量并至少使用一次
      s=1, 2+s+u;            //s,u將解釋為模塊變量,返回值=8
      v;  
                  //v將解釋為模塊變量,返回值=6
      unmvar:                //通知編譯器取消使用未定義的模塊變量
      v;  
                  //v將解釋為常量,返回值=66
      s;                     //編譯出錯,變量不可識別

7 賦值語句  [返回頁首] [返回目錄]

    賦值語句的作用是將一個值賦給一個變量。在Lu中,可以用等號對變量進行賦值。例如:

      F(x)= x=5;   //函數定義,將數值5賦給變量x;
      F[2];        //函數調用,返回值為5;

    上面進行變量賦值的例子很簡單,但等號后部分可以是任意復雜的表達式。

    對象及對象成員的賦值用“.=”并最終通過函數實現,參考:對象成員運算符(函數參數運算符 )及對象賦值運算符

8 常用算術運算符  [返回頁首] [返回目錄]

    常用的算術運算符有:+(加或正)、-(減或負)、*(乘)、/(除)、%(求模)、^(乘方)、++(自增)、--(自減)等等。
    運算符*、/、%和^是雙目運算符。注意數字與變量相乘時,乘號不可省略;在進行乘方運算時,底數應為非負數 。
    運算符+、-作加、減運算時,是二元運算符,當作正、負運算時,是單目運算符。
    運算符++、--是單目運算符,僅能對變量使用。++使變量的值加1,如果++在變量之前,那么運算符在程序語句訪問該值之前執行加法運算,這時的++運算符稱為“前置自增運算符”;如果把該運算符放在變量之后,那么運算符在程序語句訪問該值之后執行加法運算,這時的++運算符被稱為“后置自增運算符”。--使變量的值減1,如果--在變量之前,那么運算符在程序語句訪問該值之前執行減法運算,這時的--運算符稱為“前置自減運算符”;如果把該運算符放在變量之后,那么運算符在程序語句訪問該值之后執行減法運算,這時的--運算符被稱為“后置自減運算符”。例如:

      (:x)= x=2,++x;    //返回值為3;
      (:x)= x=2,++x,x;  //返回值為3;
      (:x)= x=2,x++;    //返回值為2;
      (:x)= x=2,x++,x;  //返回值為3;
      (:x)= x=2,--x;    //返回值為1;
      (:x)= x=2,--x,x;  //返回值為1;
      (:x)= x=2,x--;    //返回值為2;
      (:x)= x=2,x--,x;  //返回值為1;

    單目運算符的優先級比雙目運算符的優先級高,后置單目運算符的優先級比前置單目運算符的優先級高。對于同一優先級的運算,按從左到右的優先順序進行。

    注意:單目運算符-(負) 與雙目運算符^(乘方)需用括號區分計算的先后順序。例如:

      (-2)^2;       //返回值為4;
      -(2^2);       //返回值為-4;
      -2^2;         //編譯出錯;
      -(2+3)^2;     //編譯出錯;
      -sin(2.+3)^2; //編譯出錯;

    算術運算符的優先級如表8-1所示。在表中,同一行中運算符優先級相同,不同行中運算符的優先級從上往下依次降低。

表8-1:常用算術運算符及優先級

運 算 符

說  明

++、-- 后置單目運算符,自增減運算符
+、-、++、-- 前置單目運算符,“++、--”為自增減運算符
^ 乘方
*、/、% 乘、除、求模
+、- 加、減

9 關系運算符和邏輯運算符  [返回頁首] [返回目錄]

    關系運算是對兩個值的大小進行比較,返回一個邏輯值。邏輯值只有兩個:邏輯真true和邏輯假false。在Lu中用0表示邏輯假,其他任何非0值表示邏輯真。例如:

      3>2;       //返回true;
      2>3;       //返回false;

    關系運算符共有6個:>(大于)、>=(大于等于)、<(小于)、<=(小于等于)、==(等于)、!=(不等于)。

    邏輯值之間的運算稱邏輯運算,邏輯運算的結果仍然是一個邏輯值。有四個邏輯運算符:&(邏輯與)、|(邏輯或)、!(邏輯非)、~(邏輯異或)。通過表9-1給出的真值表可以掌握這 幾種運算。表中用1代表邏輯真,0代表邏輯假。

表9-1 真值表

p q p&q p|q !p p~q
0 0 0 0 1 1
0 1 0 1 1 0
1 1 1 1 0 1
1 0 0 1 0 0

    關系運算符和邏輯運算符的優先級如表9-2所示。在表中,同一行中運算符優先級相同,不同行中運算符的優先級從上往下依次降低。

表9-2 關系運算符和邏輯運算符及優先級

運 算 符

說  明

! 邏輯非
>、>=、<、<=、==、!= 關系運算符
& 邏輯與
~ 邏輯異或
| 邏輯或

10 逗號(冒號、分號)運算符和括號運算符  [返回頁首] [返回目錄]

    表達式中如果有多個語句,可以用逗號、冒號或分號進行分隔,Lu將按從左到右的順序計算各個語句,并返回最后一個語句的值。也可以將多個用逗號 、冒號 或分號分隔的語句放在一個括號內,Lu也將按從左到右的順序計算各個語句,并返回最后一個語句的值。例如:

      (:x)= x=2,x=5,x;                          //返回值為5;
      (:x)={x=2,x=5,x};                        
//返回值為5;
      (:x,y)={x=2,y=5,x=[x=x+1,y=y+1,x+y]:x};  
//返回值為9;
      (:x,y)={x=2,y=5,[x=x+1,y=y+1:x+y]};      
//返回值為9;

    雖然逗號、冒號或分號運算符是通用的,但逗號的用法很常見。冒號和分號運算符僅用在一些特殊場合,例如分隔一些不同類別的變量。

    注意:一般表達式之間用分號進行分隔,故分號運算符必須放到括號內才有效。另外,不是所有的Lu程序都支持分號運算符。由模塊化編譯運行庫MLu支持的程序都支持分號運算符,如下例:

      (:x,y)={x=2,y=5;x=[x=x+1,y=y+1;x+y]:x};   //返回值為9;
      (:x,y)={x=2,y=5,[x=x+1;y=y+1:x+y]};      
//返回值為9;

11 運算符及優先級  [返回頁首] [返回目錄]

    Lu中的運算符及優先級如表11-1所示,在表中,同一類型的運算符優先級相同,不同類型運算符的優先級從上往下依次降低。

表11-1:Lu運算符及優先級

運算符類型

運算符 名稱 是否允許重載 核心庫支持的運算

說  明

雙括號連接運算符 :=     雙括號連接 冒號前和等號后都必須是括號
單括號連接運算符 ( )=     單括號連接 等號后是一個表達式
[ ]=     單括號連接
{ }=     單括號連接
括號運算符 ( ) 小括號   括號運算 返回最后一個語句的值
[ ] 中括號   括號運算
{ } 大括號   括號運算
命名空間成員訪問符 :: 雙冒號   訪問命名空間成員 訪問命名空間成員
對象成員運算符 .   訪問對象成員或傳遞函數參數 也稱為函數參數運算符,或者變量函數調用運算符
后置單目運算符 ++ 后置自增 整數、實數 后置單目運算符(自增、自減、轉置、點轉置)
-- 后置自減 整數、實數
' 轉置 未定義
.' 點轉置 未定義
前置單目運算符 ! 邏輯值、整數、實數 前置單目運算符(非、正、負、自增、自減、按位非)

對整數或實數求非時,返回邏輯值,且規定!0=true,!0.0=true,其他情況均返回false。

+   被忽略
- 整數、實數、復數、三維向量
++ 前置自增 整數、實數
-- 前置自減 整數、實數
!! 按位非 整數
乘方算術運算符 ^ 乘方 整數、實數、復數 算術運算符(乘方、點乘方)
.^ 點乘方 未定義
乘除算術運算符 * 整數、實數、復數、三維向量 算術運算符(乘、左除、右除、求模、點乘、點左除、點右除)
/ 左除 整數、實數、復數
\ 右除 未定義
% 求模 整數
.* 點乘 未定義
./ 點左除 未定義
.\ 點右除 未定義
加減算術運算符 + 整數、實數、復數、三維向量、字符串 算術運算符(加、減)
- 整數、實數、復數、三維向量
移位運算符 << 左移位 整數 左移位、右移位
>> 右移位 整數
關系運算符 > 大于 整數、實數 關系運算符(大于、大于等于、小于、小于等于、等于、不等于)
>= 大于等于 整數、實數
< 小于 整數、實數
<= 小于等于 整數、實數
== 等于 整數、實數、編譯符#生成的整數
!= 不等于 整數、實數、編譯符#生成的整數
按位與 && 按位與 整數 按位與
按位異或 ~~ 按位異或 整數 按位異或
按位或 || 按位或 整數 按位或
邏輯與 & 邏輯與 邏輯值 邏輯與
邏輯異或 ~ 邏輯異或 邏輯值 邏輯異或
邏輯或 | 邏輯或 邏輯值 邏輯或
$ 整數、實數、復數 并。核心庫并運算的結果為復數或三維向量。
賦值運算符 = 賦值   賦值 賦值運算符
對象賦值運算符 .= 對象賦值   對象賦值 變量函數調用運算符與賦值運算符的結合,一般用于對象賦值
語句分隔符 , 逗號   分隔語句 逗號、冒號、分號運算符
: 冒號   分隔語句
; 分號   分隔語句

    括號連接運算符應用舉例:

      (1,2):=[6,5] 等價于 (1,2,6,5) 或者 [1,2,6,5]

      (1,2)=[6,5]  等價于 (1,2,[6,5])

      (1,2)=5      等價于 (1,2,5)

    括號連接運算符常用在函數調用中,例如有對數組操作的函數A,可接受變參數,使用格式如下:

      A[me,i]    //獲得數組me的第i個單元的值
      A[me,i:t]  //設置數組me的第i個單元的值為t

    將數組me的第i個單元的值加2,用函數可表示為:

      A[me,i:A(me,i)+2]

    用括號連接運算符可表示為:

      A[me,i]:=[A(me,i)+2] 或者 A[me,i]=A(me,i)+2

    再如有對對象操作的函數B,使用格式如下:

      B[me,t0,t1,...,tn]    //將對象me的值設置為t0,t1,...,tn

    用括號連接運算符可表示為:

      B[me]:=[t0,t1,...,tn]

    注意在Lu中,通常運算符不能連用,例如 !-2 是錯誤的,應當添加括號分隔開各個運算符,即應當寫成 !(-2) 形式。

    還有一個Lu運算符在表11-1中沒有列出:&(取變量的地址)。取地址運算符&只能用于(被逗號或冒號隔開的)單獨的變量,與其他運算符不發生任何關系,所以這個運算符在表11-1中沒有列出,其用法我們將在下面詳細介紹。

    對象成員運算符(函數參數運算符)“.”、對象賦值運算符“.=”及命名空間成員訪問符“::”也將在下面詳細介紹。

12 函數概述  [返回頁首] [返回目錄]

    函數是Lu最為重要的特性。一個函數通過傳遞給它的參數完成一個特定的功能。函數都有一個名字,可通過調用函數名并傳遞參數來使用函數。所有的函數都將返回一個函數值。典型的函數調用方法如下:

      函數名(參數1,參數2,... ...)

    函數可以有零個或多個參數,但即便有零個參數,函數名后的括號也不能缺省。函數調用時,參數一定要匹配。

    在Lu中可以使用的函數有三種:一級函數、二級函數和自定義函數(表達式)。其中一級函數為系統內置的函數,運算速度快;二級函數部分為系統內置的函數,部分為外部擴充的函數,功能非常豐富;自定義函數實際上就是一個有名表達式,由用戶定義。

    實際上,Lu還有一類為數不多的函數,稱為流程控制函數,不過對于它們,函數的意義并不明顯,更多的是流程控制的意義,因此我們不把它們包括在上面的分類中。

    Lu中的一級函數見表12-1。

表12-1:一級函數

函 數 參數類型 是否允許重載

說 明

global(p) 局部動態對象
/true/false
將局部動態對象p轉換為全局動態對象。另外,在global(true),... ...,global(false)之間生成的動態對象都為全局動態對象。參考動態對象與指針
local(p) 全局動態對象 將全局動態對象p轉換為局部動態對象。參考動態對象與指針
sin(x) 實數、復數 正弦函數
cos(x) 實數、復數 余弦函數
tan(x) 實數、復數 正切函數
asin(x) 實數、復數 反正弦函數
acos(x) 實數、復數 反余弦函數
atan(x) 實數、復數 反正切函數
sqrt(x) 實數、復數 平方根函數
exp(x) 實數、復數 指數函數
ln(x) 實數、復數 自然對數函數
lg(x) 實數、復數 常用對數函數
sinh(x) 實數、復數 雙曲正弦函數,[exp(x)-exp(-x)]/2
cosh(x) 實數、復數 雙曲余弦函數,[exp(x)+exp(-x)]/2
tanh(x) 實數、復數 雙曲正切函數,[exp(x)-exp(-x)]/[exp(x)+exp(-x)]
abs(x) 實數、復數 絕對值函數
floor(x) 實數 返回不大于x的最大整數
ceil(x) 實數 返回不小于x的最小整數
itor(x) 整數 將整數轉換成實數,大數轉換時有精度損失
rtoi(x) 實數 將實數轉換成整數,大數轉換時有誤差
con(x) 復數 求復數的共軛復數
atan2(x,y) 實數 反正切函數,求x/y的反正切值,所在象限由x和y的符號確定
fmod(x,y) 實數 求x/y的余數

13 函數的傳值調用和傳址調用  [返回頁首] [返回目錄]

    傳值調用是把變量值拷貝到被調函數的形式參數中,函數在執行時,參數的改變不會影響到調用函數時所用的變量。
    傳址調用(也叫引用調用)是把變量的地址拷貝到被調函數的形式參數中,函數在執行時,參數的改變會影響到調用函數時所用的變量。
    通常,Lu是按傳值調用的,除非你用取地址運算符&(也叫引用運算符)顯示地通知Lu編譯器,要按傳址方式使用參數。在使用運算符&時,&只能放到(用逗號 、冒號或分號隔開的)單獨存在的變量的前面。如下列:

      a(x,y)= x=2,y=3;
      (:x,y)= x=5,y=6,a(x,y),x; 
//傳值調用,x=5;
      (:x,y)= x=5,y=6,a(x,y),y; 
//傳值調用,y=6;
      (:x,y)= x=5,y=6,a(&x,y),x;
//傳址調用,x=2;
     
(:x,y)= x=5,y=6,a(x,&y),y; //傳址調用,y=3;

14 用表達式作為函數的參數  [返回頁首] [返回目錄]

    在函數調用時,有時候需要用表達式(即自定義函數)作為函數的參數。

    14.1 用編譯符@獲得表達式句柄(也稱為表達式指針、函數指針 、函數句柄)傳給函數

    例子:

      aa(x)=x+8;        //定義一元函數aa
      bb(f,x)= f(x)+2;  //定義函數bb,接受一個一元函數句柄f
      bb[@aa,2];        //調用函數bb

    14.2 用二級函數HFor("ForName")獲得表達式句柄(也稱為表達式指針、函數指針 、函數句柄)傳給函數

    例子:

      aa(x)=x+8;        //定義一元函數aa
      bb(f,x)= f(x)+2;  //定義函數bb,接受一個一元函數句柄f
      bb[HFor("aa"),2]; //調用函數bb

    14.3 用表達式的名稱作為字符串(兩個雙引號"..."之間的內容為一個字符串)傳給函數

    例子:核心庫中沒有例子。

    一般,在函數的說明中會指定需傳遞的表達式參數的類型,或者傳遞表達式的句柄,或者傳遞字符串形式的表達式名稱。

15 表達式的完整定義及表達式句柄  [返回頁首] [返回目錄]

    在這里,我們總結性地給出Lu表達式的完整定義,以幫助用戶更好地理解Lu,為了定義的完整性,部分地重復了前面所敘述過的內容。Lu表達式的完整定義如下:

      F(a,b:x,y,static,u,free,v:s,t,common,w)=
      {
        x=1,y=2,[x+y,x*y],(x-y):
        a+b+x+y+static+u+s+t+common+v
      }

    F是表達式的名字,必須位于表達式的開頭,該名字必須是一個標識符。給表達式起一個名字,主要是以后可以通過該名字來調用該表達式。表達式也可以沒有名字,但這樣,在其它的表達式中將無法調用它。在Lu中,一般表達式的名字是唯一的,不能給兩個表達式起同一個名字(模塊內表達式的名字除外,模塊的定義將在后面介紹)。

    如果定義了表達式名字,表達式名字后必須跟一對小括號(只能用小括號),用來定義變量。變量個數可以為零,也可以有任意多個。有多個變量時,變量間以逗號或冒號分隔。用冒號隔開的變量,從前往后依次為自變量、動態變量、靜態變量、模塊變量和全局變量,即第一個冒號前為自變量,兩個冒號之間為動態變量和靜態變量,第二個冒號后為模塊變量和全局變量。兩個冒號之間用關鍵字static分隔動態變量和靜態變量,static之前為動態變量,static及以后變量均為靜態變量,關鍵字static只能用在兩個冒號之間。第二個冒號后用關鍵字common分隔模塊變量和全局變量,common之前為模塊變量,common及以后變量均為全局變量,關鍵字common只能用在第二個冒號后。 所有變量以及冒號均可缺省。

    在這個例子中,a和b是自變量,x和y是動態變量,static、u、free和v是靜態變量,s和t是模塊變量,common和w是全局變量。

    自變量、動態變量和靜態變量只能被定義該變量的表達式所訪問;模塊變量可被同一模塊的所有表達式所訪問,其他模塊的表達式無法訪問;全局變量可被所有的表達式所訪問。自變量用于向表達式傳遞參數 ,因此自變量也稱為形式參數。動態變量只在表達式執行時起作用,一旦表達式執行完畢,動態變量也隨之失效。靜態變量存在于表達式的整個生命周期,每次表達式執行完畢,靜態變量的值被保留。

    Lu在編譯表達式時, 自變量不進行初始化;動態變量初始化為nil;靜態變量初始化為0;模塊變量和全局變量在第一次生成時初始化為nil,以后使用不再進行初始化。

    如果定義了靜態變量free,Lu在銷毀表達式前將自動設置free為1,然后自動執行表達式。

    即便表達式沒有名字,也可以用一對小括號(只能用小括號)來定義變量,這時,小括號必須位于表達式的開頭。

    如果用小括號定義了變量,小括號后必須跟一個等號,該等號標志表達式可執行部分的開始,等號及等號后的可執行部分均不可缺省。

    如果表達式中的變量名與一個常量名相同,則常量名被忽略。

    表達式中可以不定義變量,這時,表達式中只有可執行部分,稱常量表達式,或者稱無參表達式。

    表達式的可執行部分任何情況下都不可缺省。

    表達式的可執行部分由多個語句組成,多個語句之間用逗號、冒號或分號分隔,多個語句可以放在一對括號內。可執行部分中可以使用三對括號( )、[ ]和{ },括號必須成對使用。在Lu中,括號是一種運算符,具有一個值,即該括號內最后一個語句的值。表達式總是返回(最外層括號中)最后一個語句的值。另外,最外層的括號不是必須的,但表達式若有逗號 、冒號或分號隔開的多個語句,有了最外層的括號后,表達式更易讀。

    用這種方式定義表達式時,實際上就是自定義了一個函數,可以在任意的表達式中使用該函數。

    以下都是合法的表達式定義的例子:

      2;                     //常量(無參)表達式;
      ()=2;                 
//常量(無參)表達式;
      (::)=2;                //常量(無參)表達式;
      A()=2;                
//常量(無參)表達式(函數),在其它表達式中可以調用該函數;
      A(::)=2;               //常量(無參)表達式(函數),在其它表達式中可以調用該函數;
      B(x)=2;               
//有一個自變量的表達式(函數);
      (x)=23;               
//有一個自變量的表達式,但沒有函數名,不能在其他表達式中調用它;
      F(a,b)= ... ...       
//定義了兩個自變量a和b;
      F(:x,y)= ... ...      
//定義了兩個動態變量x和y;
      F(:static,u)= ... ...  //定義了兩個靜態變量static和u;
      F(::s,t)= ... ...     
//定義了兩個模塊變量s和t;
      F(::common,v)= ... ... //定義了兩個全局變量common和v;
      F(a,b:x,y)= ... ...   
//定義了兩個自變量a和b,兩個動態變量x和y;
      F(a,b::s,t)= ... ...  
//定義了兩個自變量a和b,兩個模塊變量s和t;
      F(:x,y:s,t)= ... ...  
//定義了兩個動態變量x和y,兩個模塊變量s和t;

    Lu表達式可用句柄(也稱為表達式指針、函數指針、函數句柄)進行標識,可以使用 編譯符@或二級函數HFor("ForName")獲得表達式的句柄。用編譯符@獲得函數句柄是在編譯期進行的,而用二級函數HFor("ForName")獲得表函數句柄是在運行期進行。

16 流程控制  [返回頁首] [返回目錄]

    在Lu中,表達式的各個語句一般是順序執行的。但是某些函數可以改變語句執行的順序,稱為流程控制函數。

    16.1 立即返回函數 return(x)

    結束計算并立即返回表達式的值為x。

    16.2 掛起協程并返回函數 yield(x)

    掛起協程并立即返回表達式的值為x。下次執行表達式時將從yield(x)的下一條語句開始。

    16.3 判斷函數 if(x,y1,y2,... ...,yn)

    當邏輯語句x的值為真(或者對象x值非0)時,依次執行語句y1,y2,... ...,yn,否則,不執行語句y1,y2,... ...,yn。
    該函數至少要有2個自變量參數,其中第一個參數為邏輯語句。
    該函數的返回值無意義。

    16.4 自定義分段函數

      which{
        邏輯語句1 : 語句1,
        邏輯語句2 : 語句2,
        ... ...,
        邏輯語句n : 語句n,
        缺省語句
      };

    Lu從前往后計算并檢測邏輯語句的值,當檢測到邏輯真(或者對象值非0)時,計算與此邏輯真對應的語句并返回該語句的值,如果沒有檢測到邏輯真,則計算缺省語句的值作為返回值,若此時沒有缺省語句,則產生一個運行錯誤(錯誤代碼為0)。
    該函數至少要有2個自變量參數。
    例如下式定義了一個分段函數:

      (x)=which{x>0, 2*x-1,
            x*x-1
          };

    如果舍棄該函數的返回值,則該函數可以作為一個選擇計算函數使用。

    16.5 while循環函數

    while循環是“當型”循環,其特點是:先判斷條件是否成立,當條件成立時,則執行循環體,否則退出循環體,即“當條件成立時執行循環”。“當型”循環的循環體有可能一次也不執行。
    while循環函數的格式如下:

      while{x,
        y1,y2,
        ...,
        break(),
        ...,
        continue(),
        ...,
        yn
      };

    其中x為邏輯語句;y1,y2,...,break(),...,continue(), ...yn為循環體語句。當x的值為真(或者對象x值非0)時,依次執行循環體語句,直到x的值為假時退出循環。當執行到break()函數時,跳出while循環,執行while循環后面的語句部分;當執行到continue()函數時,返回到while循環的開始語句x處繼續執行。
    在循環體語句中,必須有能修改邏輯語句x的值的語句,使x的值為假,退出循環體,否則將產生無限循環。
    該函數至少要有2個自變量參數,其中第一個參數為邏輯語句。
    該函數的返回值無意義。
    以下是一個while循環的例子:

      (:i,k)=
      {
        i=0, k=0,
        while{i<=1000000, k=k+i, i++},
//當i<=1000000時,計算k=k+i,然后i++;
        k
      };

    也可以將循環增量寫在前面(使用括號運算符):

      (:i,k)=
      {
        i=-1, k=0,
        while{(i++, i<=1000000), k=k+i},
//當i<=1000000時,計算k=k+i,然后i++;
        k
      };

    16.6 until循環函數

    until循環是“直到型”循環,其特點是:先執行循環體,再判斷條件是否成立,當條件成立時,退出循環體,否則繼續執行循環體,即“執行循環直到條件成立”。“直到型”循環的循環體至少執行一次。
    until循環函數的格式如下:

      until{x1,x2,
        ...,
        break(),
        ...,
        continue(),
        ...,
        y
     
};

    until為先執行后判斷的循環函數。即先執行循環體語句x1,x2,...,break(),...,continue(),...,然后計算邏輯語句y的值,直到y的值為真(或者對象y值非0)時退出循環。當執行到break()函數時,跳出until循環,執行until循環后面的語句部分;當執行到continue()函數時,返回到until循環的開始語句x1處繼續執行。
    在循環體語句中,必須有能修改邏輯語句y的值的語句,使y的值為真,退出循環體,否則將產生無限循環。
    該函數至少要有2個自變量參數,其中最后一個參數為邏輯語句。
    該函數的返回值無意義。
    以下是一個until循環的例子:

      (:i,k)=
      {
        i=0,k=0,
       
until{k=k+i,i++,i>1000000}, //計算k=k+i,i++,當i>1000000時退出;
        k
      };

    注意:
    (1)break()和continue()是兩個無參函數,只能用在while和until兩個循環函數中。
    (2)Lu支持多線程,在多線程的程序中,如果不慎進入了無限循環,可以通過另一個線程退出。

17 字符串  [返回頁首] [返回目錄]

    在Lu中使用Unicode寬字符串,一個寬字符有2個字節,用兩個雙引號定義一個字符串,即"..."表示一個字符串。在Lu字符串中,還可以用反斜杠轉義字符輸入一些特殊字符,見表17-1。

表17-1 反斜杠轉義字符

\\ 反斜杠“\”
\" 雙引號“"”
\% 0
\a 警告
\b 退格
\f 換頁
\n 換行
\r 回車
\t 水平制表符
\v 垂直制表符
\& 沒有對應的字符。當該轉義字符位于字符串最前面時(例如:"\&... ..."),指示編譯器在數的邊界處存放該字符串,并影響轉義字符\[n];該轉義字符位于字符串的其他位置時不產生任何作用
\NNNNN 任意字符,NNNNN是該字符Unicode碼的10進制值,NNNNN必須是5個數字,例如NNNNN碼為9的字符,應寫成\00009
\xNNNN 任意字符,NNNN是該字符Unicode碼的16進制值,NNNN必須是兩個16進制數字(字母A到F以單個數字的形式表示10進制數的10到15),例如Unicode碼為11的字符,應寫成“\x000B”
\[n] n是一個10進制整數,轉換成n×k個空格符。若字符串最前面沒有轉義字符\&,k=1;若字符串最前面有轉義字符\&, 則每個單元取系統字長(64位系統為8個字節),此時k=4,這種字符串格式可轉換為整數、實數或復數數組。例如: 任意表達式中,"\[256]" 是一個256個字符長的空格符字符串。"\&\[256]" 是一個256×4個字符長 (占用256×8個字節)的空格符字符串。

    除了表中定義的之外,Lu沒有定義其它的轉義字符(“\”和其他字符的組合都是非法的)。 可以看出,反斜杠“\”和雙引號“"”只能通過轉義字符輸入。

    另外,若字符串前有符號@(例如:@"...\r\n..."),將忽略轉義字符。

    例如:

      o("hello!\r\n字符串!!!");       //用o函數輸出字符串;
      o(@"hello!\r\n字符串!!!");      //用o函數輸出字符串;

    可以用字符串標識任意類型的數據值,但核心庫中沒有這方面的例子。

    可以用字符串存儲任意類型的數據值,如下面將要講到的靜態數組。

    Lu字符串有二種類型:Lu靜態字符串和Lu動態字符串。Lu靜態字符串即在Lu表達式中定義的字符串,長度是固定的,不能隨意進行銷毀(但Lu靜態字符串也并非一成不變);Lu動態字符串由函數new生成。

18 數組  [返回頁首] [返回目錄]

    Lu數組有靜態和動態兩種。

    Lu動態數組是多維的,參考函數new的說明。

    Lu靜態數組是一維的,通過一組函數操作靜態字符串來實現。使用數組之前,須先用轉義字符"\[n]"定義足夠的靜態空間。

    Lu內置了Unicode字符數組、整數數組、實數數組、復數數組和三維向量數組的操作函數,參考函數AA2A3的說明。也可以通過數組句柄直接訪問數組元素,如下例:

    例子1: 靜態Unicode字符數組

(:i,k,str)=
{
    str="
**\[26]**",      //定義字符串str,共30個字符,這就是一個Unicode字符數組
    k=["a"], k=k[0],      
//用獲得字符"a"的Unicode值并賦值給k
    i=0, while{i<26, str[2+i]=k++, i++}, 
//將星號之間是字符空間設置為26個英文字母
    o
(str)                //輸出字符數組str
};

    例子2:靜態實數數組,字符串必須定義成"\&... ..."格式。

(:i,k,a,s)=
{
    a=realss("
\&\[10]"),             //定義4×10個字符長的字符串,并轉換為實數數組
    k=len(a),                       
//獲得數組長度
    i=0, while{i<k, a(i)=1.0, i++},  
//將數組的每一個元素都設為1.0
    i=0, s=0.0, while{i<k, s=s+a(i++)}, 
//將數組的所有元素都加起來
    s
                               //輸出相加后的值
};

    例子3:動態實數數組

(:i,j,m,n,a,s)=
{
    a=new(reals,3,10),   
//定義3×10實數數組(矩陣)
    len(a,0,&m,&n),      
//獲得數組維數大小
    s=0.0, i=0, while{i<m,

      j=0, while{j<n, a(i,j)=1.0, s=s+a(i,j), j++}, 
//將數組的每一個元素都設為1.0,并將數組的所有元素都加起來
      i++
    },
    s
                    //輸出相加后的值
};

19 Lu表  [返回頁首] [返回目錄]

    Lu動態數據存放在一個線性表中,簡稱lu表,lu表中可以存放任意類型的數據,包括nil和lu表。

    有兩種方法可以生成lu表。一種是使用lu函數,例如:lu[2,3.3,"me"];或者使用函數new[lu,5:a,b,c,...]創建lu表,此時可指定lu表的長度并賦初值。

    lu表是Lu的基本動態數據類型。函數以lu表為參數時,可實現變參數傳遞。在垃圾收集時,若一個lu表是有效的,則該lu表中的所有對象都是有效的,不會被垃圾收集器所收集。

    例子1:

(:a,b)= a=lu[2,3.5,"me",lu[5.5,true]], b=a[3], o[a(0),"  ",a(1),"  ",a(2),"  ",b(0),"  ",b(1),"  "];

    例子2:

(:a,b)= a=new[lu,4: 2,3.5,"me",lu[5.5,true]], b=a[3], o[a(0),"  ",a(1),"  ",a(2),"  ",b(0),"  ",b(1),"  "];

    例子3:

f(x:i,k,s)= k=len(x), s=x[0], i=1, while{i<k, s=s+x[i++]}, s;    //函數f接受一個一維對象,并對對象的所有元素相加
f[lu(1,2,3,4,5)];        //5個整數相加
f[lu(1.0,2.0,3.0)];      //3個實數相加
f[lu("abc","123","xyz")];//3個字符串相加

20 動態對象與指針 [返回頁首] [返回目錄]

    指針是動態數據對象的內存地址,對象的成員函數通過指針可以操作對象。Lu核心庫中的動態對象有Lu動態數據、動態字符串string、動態整數數組ints及動態實數數組reals等。擴展庫中將存在更多的動態對象,請參考各擴展庫的說明。

    通常,動態對象是用函數new申請的,但也不盡然,例如函數lu[...]就返回一個動態對象。

    所有新生成的動態對象都是局部對象,其生命期最長為所在表達式運行時的生命期,即當一個表達式運行結束時,所有在該表達式中的局部動態對象都將被銷毀。但函數global(p)可以將局部動態對象轉換為全局動態對象,則表達式運行生命期結束時該對象不會被銷毀。也可以使用函數local(p)將一個全局動態對象轉換為局部動態對象,則表達式運行生命期結束時該對象會被銷毀。

    Lu表達式使用棧管理局部動態對象,在棧回退時局部動態對象將被銷毀,這使得某些局部動態對象的生命期小于所在表達式運行時的生命期。參考以下例子理解棧的變化及對動態對象生命期的影響:

main(:a,b,c,d,e,f,g)=
{
    gc[],
               //垃圾收集。棧增長1。
    a=
new[string,"aa"],
//生成動態字符串a。棧增長1,a生命期開始。
    b=
new[string,"bb"],
//生成動態字符串b。棧增長1,b生命期開始。
    c=
new[string,"cc"],
//生成動態字符串c。棧增長1,c生命期開始。
    d=a+b+c;
            //a+b返回動態字符串ab,棧增長1,ab生命期開始;然后ab+c生成動態字符串d,棧又增長1;賦值時棧回退1,ab被銷毀。故該語句總體上使棧增長1,d生命期開始。
    if{ true,
           //棧增長1。
        e=a+b+c+d,
      //a+b+c+d返回動態字符串e。該語句執行時共生成3個動態對象,執行結束時僅保留最后一個。總體上使棧增長1,e生命期開始。
        f=a+b+c+d+e,    //a+b+c+d+e返回動態字符串f。該語句總體上使棧增長1,f生命期開始。
        global(e),
     //將e轉換為全局對象。棧增長1,沒有新的動態對象生成。
        global(f) 
     //將f轉換為全局對象。棧增長1,沒有新的動態對象生成。
    },
                  //棧回退若干,但沒有銷毀e、f。若不對e、f使用global函數,則e、f將被銷毀。
    //local(e),local(f),//本例中沒有使用函數local,若使用該函數,則將全局動態對象e和f再轉換為局部動態對象,表達式運行結束時會自動銷毀。實用中建議這樣做。
    g=a+b+c+d+e+f,      //a+b+c+d+e+f返回動態字符串g。該語句總體上使棧增長1,g生命期開始。
    o[a,"\r\n",b,"\r\n",c,"\r\n",d,"\r\n",e,"\r\n",f,"\r\n",g,"\r\n"]
  //輸出a、b、c、d、e、f、g
};
                      //棧回退若干,銷毀a、b、c、d、g,但e、f被保留

    函數if、which、while、until及括號運算符( )、[ ]、{ }在執行結束時將銷毀里面生成的動態對象。例如,如果在循環中不斷生成新的動態對象,但不對新動態對象使用global函數,則可能只有最后一次循環中生成的新動態對象被保留,其余的都將被銷毀。

    在不追求效率的程序中,可以在表達式(或函數)的開始使用函數global(true),則所有的動態對象都將成為全局的, 如下列:

main(:a,b,c,d,e,f,g)=
{
    global[true],
       //在程序開始使用函數global。
    a=
new[string,"aa"],
    b=
new[string,"bb"],

    c=
new[string,"cc"],

    d=a+b+c;

    if{ true,
        e=a+b+c+d,
        f=a+b+c+d+e
    },

    g=a+b+c+d+e+f,
    o[a,"\r\n",b,"\r\n",c,"\r\n",d,"\r\n",e,"\r\n",f,"\r\n",g,"\r\n"]

};

    全局動態對象可通過函數gc銷毀。以下例子演示了gc、global、ObjNum、delete等函數的使用:

gc[];                            //垃圾收集,必要時重新初始化程序,使當前動態對象數為0
lu[1], lu[2], global[lu(3)], global[lu(4)], lu[5], lu[6], ObjNum[0];
//生成6個對象,返回對象總數:6
ObjNum[0];
                      //返回當前對象總數:6
ClearBuf[], ObjNum[0];          
//清空緩沖區,返回當前對象總數:2
gc[], ObjNum[0];                
//垃圾收集,返回當前對象總數:0

lu[1], lu[2], global[true], lu(3), lu(4), global[false], lu[5], lu[6], ObjNum[0];
//生成6個對象,返回對象總數:6
ObjNum[0];                      
//返回當前對象總數:6
ClearBuf[], ObjNum[0];          
//清空緩沖區,返回當前對象總數:2
gc[], ObjNum[0];
                //垃圾收集,返回當前對象總數:0

(:a,b,c,d:e,f)= a=lu[1], b=lu[2], global[true], e=lu(3), f=lu(4), global[false], c=lu[5], d=lu[6], ObjNum[0];
//生成6個對象,返回對象總數:6
ObjNum[0];                      
//返回當前對象總數:6
ClearBuf[], ObjNum[0];
           //清空緩沖區,返回當前對象總數:2
gc[], ObjNum[0];                
//垃圾收集,返回當前對象總數:2
(::e,f)= delete[e,f], ObjNum[0];
//徹底銷毀對象,返回當前對象總數:0

    使用協程將有助于動態對象的管理,參考:通過協程初始化和銷毀數據

21 對象成員運算符(函數參數運算符 )及對象賦值運算符  [返回頁首] [返回目錄]

    若標識符后面有句點“.”,表示將該標識符聯系到一個函數,例如:a.sin();或者產生一個函數調用,例如:a.b 相當于 a(b)。若變量是顯示說明的,則通過句點將產生隱含的oset函數或oget函數調用,稱變量函數調用,例如:a.b 相當于 oget[a,b];a.b=c 相當于 oset[a,b:c]。

    在Lu中,存在很多動態對象,動態對象用指針標識,所以Lu編譯器把任何一個函數或表達式都看作動態對象或指針的成員函數。為了存取動態對象時,在形式上看起來更美觀一些,Lu32.dll中提供了 對象成員運算符“.”操作對象的成員函數或指針數據。如下例:

      (:x)= x=2.3, x.sin();                //計算sin(x)。
      (:x)= x=2.3, sin.=x;                 //計算sin(x)。
      (:x)= x=2.3, sin.x;                  //計算sin(x)。

      f(x,y)= x.y.atan2().sin();          //定義函數f,計算sin(atan2(x,y))。
      (:x,y)= x=2.3,y=3.3, x.f(y);         //計算f(x,y)。
      (:x,y)= x=2.3,y=3.3, f.x.y;          //計算f(x,y)。
      (:x,y)= x=2.3,y=3.3, f[x]=y;         //計算f(x,y)。
      (:x,y)= x=2.3,y=3.3, f.x.=y;         //計算f(x,y)。
      (:x,y)= x=2.3,y=3.3, f.x=y;          //計算f(x,y)。

      "字符串也可以的".o[];                 //執行o["字符串也可以的"];
      o."字符串也可以的";
                   //執行o["字符串也可以的"];

      (:f,x,y)= x=2,y=3.3, f.x=y;          //因f是一個顯示說明的變量,計算oset(f,x:y)。
      (:f,x,y)= x=2,y=3, f.x.y;            //因f是一個顯示說明的變量,計算oget(f,x,y)。
      (:f,x,y)= x=2,y=3, f.x.y=f.x.y+1;    //因f是一個顯示說明的變量,計算oset[f,x,y : oget(f,x,y)+1]。

    若句點“.”沒有將標識符聯系到一個函數,則第一個句點前只能是一個類似變量的標識符,其他句點“.”前允許是常量名、變量名、字符串、括號運算符或函數。

    若句點“.”將標識符聯系到一個函數,則任一句點“.”前允許是常量名、變量名、字符串、括號運算符或函數。運算符“.”表示它前面的參數是它后面最近的函數的一個參數,所以稱為函數參數運算符,該運算符之所以也稱為 對象成員運算符,是因為“.”就是為了表示對象的成員關系而設置的。成員函數在調用時,先把對象成員運算符“.”前的數據作為參數,并與函數括號內的參數一起合并,然后執行計算。即:函數的參數個數為 對象成員運算符“.”前的變量個數與函數括號內的參數個數之和。注意:只有成員函數前面的數據才是該函數的參數。例如:

      (2).(3).max[].(5).min[]

上式中,2和3max[]的參數,而max[2,3]和5min[]的參數,即:min{max[(2),(3)],(5)}

    使用對象成員運算符,函數if、while、until等函數有以下用法:

      (x).if                //x為真時執行花括號內的內容。
      {
         ... ...
      };

      (x).while             //x為真時循環執行花括號內的內容。
      {
         ... ...
      };

      //循環執行花括號內的內容直到x為真。
      {
         ... ...
      }.until(x);

    對象成員運算符不但可以表示對象的成員關系,合理地使用該運算符也可使程序更易讀。例如:對象ObjA有一個成員函數ObjA_Add(a,b),執行對象a和b相加, 則對象a、b、c、d連續相加的表達式為:

ObjA_Add{ObjA_Add[ObjA_Add(a,b),c],d}

    用運算符“.”可表示為:

a.ObjA_Add(b).ObjA_Add(c).ObjA_Add(d)

    盡管運算符“.”對程序的運行速度沒有影響,但會影響編譯速度,因而建議僅在需要的時候使用它。

22 遞歸調用  [返回頁首] [返回目錄]

    如果一個函數直接或者間接地調用了自己,稱作函數的遞歸調用。Lu支持函數的遞歸調用。

    為了在Lu中使用遞歸,需要在表達式中用SetStackMax(n)設置好堆棧。注意n的值不能取得太大,當n取得很大時,函數遞歸調用雖不會溢出Lu的堆棧,但會使系統堆棧溢出,這樣會使程序運行中斷,丟失數據。并不需要每次運行程序都進行堆棧的設置,如果堆棧設置的合適,可以只設置一次堆棧。

    下面就是遞歸的最簡單的例子:

      SetStackMax(1000);   //設置堆棧為1000;
      a()=a();              //函數a遞歸地調用自己,屬于無窮遞歸調用。

    直接運行上面的表達式,將會返回一個堆棧溢出的運行錯誤。雖然溢出Lu的堆棧不會中斷程序的運行,但對于上面的程序,無論設置多大的堆棧都會溢出,因為函數a的遞歸定義是錯誤的。遞歸函數應當包含一條控制該函數是繼續調用其本身還是返回的語句。如果沒有這樣的語句,遞歸函數將用完分配給堆棧的所有內存空間,導致堆棧溢出錯誤。

    下面舉一個能正常遞歸調用返回的例子。

      SetStackMax(1000);                   //設置堆棧為1000;
      Fact(n)=which{n<=1,1,n*Fact(n-1)};  
//階乘函數Fact的遞歸實現;
      Fact(3);                             //計算3!;
      Fact(5);                             //計算5!;
      Fact(10);                            //計算10!;
      Fact(100);                           //計算100!;

    以下是一個交叉遞歸的例子。

      SetStackMax(1000); //設置堆棧為1000;
      a(x:k)=o["a..."],if(x<1,return[x]),k=HFor("b"),k(x-1);    //a(...)函數中調用了b(...)函數;
      b(x:k)=o["b..."],if(x<1,return[x]),
k=HFor("a"),k(x-1);    //b(...)函數中調用了a(...)函數;
      a[10];            
//啟動遞歸程序;

23 模塊化編譯  [返回頁首] [返回目錄]

    Lu支持表達式的模塊化編譯。

    在用Lu編譯表達式時,要給該表達式指定模塊號,模塊號用一個整數進行標識。

    在Lu中,一個模塊由一個或多個表達式組成。模塊用一個整數標識,整數可正可負,只要絕對值相等,就屬于同一個模塊。一般用正整數表示該模塊名。模塊共有兩類,即主模塊(0#模塊)和普通模塊(其他標號的模塊)。

    同一模塊中,模塊號為負的表達式稱私有表達式,只能被本模塊的表達式 所訪問(即調用),在其他模塊中是不可見的;模塊號為正的表達式稱公有表達式或全局表達式,能被任何一個表達式所訪問。主模塊(0#模塊)中的表達式都是私有表達式。任何一個表達式,既可以訪問本模塊中的表達式,也可以訪問其他模塊中的全局表達式,如果本模塊中的一個私有表達式與其他模塊的一個全局表達式重名,將優先調用本模塊中的私有表達式。

    由以上規定可以看出,主模塊可以訪問本模塊中的表達式,也可以訪問其他模塊中的全局表達式。因此,主模塊常常用在主程序中。

    通常(核心庫不提供這些支持),可以用編譯符#MODULE##END#定義一個模塊,用編譯符:::輸出模塊中的全局表達式。另外,若表達式名稱前有編譯符!!!,在編譯后將立即執行;若表達式名稱前有編譯符~~~,只編譯,不執行。如下例:

#MODULE#             //定義一個子模塊
!!!a()=o
("字符串!"); //模塊私有表達式,編譯后立即執行
f(x)= x+1;          
//模塊私有表達式
~~~g()= 100;         
//模塊私有表達式,只編譯,不執行
:::h(x)= f(x)+g()+2; 
//全局表達式
#END#                
//子模塊定義結束


f(x)= x+5;
           //主模塊中的私有表達式,可以使用與其他模塊中的表達式相同的名字
f[3];
                //調用主模塊中的函數f
h[3];
                //調用子模塊中的全局表達式

24 模塊命名空間  [返回頁首] [返回目錄]

    使用命名空間可以有效地避免函數重名問題。Lu中可以用函數Module創建模塊命名空間,命名空間創建后,可以用函數OutFun輸出該模塊的表達式,不管是私有表達式還是公有表達式,都可以輸出。

      Module("Name":"Name1","Name2",... ...)    //創建模塊命名空間Name,繼承自"Name1","Name2",... ...

      OutFun("fun1","fun2","fun3",... ...)      //輸出模塊命名空間中的表達式"fun1","fun2","fun3",... ...

    模塊命名空間只能創建一次,可以繼承,甚至可以循環繼承,如果確實有必要。模塊命名空間是一棵樹或者是一個連通圖。

    當模塊中有表達式時,才能創建該模塊的命名空間,當該模塊中的最后一個表達式被銷毀時,將同時銷毀該命名空間。

    當為一個命名空間指定父空間(基空間)時,該父空間是否存在可以是未知的,即父空間并不一定要先于子空間而存在。

    模塊命名空間中輸出的表達式可以用命名空間成員訪問符::調用,如:Name::fun1(...)。 如果該命名空間中沒有輸出指定的表達式,而該空間的父空間中輸出了同名表達式,就會調用父空間中的同名表達式。可以連續使用訪問符::直接調用指定父空間(或該父空間的父空間)中的表達式,如:Name1::Name2::Name3::fun1(...)。 可以看出,模塊命名空間中的表達式調用規則類似于C++中的虛函數調用規則。

    由于Module和OutFun是兩個函數,為了使創建的空間及輸出函數立即可用,應在編譯完Module或OutFun所在的表達式后,立即執行該表達式。

    例子:

#MODULE#                         //定義一個子模塊
  !!!Module("AA");               //創建模塊命名空間AA,該表達式編譯后將立即執行
  Set(x::xx)= xx=x;              //模塊私有表達式
  Get(::xx)= xx;                 //模塊私有表達式
  aa()= 111;                     //模塊私有表達式
  !!!OutFun("Set","Get","aa");   //輸出模塊命名空間中的表達式,該表達式編譯后將立即執行
#END#                            //子模塊定義結束

#MODULE#                         //定義一個子模塊
  !!!Module("BB","AA");          //創建模塊命名空間BB,繼承自"AA",該表達式編譯后將立即執行
  Set(x::xx)= xx=x;              //模塊私有表達式
  Get(::xx)= xx;                 //模塊私有表達式
  !!!OutFun("Set","Get");        //輸出模塊命名空間中的表達式,該表達式編譯后將立即執行
#END#                            //子模塊定義結束

//以下都是主模塊中的表達式

aa()= 999999;
aa();                            //調用主模塊中的表達式aa,結果為:999999

BB::aa();                        //通過模塊空間BB調用空間AA中的表達式aa,結果為:111

BB::Set(33);                     //調用模塊空間BB中的表達式Set,結果為:33
BB::Get();                       //調用模塊空間BB中的表達式Get,結果為:33

BB::AA::Set(55);                 //調用模塊空間AA中的表達式Set,結果為:55
BB::AA::Get();                   //調用模塊空間AA中的表達式Get,結果為:55
BB::Get();                       //調用模塊空間BB中的表達式Get,結果為:33
AA::Get();                       //調用模塊空間AA中的表達式Get,結果為:55

25 二級函數命名空間  [返回頁首] [返回目錄]

    為了避免二級函數重名,二級函數可以采用模塊命名空間中的命名方式,如:Fun2::Set(...),稱二級函數命名空間。是否采用二級函數命名方式,取決于提供二級函數的模塊。

    一般,二級函數名稱中不應有空格,所以可用如下方式判斷一個函數是一個二級函數,或者是一個模塊命名空間中輸出的表達式。

      name ::Set(...);                //注意::前有一空格,若編譯通過,一般是模塊命名空間中的表達式,若編譯未通過,說明是個二級函數

26 常量命名空間  [返回頁首] [返回目錄]

    為了避免常量重名,常量可以采用函數命名空間中的命名方式,如:ConstName::MyConst,稱常量命名空間。常量命名空間可由程序或庫提供,也可用函數const創建。

    一般,常量名稱中不應有空格,否則該常量將無法正常訪問。

    例子:

      !!!const("aa::c1",111);    //在命名空間aa中定義永久性常量
      !!!const("aa::c2",222,true);
//在命名空間aa中定義暫時性常量
      aa::c1;                    //直接訪問命名空間中的常量
      aa::c2;                   
//直接訪問命名空間中的常量
      !!!using("aa");           
//訪問命名空間aa
      c1;                   
    //訪問命名空間中的常量
      c2;                   
    //訪問命名空間中的常量

27 訪問命名空間  [返回頁首] [返回目錄]

    命名空間(包括模塊命名空間和二級函數命名空間)比較長時,輸入比較麻煩,使用using函數可簡化命名空間的訪問。

    執行過函數using("NameSpace1","NameSpace2","NameSpace::name","ConstSpace",... ...)后,如果有一個命名空間中的函數NameSpace2::fun(x,y),不必寫出該命名空間,直接使用函數fun(x,y)就可以了 ;如果有一個常量命名空間中的常量ConstSpace::MyConst, 也不必寫出該命名空間,直接使用常量MyConst就可以了。 函數命名空間若與常量命名空間相同,只寫出一個即可。如下列:

      using("NameSpace1","NameSpace2","NameSpace::name","ConstSpace");
      2+fun(5,6)+MyConst+3;

    注意:如果有一個非命名空間中的函數fun(x,y),將優先調用該函數,遇到這種情況,應使用函數的全名:NameSpace2::fun(x,y)。 如果有一個非命名空間中的常量MyConst,將優先調用該常量,遇到這種情況,應使用常量的全名:ConstSpace::MyConst

    可使用using函數一次指定多個命名空間,當再一次調用using函數時,以前指定的命名空間將被刪除,可訪問的命名空間被重新設定。用using函數指定的命名空間僅在本模塊中起作用,換句話說,可在不同的模塊中使用using函數而互不影響。

    例子:

#MODULE#                         //定義一個子模塊
  !!!Module("AA");               //創建模塊命名空間AA,該表達式編譯后將立即執行
  Set(x::xx)= xx=x;              //模塊私有表達式
  Get(::xx)= xx;                 //模塊私有表達式
  aa()= 111;                     //模塊私有表達式
  !!!OutFun("Set","Get","aa");   //輸出模塊命名空間中的表達式,該表達式編譯后將立即執行
#END#                            //子模塊定義結束

#MODULE#                         //定義一個子模塊
  !!!Module("BB","AA");          //創建模塊命名空間BB,繼承自"AA",該表達式編譯后將立即執行
  Set(x::xx)= xx=x;              //模塊私有表達式
  Get(::xx)= xx;                 //模塊私有表達式
  !!!OutFun("Set","Get");        //輸出模塊命名空間中的表達式,該表達式編譯后將立即執行
#END#                            //子模塊定義結束

//以下都是主模塊中的表達式

!!!using("BB");                  //主模塊可訪問命名空間BB

aa();                            //通過模塊空間BB調用空間AA中的表達式aa,結果為:111

Set(33);                         //調用模塊空間BB中的表達式Set,結果為:33
Get();                           //調用模塊空間BB中的表達式Get,結果為:33

BB::AA::Set(55);                 //調用模塊空間AA中的表達式Set,結果為:55
BB::AA::Get();                   //調用模塊空間AA中的表達式Get,結果為:55
BB::Get();                       //調用模塊空間BB中的表達式Get,結果為:33
AA::Get();                       //調用模塊空間AA中的表達式Get,結果為:55

28 二級函數  [返回頁首] [返回目錄]

表28-1 Lu核心庫二級函數

函數 說明   函數 說明
A 數組元素存取 gc 垃圾收集
A2 復數數組元素存取 ObjNum 獲得或設置動態對象數目
A3 三維向量數組元素存取 ClearBuf 清空垃圾對象緩沖區
clock 獲取流逝過去的時鐘脈沖數 lu 生成Lu動態數據
sum 求和函數 intss 將靜態字符串轉換為靜態一維整數數組
pro 求積函數 realss 將靜態字符串轉換為靜態一維實數數組
SetStackMax 設置堆棧最大數目 cast 強制類型轉換
const 設置常量 del 將動態對象放到垃圾對象緩沖區
Module 創建模塊命名空間 delete 立即銷毀對象
OutFun 輸出模塊命名空間中的表達式 new 生成新的對象
HFor 獲得表達式的句柄 len 返回對象長度
using 訪問命名空間 newtype 創建新的擴展數據類型
free 標記需自動銷毀的對象 status 查詢表達式(包含協程)狀態
o 獲得對象信息 abort 中止掛起的協程
oset 對象賦值 terminate 終止協程
oget 獲得對象的值 resume 重啟一個終止的協程

    在下面的說明中,LuErr表示相應函數的運行錯誤代碼。

    28.1 數組元素存取 A(p,...):

    該函數效率很高,但僅對如下數組元素進行存取(存取時僅對數據的基本類型做檢查,忽略擴展類型):

    (1)靜態字符串(一維靜態字符數組)

    例子:

(:p)= p="   ", A[p,1]=66, A[p,1];

    (2)靜態(一維)整數數組

    例子:

(:p)= p=intss("\&\[5]"), A[p,1]=66, A[p,1];

    (3)靜態(一維)實數數組

    例子:

(:p)= p=realss("\&\[5]"), A[p,1]=66.77, A[p,1];

    (4)靜態(一維)lu表

    例子:

(:p)= p=lu[1,1,1], A[p,1]=66.77, A[p,1];

    (5)動態字符串(一維動態字符數組)

    例子:

(:p)= p=new[string,5], A[p,1]=66, A[p,1];

    (6)動態(多維)整數數組

    例子:

(:p)= p=new[ints,3,5], A[p,1,3]=66, A[p,1,3];

    (7)動態(多維)實數數組

    例子:

(:p)= p=new[reals,3,5], A[p,1,3]=66.77, A[p,1,3];

    若LuErr=1:對象類型不可識別;LuErr=2:參數不匹配;LuErr=3:對象不存在;LuErr=4:元素地址錯誤;LuErr=5:賦值參數不符合要求;LuErr=6:數組維數非法。

    28.2 復數數組元素存取 A2(p,...):

    該函數僅對如下數組元素進行存取(存取時僅對數據的基本類型做檢查,忽略擴展類型):

    (1)靜態字符串(看作一維靜態復數數組,一個數組元素占16個字節,相當于8個Unicode字符長度)

    例子:

(:p)= p="\[32]", A2[p,1]=66+77i, A2[p,1];

    (2)靜態(一維)實數數組(看作一維靜態復數數組,一個數組元素占16個字節,相當于2個實數長度)

    例子:

(:p)= p=realss("\&\[6]"), A2[p,1]=66+77i, A2[p,1];

    (3)動態多維實數數組(最后一維必須為2,看作動態復數數組,即實數數組a[i,j,2]相當于復數數組a[i,j])

    例子:

(:p)= p=new[reals,3,5,2], A2[p,1,3]=66-77i, A2[p,1,3];

    若LuErr=1:對象類型不可識別;LuErr=2:參數不匹配;LuErr=3:對象不存在;LuErr=4:元素地址錯誤;LuErr=5:賦值參數不符合要求;LuErr=6:數組維數非法。

    28.3 三維向量數組元素存取 A3(p,...):

    該函數僅對如下數組元素進行存取(存取時僅對數據的基本類型做檢查,忽略擴展類型):

    (1)靜態字符串(看作一維靜態三維向量數組,一個數組元素占24個字節,相當于12個Unicode字符長度)

    例子:

(:p)= p="\[36]", A3[p,1]=(3$5$7), A3[p,1];

    (2)靜態(一維)實數數組(看作一維靜態三維向量數組,一個數組元素占24個字節,相當于3個實數長度)

    例子:

(:p)= p=realss("\&\[6]"), A3[p,1]=(3$5$7), A3[p,1];

    (3)動態多維實數數組(最后一維必須為3,看作動態三維向量數組,即實數數組a[i,j,3]相當于三維向量數組a[i,j])

    例子:

(:p)= p=new[reals,3,5,3], A3[p,1,3]=(3$5$7), A3[p,1,3];

    若LuErr=1:對象類型不可識別;LuErr=2:參數不匹配;LuErr=3:對象不存在;LuErr=4:元素地址錯誤;LuErr=5:賦值參數不符合要求;LuErr=6:數組維數非法。

    28.4 獲取流逝過去的時鐘脈沖數 clock();

    28.5 求和函數 sum(@F,y1min,y1max,y1dy,y2min,y2max,y2dy,... ...):

    用法1:F為求和函數句柄;y1min,y1max,y1dy為第一個自變量的初值、終值和參數增量[初值<終值,參數增量>0],依次類推。

    用法2:該函數可被重載。

    例子:

      F(x,y)=sin[x]+0.8-y;
      sum(@F,1.,2.,0.01,2.,5.,0.1);

    LuErr=1:參數太少;LuErr=2:非法表達式句柄或者找不到重載函數;LuErr=3:常量表達式無法求和;LuErr=4:參數個數不匹配;LuErr=5:內存分配失敗 ;LuErr=6:自變量參數非法;LuErr=7:求和函數應返回一個實數。

    28.6 求積函數 pro(@F,y1min,y1max,y1dy,y2min,y2max,y2dy... ...):

    用法請參見sum(),用于求積。該函數也可被重載。

    LuErr=1:參數太少;LuErr=2:非法表達式句柄或者找不到重載函數;LuErr=3:常量表達式無法求積;LuErr=4:參數個數不匹配;LuErr=5:內存分配失敗 ;LuErr=6:自變量參數非法;LuErr=7:求積函數應返回一個實數。

    28.7 設置堆棧最大數目 SetStackMax(n):

    在使用遞歸函數之前,需要先設置一定數目的堆棧。通常n的值不能取得太大,當n取得很大時,函數遞歸調用雖不會溢出Lu的堆棧,但會使系統堆棧溢出,這樣會使程序運行中斷。該函數返回實際設置的堆棧數目。

    若LuErr=1:內存分配失敗;LuErr=2:在函數遞歸時使用該函數;LuErr=2:需要整數參數。

    28.8 設置常量 const("ConstStr",ConstValue)或const("ConstStr",ConstValue,bMode):

    ConstStr:常量名,要符合Lu標識符的命名規定,否則編譯器找不到該常量。可使用常量命名空間的方式命名,例如:"ConstName::MyConst"
    ConstValue:常量的值。當bMode為邏輯假時,不使用該值。
    bMode:邏輯值,指出工作方式。若缺省該參數(函數const只有兩個參數),創建一個永久性常量,無法刪除一個永久性常量,除非Lu重新初始化。若bMode為true,創建一個暫時性常量;若bMode為false,刪除一個暫時性常量。暫時性常量保持常量的基本意義,在編譯表達式時不可改變其值,但可被const函數刪除。
    該函數返回值的意義如下:

      0:設置成功。
      1:已存在該常量。
      2:內存分配失敗。
      3:不能用空字符串作為常量名。
      4:參數不符合要求。
      5:需用字符串指出常量名。
      6:不能刪除該常量。

    28.9 創建模塊命名空間 Module("NameSpace":"name1","name2",... ...):

    該函數為調用該函數的模塊創建模塊命名空間。NameSpace為空間名稱,繼承自"name1","name2",... ...。模塊命名空間可以循環繼承。模塊命名空間是一棵樹或者是一個連通圖。

    請參考模塊命名空間了解該函數的使用。

    該函數返回值的意義如下:

      0:創建成功。
      1:至少需要一個參數,不能缺省模塊命名空間名稱。
      2:不能重復創建模塊命名空間,模塊命名空間只能創建一次。
      3:須用字符串指出模塊命名空間名稱。
      4:內存錯誤。
      5:該命名空間已被其他模塊使用。
      6:要創建的命名空間不能是空字符串。

    28.10 輸出模塊命名空間中的表達式 OutFun("fun1","fun2",... ...):

    該函數為模塊命名空間輸出表達式。模塊命名空間即調用函數OutFun的模塊的命名空間。須用字符串 參數指出表達式的名稱。

    請參考模塊命名空間了解該函數的使用。

    該函數返回值的意義如下:

      0:操作成功。
      i:其他正整數:前i-1個參數操作成功,第i個參數操作失敗,其他參數未進行操作。

    28.11 獲得表達式的句柄 HFor("ForName",bType,&nPara,&hModule,&bParaModify):

    ForName:表達式的名稱。可以是簡單名稱,也可以是包含模塊命名空間訪問符::(該訪問符可簡化為一個或多個:)的復雜名稱。
   
bType:名稱的類型。如果bTypetrue,只能取簡單名稱。如果bTypefalse,名稱 可包含模塊命名空間訪問符::。 若缺省該參數,默認為true
   
nPara:返回表達式的參數個數。該參數可以缺省。
    hModule:返回表達式所在的模塊。該參數可以缺省。
    bParaModify:返回一個邏輯值,表示表達式的自定義參數在執行過程中是否可能被修改,邏輯真表示可能被修改。該參數可以缺省。

    28.12 訪問命名空間 using("NameSpace1","NameSpace2","NameSpace::name",... ...):

    命名空間(包括模塊命名空間、二級函數命名空間和常量命名空間)比較長時,輸入比較麻煩,使用using函數可簡化命名空間的訪問。

    執行過函數using("NameSpace1","NameSpace2","NameSpace::name","ConstSpace",... ...)后,如果有一個命名空間中的函數NameSpace2::fun(x,y),不必寫出該命名空間,直接使用函數fun(x,y)就可以了;如果有一個常量命名空間中的常量ConstSpace::MyConst,也不必寫出該命名空間,直接使用常量MyConst就可以了。 函數命名空間若與常量命名空間相同,只寫出一個即可。如下列:

      using("NameSpace1","NameSpace2","NameSpace::name","ConstSpace");
      2+fun(5,6)+MyConst+3;

    注意:如果有一個非命名空間中的函數fun(x,y),將優先調用該函數,遇到這種情況,應使用函數的全名:NameSpace2::fun(x,y)。 如果有一個非命名空間中的常量MyConst,將優先調用該常量,遇到這種情況,應使用常量的全名:ConstSpace::MyConst

    可使用using函數一次指定多個命名空間,當再一次調用using函數時,以前指定的命名空間將被刪除,可訪問的命名空間被重新設定。用using函數指定的命名空間僅在本模塊中起作用,換句話說,可在不同的模塊中使用using函數而互不影響。

    28.13 標記需自動銷毀的對象 free(p):

    函數free(p)可將對象p自動記錄下來,在銷毀表達式時銷毀對象p。函數free(p)仍然返回對象p,若標記失敗返回0。用法舉例如下:

      ConstObj(:static,obj)=
      {
        if{!obj, obj=new[...].free()},
 //第一次調用函數ConstObj()時,用函數new申請一個對象obj,并用函數free()標記在銷毀表達式時銷毀它
        obj
                            //返回對象obj
      
};
      ConstObj();                      //每次調用函數ConstObj(),都會得到同一個對象
      ConstObj();                      //每次調用函數ConstObj(),都會得到同一個對象

    注意:函數free(p)并不立即銷毀對象p,而是在銷毀表達式時銷毀該對象。并不是所有的對象都可由函數free銷毀,這取決于主程序和Lu擴展庫的設計。函數free(p)只適合銷毀少量的數據,故不要將其放在循環中使用。

    注意:靜態變量free和函數free(p)不必同時使用,同時使用將降低效率。

    28.14 獲得對象信息 o(p1,p2,p3,... ...):

    獲得對象p1,p2,p3,... ...的信息。函數返回輸出的信息串的長度。

    o函數是被重載的,故該函數執行時,對象p1,p2,p3,... ...的重載o函數將被依次調用。

    可直接通過o函數輸出的內置對象有:整數、實數、復數、三維向量、nil、表達式句柄、邏輯值、字符串。

    不是所有的對象都支持o函數,具體請查看該對象的說明。

    28.15 對象賦值 oset(p,... ...):

    p是一個對象指針,函數oset(p,... ...)可對對象p賦值。oset函數的調用是與等號相關聯的。

    若LuErr=1:參數 不匹配;LuErr=2:沒有為對象重載該函數;LuErr=3:非法的對象指針;LuErr=4:參數不符合要求。

    不是所有的對象都支持oset函數,具體請查看該對象的說明。

    例子:設A、B是二維數組對象,i=2,j=3

    A(2,3)=1;    //相當于 oset[A,2,3 : 1]

    A.i.j=1;     //相當于 oset[A,i,j : 1]

    A.=A+B;      //相當于 oset[A : A+B]

    28.16 獲得對象的值 oget(p,... ...):

    p是一個對象指針,函數oget(p,... ...)可獲得對象p的值;p也可以是一個自定義函數的指針,此時將產生一個函數調用。oget函數的調用不與等號相關聯。

    該函數的返回值請參考對象的有關說明。若p是函數指針,則該函數返回函數調用的值。

    若LuErr=1:參數 不匹配;LuErr=2:沒有為對象重載該函數;LuErr=3:非法的對象指針;LuErr=4:參數不符合要求;LuErr=5:調用函數時參數不匹配;LuErr=6:非法的函數句柄。

    不是所有的對象都支持oget函數,具體請查看該對象的說明。

    例子1:對象指針。設A是一個二維數組對象,i=2,j=3

    A(2,3)=A(2,3)+1;    //相當于 oset[A,2,3 : oget(A,2,3)+1]

    A.i.j=A.i.j+1;      //相當于 oset[A,i,j : oget(A,i,j)+1]

    例子2:函數指針

    a(x)=x+2;
    (:p)= p=HFor("a"), p(3);   
//p(3)相當于oget(p,3),即調用函數a(3)

    28.17 垃圾收集 gc():

    立即掃描并銷毀垃圾對象,釋放所占空間。函數返回0表示運行成功,否則表示不允許運行垃圾收集器。

    在Lu中,一個對象用一個指針標識,該指針存放在一個Lu數據中,此時,我們稱這個數為指向該對象的指針。若Lu系統中的靜態變量、模塊變量、全局變量以及正在運行的表達式的數據區(自變量、動態變量及數據堆棧)中存在一個數對 指向一個全局象時,該對象是有效的,否則視為垃圾對象,會被垃圾收集器所回收。

    28.18 獲得或設置動態對象數目 ObjNum(n):

    當n>=100時設置允許的動態對象的最大數目,否則返回當前動態對象的數目。

    通常,Lu在檢測到動態對象數目超過了允許的最大值時,將啟動垃圾收集器進行清理。

    28.19 清空垃圾對象緩沖區 ClearBuf():

    動態對象生命期結束時,或者用函數del(...)刪除對象時,對象會暫時存放在垃圾對象緩沖區中,該函數用于清空垃圾對象緩沖區。

    28.20 生成Lu動態數據 lu(a,b,c,...):

    Lu動態數據存放在一個線性表中,簡稱lu表,lu表中可以存放任意類型的數據,包括nil和lu表。

    還可以用函數new[lu,5:a,b,c,...]創建lu表,此時可指定lu表的長度并賦初值。

    lu表是Lu的基本動態數據類型。函數以lu表為參數時,可實現變參數傳遞。在垃圾收集時,若一個lu表是有效的,則該lu表中的所有對象都是有效的,不會被垃圾收集器所收集。

    28.21 將靜態字符串轉換為靜態一維整數數組 intss("...",x1,x2,x3,...):

    參數x1,x2,x3,...將給數組賦初值(只能是整數或實數,若是實數將截斷取整)。

    若LuErr=1:非法的字符串對象;LuErr=2:非法的初值參數 。

    28.22 將靜態字符串轉換為靜態一維實數數組 realss("...",x1,x2,x3,...):

    參數x1,x2,x3,...將給數組賦初值(只能是實數或整數,若是整數將自動進行類型轉換)。

    若LuErr=1:非法的字符串對象;LuErr=2:非法的初值參數 。

    28.23 強制類型轉換 cast(Data,MyType):

    強制將數據Data的擴展類型轉換為MyType類型。MyType是一個整數,標識某種類型的數據。若MyType為self,則將數據Data的擴展類型轉換為其基本類型。

    28.24 將動態對象放到垃圾對象緩沖區 del(x1,x2,x3,...):

    垃圾對象緩沖區中的對象將由Lu在合適的時候進行銷毀。

    28.25 立即銷毀對象 delete(x1,x2,x3,...):

    28.26 生成新的對象 new(type,x1,x2,x3,...):

    type是一個整數,標識某種動態數據類型;x1,x2,x3,...是初始化參數。

    Lu核心庫中定義的動態類型有lu表(lu)、動態字符串(string)、動態整數數組(ints)和動態實數數組(reals),見下表:

動態類型 標識符 例 子 說 明
lu表 lu new[lu,5] 生成長度為5的lu表。
new[lu,5 : 2, "aa", 3.3] 生成長度為5的lu表并賦初值,若有多余的初值參數,將被忽略。
字符串 string new[string,5] 生成長度為5的動態字符串。
new[string,5,"abc"] 生成長度為5的動態字符串并賦初值,初值是一個字符串參數,若字符串過長,將被截斷。
new[string,"abc"] 生成與所給字符串長度相同的動態字符串,并賦初值。
整數數組 ints new[ints,3,5] 生成二維數組。
new[ints,3,5,data: 11,22,33] 生成二維數組并賦初值,多余的參數被忽略。
new[ints,data: 11,22,33] 生成一維數組并賦初值,數組長度即初值參數個數。
實數數組 reals new[reals,3,5] 生成二維數組(矩陣)。
new[reals,3,5,data: 1.1,2.2,3.3] 生成二維數組(矩陣)并賦初值,多余的參數被忽略。
new[reals,data: 1.1,2.2,3.3] 生成一維數組并賦初值,數組長度即初值參數個數。

    28.27 返回對象長度 len(p,&x1,&x2,&x3,...):

    該函數返回對象p的長度,并通過參數x1,x2,x3等返回更多信息。如果是動態整數數組(ints)或動態實數數組(reals),則x1返回數組維數,x2,x3,...返回每一維的大小。

    28.28 創建新的擴展數據類型 newtype():

    該函數返回一個新的數據擴展類型(是一個整數),當該類型的數據成為Lu運算符或重載函數的操作數時,將調用與該擴展類型匹配的運算符重載函數。調用函數newtype的表達式即是與該擴展類型匹配的運算符重載函數,參考運算符及函數重載

    在一個表達式中多次調用newtype()時,僅返回一種數據擴展類型,或者nil。

    28.29 查詢表達式(包含協程)狀態 status(p):

    p是一個表達式句柄。 正常返回一個整數表示表達式狀態:0:普通表達式;1:普通表達式正在運行;2:普通表達式遞歸運行;5:協程;6:協程正在運行;7:協程掛起;8:協程終止。p非法時返回nil。

    28.30 中止掛起的協程 abort(p):

    p是一個協程句柄,正處于掛起狀態。協程中止后,可以再次從頭開始執行。操作成功返回true,否則返回false。

    28.31 終止協程 terminate(p):

    p是一個協程句柄,處于正常等待運行狀態或者掛起狀態。協程終止后將不能執行,執行時總返回nil。操作成功返回true,否則返回false。

    28.32 重啟一個終止的協程 resume(p):

    p是一個協程句柄,正處于終止狀態,重啟后可以運行。操作成功返回true,否則返回false。

29 運算符及函數重載 [返回頁首] [返回目錄]

    Lu運算符有單目運算符和雙目運算符兩種,最多有兩個操作數。Lu程序在運行時,如果遇到運算符,先查看第一操作數的擴展類型,找到其重載函數調用之,若第一操作數沒有重載函數,再查看第二操作數的擴展類型,找到其重載函數調用之,若仍沒有找到重載函數,將報告一個運行錯誤。

    Lu部分內置函數允許重載,對于一元函數或參數不確定函數,都是由第一操作數的擴展類型決定重載函數的調用,或者報告一個找不到重載函數的運行錯誤;對于二元函數,先查看第一操作數的擴展類型,找到其重載函數調用之,若第一操作數沒有重載函數,再查看第二操作數的擴展類型,找到其重載函數調用之,若仍沒有找到重載函數,將報告一個運行錯誤。

    在Lu中可重載的運算符及函數見下表:

theOperator 運算符 功  能 參數個數m+1 引起重載函數被調用的操作數 說  明
0 + 2 第一操作數優先,其次第二操作數  
1 - 2 第一操作數優先,其次第二操作數  
2 * 2 第一操作數優先,其次第二操作數  
3 % 求模 2 第一操作數優先,其次第二操作數  
4 / 左除 2 第一操作數優先,其次第二操作數  
5 \ 右除 2 第一操作數優先,其次第二操作數  
6 ^ 乘方 2 第一操作數優先,其次第二操作數  
7 - 1 第一操作數  
8 ' 轉置 1 第一操作數  
9 ++o 前置自增 1 第一操作數 通常先對當前對象自增,再返回當前對象。
10 --o 前置自減 1 第一操作數 通常先對當前對象自減,再返回當前對象。
11 o++ 后置自增 1 第一操作數 通常返回新對象。
12 o-- 后置自減 1 第一操作數 通常返回新對象。
13 << 左移位 2 第一操作數優先,其次第二操作數  
14 >> 右移位 2 第一操作數優先,其次第二操作數  
15 > 大于 2 第一操作數優先,其次第二操作數  
16 >= 大于等于 2 第一操作數優先,其次第二操作數  
17 < 小于 2 第一操作數優先,其次第二操作數  
18 <= 小于等于 2 第一操作數優先,其次第二操作數  
19 == 等于 2 第一操作數優先,其次第二操作數  
20 != 不等于 2 第一操作數優先,其次第二操作數  
21 & 2 第一操作數優先,其次第二操作數  
22 | 2 第一操作數優先,其次第二操作數  
23 ~ 異或 2 第一操作數優先,其次第二操作數  
24 ! 1 第一操作數  
25 .* 點乘 2 第一操作數優先,其次第二操作數  
26 ./ 點左除 2 第一操作數優先,其次第二操作數  
27 .\ 點右除 2 第一操作數優先,其次第二操作數  
28 .^ 點乘方 2 第一操作數優先,其次第二操作數  
29 .' 點轉置 1 第一操作數  
30 && 按位與 2 第一操作數優先,其次第二操作數  
31 || 按位或 2 第一操作數優先,其次第二操作數  
32 ~~ 按位異或 2 第一操作數優先,其次第二操作數  
33 !! 按位非 1 第一操作數  
34 $ 2 第一操作數優先,其次第二操作數  
35~38 未定義        
39 sum 重載函數 不確定 第一操作數 求和。
40 pro 重載函數 不確定 第一操作數 求積。
41 deepcopy 重載函數 不確定 第一操作數 深復制對象。
42 get 重載函數 不確定 第一操作數 獲得對象(成員)。
43 set 重載函數 不確定 第一操作數 設置對象(成員)。
44 len 重載函數 不確定 第一操作數 返回對象長度。
45 copy 重載函數 不確定 第一操作數 復制對象。
46 new 重載函數 不確定 第一操作數 第一操作數是一個整數,表示一種擴展數據類型。該函數返回一個新對象。
47 oset 重載函數 不確定 第一操作數 對象賦值。
48 oget 重載函數 不確定 第一操作數 獲得對象的值。
49 o 重載函數 1 第一操作數 獲得對象信息。函數要返回信息串的長度。
50 sqrt 重載函數 1 第一操作數  
51 exp 重載函數 1 第一操作數  
52 ln 重載函數 1 第一操作數  
53 lg 重載函數 1 第一操作數  
54 sin 重載函數 1 第一操作數  
55 cos 重載函數 1 第一操作數  
56 tan 重載函數 1 第一操作數  
57 asin 重載函數 1 第一操作數  
58 acos 重載函數 1 第一操作數  
59 atan 重載函數 1 第一操作數  
60 sinh 重載函數 1 第一操作數  
61 cosh 重載函數 1 第一操作數  
62 tanh 重載函數 1 第一操作數  
63 abs 重載函數 1 第一操作數  
64 floor 重載函數 1 第一操作數  
65 ceil 重載函數 1 第一操作數  
66 itor 重載函數 1 第一操作數  
67 rtoi 重載函數 1 第一操作數  
68 con 重載函數 1 第一操作數  
69 atan2 重載函數 2 第一操作數優先,其次第二操作數  
70 fmod 重載函數 2 第一操作數優先,其次第二操作數  

    重載函數的格式如下:

mytype(x,y,...,num,op)=which  //x,y,...中,x是第一操作數,y是第二操作數,以此類推;num是操作數個數;op標識所重載的運算符或函數。
{
  op<0  : return[newtype()], 
//返回重載函數相關的擴展數據類型
  op==0 : { ... },   
//重載運算符+
  op==1 : { ... },   
//重載運算符-
  op==2 : { ... },   
//重載運算符*
  ... ,              
//重載其他運算符
  { nil }            
//該數據類型不支持該運算符的重載 ,返回nil;若定義了該運算符或函數的操作,但操作失敗時,應返回nil1。
};

    重載函數的參數個數至少為4個。重載函數被調用時,前面的參數將傳入實際的操作數,并將所有操作數的擴展類型重置為與其基本類型相同,多于的參數被置為nil;倒數第二個參數num傳入實際的操作數個數;而最后一個參數op決定對哪個運算符或函數進行重載。當op<0時,由函數newtype()獲取并返回一個數據的擴展類型,正是該擴展類型引起該重載函數的調用。

    由運算符重載引起的運行錯誤,其出錯函數名為operator::operator,錯誤代碼:1:找不到該擴展類型的重載函數;2:重載函數的參數個數太少,而實際要傳入的參數個數太多。

    例子:

thetype(x,y,num,op)=which
{
  op<0  : return[newtype()],
  op==0 : x-y,   
//重載運算符+
  op==1 : x+y,   
//重載運算符-
  op==2 : x/y,   
//重載運算符*
  nil            
//該數據類型不支持該運算符的重載,返回nil
};
test(:type,a,b)=
  type=thetype(0,0,0,-1), 
//獲取新數據類型
  a=cast[3,type], b=cast[5,type], 
//強制轉換為新數據類型
  o[" a=",3," b=",5], 
//輸出a和b
  o[" a+b=",a+b], 
    //計算并輸出a+b,變成了a-b
  o[" a-b=",a-b],     
//計算并輸出a-b,變成了a+b
  o[" a$b=",a$b];     
//沒有重載運算符$,故輸出nil

30 協程 [返回頁首] [返回目錄]

    如果表達式(函數)中使用了函數yield,該表達式稱為一個協程(coroutine)。協程不能遞歸運行,只有這一個限制。Lu協程有4種狀態:正常、運行、掛起、 終止。正常態是可以運行但還沒有運行的狀態;終止態協程是不能運行的,如果運行將返回nil。與協程相關函數有5個:

    yield:掛起協程并立即返回表達式(函數)的值。
    status:查詢表達式(包含協程)的狀態。
    abort:中止掛起的協程,此時協程處于正常態。
    terminate:終止協程,協程處于終止態,不能運行。
    resume:重啟一個終止的協程,協程轉為正常態。

    例子1:

f(x:i)= i=0, while{(i++, i<=5), yield(i)}, 888;
f[0]; f[0]; f[0]; f[0]; f[0]; f[0]; f[0]; f[0];

    結果(返回888時協程轉為了正常態):

1
2
3
4
5
888
1
2

    例子2:協程狀態轉換

f(x:i)= i=0, while{(i++, i<=5), yield(i)}, 888;
f[0]; f[0]; status[@f]; f[0]; abort[@f]; f[0]; terminate[@f]; status[@f]; f[0]; resume[@f]; f[0]; f[0]; f[0];

    結果(對照status、abort、terminate、resume等函數的返回值很容易理解以下結果):

1
2
7
3
true
1
true
8
nil
true
1
2
3

    例子3:篩選數據,輪詢方式工作。

out_lt_5(x)=
  while{true,
    which{x>=0 & x<5:
      {o["out_lt_5:0~4的數:", x, "\r\n"], yield(true)},   
//篩選成功,掛起協程,返回true
      {o["out_lt_5:不是0~4的數。\r\n"], yield(false)}      
//篩選失敗,掛起協程,返回false
    }
  };
out_gt_5(x)=
  while{true,
    which{x>5 & x<=9:
      {o["out_gt_5:5~9的數:", x, "\r\n"], yield(true)},   
//篩選成功,掛起協程,返回true
      {o["out_gt_5:不是5~9的數。\r\n"], yield(false)}      
//篩選失敗,掛起協程,返回false
    }
  };
out_other(x)= while{true, o["out_other:其他的數據:", x, "\r\n"], yield(true)};   
//篩選總是成功,掛起協程,返回true
main(:x,k,i)=
  x=lu[8,3,22,2,9,55,-33,1],
  k=len(x), i=-1,
  while{(i++, i<k), o["\r\nmain:篩選數據:", x(i), "\r\n"], which{out_lt_5[x(i)], 0, out_gt_5[x(i)], 0, out_other[x(i)]}};

    結果:

main:篩選數據:8
out_lt_5:不是0~4的數。
out_gt_5:5~9的數:8

main:篩選數據:3
out_lt_5:0~4的數:3

main:篩選數據:22
out_lt_5:不是0~4的數。
out_gt_5:不是5~9的數。
out_other:其他的數據:22

main:篩選數據:2
out_lt_5:0~4的數:2

main:篩選數據:9
out_lt_5:不是0~4的數。
out_gt_5:5~9的數:9

main:篩選數據:55
out_lt_5:不是0~4的數。
out_gt_5:不是5~9的數。
out_other:其他的數據:55

main:篩選數據:-33
out_lt_5:不是0~4的數。
out_gt_5:不是5~9的數。
out_other:其他的數據:-33

main:篩選數據:1
out_lt_5:0~4的數:1

    例子4:篩選數據,自己不能處理就交給下一個協程。

out_other(x)= while{true, o["out_other:其他的數據:", x, "\r\n"], yield(true)};
out_gt_5(x)=
  while{true,
    which{x>5 & x<=9:
      {o["out_gt_5:5~9的數:", x, "\r\n"], yield(true)},          
//篩選成功,掛起協程,返回true
      {o["out_gt_5:不是5~9的數。\r\n"], out_other(x), yield(true)}
//篩選失敗,交給out_other處理,然后掛起協程,返回true
    }
  };
out_lt_5(x)=
  while{true,
    which{x>=0 & x<5:
      {o["out_lt_5:0~4的數:", x, "\r\n"], yield(true)},
          //篩選成功,掛起協程,返回true
      {o["out_lt_5:不是0~4的數。\r\n"], out_gt_5(x), yield(true)}
//篩選失敗,交給out_gt_5處理,然后掛起協程,返回true
    }
  };
main(:x,k,i)=
  x=lu[8,3,22,2,9,55,-33,1],
  k=len(x), i=-1,
  while{(i++, i<k), o["\r\nmain:篩選數據:", x(i), "\r\n"], out_lt_5[x(i)]};
//篩選時只有一個入口out_lt_5

    結果同例子3。

    例子5:生產者與消費者(使用了擴展庫LuSystem,命名空間為sys)。

!!!using["sys"];      //使用命名空間sys
生產者(x:pp,k,i:p)=   //自變量x僅用于避免該函數自動執行
    p=nil,            //p是模塊變量,用于傳遞數據
    pp=lu[2,"aaa",6], //pp存放數據
    k=len(pp), i=0,
    while{i<k,
        o["\r\n---生產者---\r\n"],   //打印所在位置
        if{isnil(p), p=pp[i++], o["\r\n---生產者發送數據  ", p, "\r\n"]},  //p=nil時發送一個數據
        yield(true)   //掛起協程
    };
消費者(x:i:p)=        //自變量x僅用于避免該函數自動執行
    i=0,              //控制變量,i為偶數時消費者才消費
    while{true,
        o["\r\n---消費者---\r\n"], //打印所在位置
        if[++i%2, yield(true)],    //i為奇數時掛起協程
        if{!isnil(p), o["\r\n---消費者輸出數據  ", p, "\r\n"], p=nil}, //p!=nil時輸出一個數據
        if[status(@生產者)==8, break()],   //如果生產者終止,消費者就退出
        yield(true)   //掛起協程
    };
main()=
    while{true,
        o["\r\n---main---\r\n"],  //打印所在位置
        生產者[0],
        if[status(@生產者)==5, terminate(@生產者)],  //如果生產者結束,就終止生產者
        消費者[0],
        if[status(@消費者)==5, break()]  //如果消費者結束,就退出
    };

    結果:

---main---

---生產者---

---生產者發送數據 2

---消費者---

---main---

---生產者---

---消費者輸出數據 2

---main---

---生產者---

---生產者發送數據 aaa

---消費者---

---消費者輸出數據 aaa

---main---

---生產者---

---生產者發送數據 6

---消費者---

---main---

---消費者輸出數據 6

31 動態內存管理 [返回頁首] [返回目錄]

    C程序員要自己管理內存的分配和回收,而Python具有垃圾自動回收的機制,Lu的動態內存管理兼有二者的特點:既可以手動回收垃圾,也可以完全依賴于Lu的垃圾自動回收機制。通常,Lu中用類似new的函數生成動態對象,而用類似delete的函數銷毀動態對象,這一點類似于C,用戶可以高效地管理內存;所有用戶沒有銷毀的對象,會由Lu的垃圾收集器管理,并最終會被Lu安全地回收,這一點類似于Python。

    除Lu核心庫中提供的動態對象外,支持模塊可以使用C/C++、Delphi等語言創建任意的動態對象并加入Lu系統。

    在Lu中,一個對象用一個指針標識,該指針保存在Lu數據中,此時稱這個數為指向該對象的指針。若Lu系統中的靜態變量、模塊變量、全局變量以及正在運行的表達式的數據區(自變量、動態變量及數據堆棧)中存在一個數指向一個對象時,該對象是有效的,否則視為垃圾對象,會被垃圾收集器所回收。

    由于只要有一個指針(直接或間接)指向某對象,該對象就不會被垃圾收集器回收,故若要確保立即銷毀某對象,應使用delete之類的專用函數,而不要依賴垃圾收集器。

    Lu的內存管理特點:(1)與C/C++類似,用戶可立即銷毀一個對象;(2)被用戶忽略的垃圾將被自動回收;(3)任何時候,可手動立即啟動垃圾收集器;(4)垃圾回收是有運行開銷的,但如果用戶有效地管理了內存,垃圾回收器可以一次也不啟動。

    啟動Lu垃圾收集器:

    (1)用戶調用函數gc()將立即啟動垃圾收集器。

    (2)當Lu檢測到對象太多時將自動啟動垃圾收集器。Lu用一個計數器記住生成的動態對象數目,當達到規定值時,就啟動垃圾收集器;允許用戶修改對象計數器允許的最大值。

    (3)主程序可能會按一定的算法在處理器空閑時調用Lu垃圾收集器,這取決于主程序的設計。

    注意:與其他語言的垃圾收集器不同,Lu垃圾收集器總是立即啟動的。

    使用協程將有助于動態對象的管理,參考:通過協程初始化和銷毀數據

32 表達式的初始化及銷毀  [返回頁首] [返回目錄]

    (1)通過 協程初始化和銷毀數據:強烈建議的方法

    協程在掛起時,里面產生的所有數據(包含局部動態對象)被保存,故可以設計一個永遠掛起的協程產生全局對象(實際上,對該協程來說,這些對象是局部的,但可以使該協程始終處于掛起狀態,直至協程被顯示地終止),例如:

global_obj(x,y,z:i,j,k)= i=lu[2,3], j=lu[2.3], k=lu["全局字符串"], while{true, x=i, y=j, z=k, yield(0)}; //通過函數global_obj獲得的三個對象將始終有效

    也可以將對象保存在模塊變量中:

global_obj(::i,j,k)= i=lu[2,3], j=lu[2.3], k=lu["全局字符串"], while{true, yield(0)}; //模塊變量i,j,k中的三個對象將始終有效

    (2)通過靜態變量初始化和銷毀數據

    通過靜態變量,Lu表達式可以進行初始化,也可以借助專用靜態變量free進行銷毀表達式前的釋放工作(Lu在銷毀表達式前將自動設置free=1,然后自動執行表達式)。如下例:

      f(x:y,static,d,e,free)=           //表達式用o函數輸出字符串
      {
        if{d==0,o("初始化!"),d=1},     
//注意靜態變量d使初始化僅執行一次。
        if{free,o("銷毀!"),return(0)},
//Lu在銷毀表達式前將自動設置free=1,然后自動執行表達式,使銷毀僅執行一次。
       
x+1                             //每次調用使自變量增1。
     
};
      f(0);

      f(1);
      f(2);

    為了充分發揮靜態變量free的作用,在表達式中不要對其有任何的賦值運算,以免引起混亂。另外,如果沒有將free定義為靜態變量,Lu在銷毀表達式前將不會將其自動設置為1,也不會自動執行表達式。

    如果一個表達式中直接調用的其他表達式或二級函數被刪除,該表達式將不能運行,這樣即便在該表達式中定義了靜態變量free,在Lu銷毀該表達式前也無法執行該表達式。如下例:

      //函數f中調用了函數a,如果函數a先于函數f被銷毀,則函數f將無法進行正常的銷毀工作。

      a(x)=x+1;

      f(x:y,static,d,e,free)=
      {
        if{d==0,o("初始化!"),d=1},        
//注意靜態變量d使初始化僅執行一次。
        if{free,o("銷毀!"),return(0)},
   //如果函數a先于函數f被銷毀,則函數f將無法進行正常的銷毀工作。
        a(x)
+1                            //調用函數a。
     
};
      f(0);

      f(1);
      f(2);

    若要上面的程序正常工作,主程序在銷毀表達式時必須遵循“后進先出”的原則,即:后編譯的表達式先刪除。

    動態調用表達式或二級函數,能使上面的程序正常工作。如下例:

      //函數f動態調用了函數a,即便函數a先于函數f被銷毀,也不影響函數f執行正常的銷毀工作。

      a(x)=x+1;

      f(x:y,aa,static,d,e,free)=
      {
        if{d==0,o("初始化!"),d=1},       
//注意靜態變量d使初始化僅執行一次。
        if{free,o("銷毀!"),return(0)},
  //Lu在銷毀表達式前將自動設置free=1,然后自動執行表達式,使銷毀僅執行一次。
        aa=HFor("a"),
                    //動態獲取函數a的句柄。
        aa(x)
+1                          //通過函數句柄aa動態調用函數a,使自動銷毀正常進行。
      
};
      f(0);
      f(1);
      f(2);

    (3)通過函數free(p)銷毀數據

    函數free(p)可將對象p自動記錄下來,在銷毀表達式時銷毀對象p。用法舉例如下:

      ConstObj(:static,obj)=
      {
        if{obj==0, obj=new[...].free()},
 //第一次調用函數ConstObj()時,用函數new申請一個對象obj,并用函數free()標記在銷毀表達式 時銷毀它
        obj
                              //返回對象obj
      
};
      ConstObj();                        //每次調用函數ConstObj(),都會得到同一個對象
      ConstObj();                        //每次調用函數ConstObj(),都會得到同一個對象

    注意:函數free(p)并不立即銷毀對象p,而是在銷毀表達式時銷毀該對象。并不是所有的對象都可由函數free銷毀,這取決于主程序和Lu擴展庫的設計。函數free(p)只適合銷毀少量的數據,故不要將其放在循環中使用。

    注意:靜態變量free和函數free(p)不必同時使用,同時使用將降低效率。

    最后指出,能否進行銷毀表達式前的釋放工作,取決于主程序的設計,與主程序如何使用Lu的輸出函數有關。即便銷毀表達式前沒有進行釋放工作,Lu最后也將釋放所有資源,因而無需擔心太多。

    簡單地,使用Lu的垃圾收集器,可不必關心垃圾對象的銷毀問題。

33 錯誤處理 [返回頁首] [返回目錄]

    錯誤有編譯錯誤和運行錯誤兩種。通常,Lu程序總是準確定位編譯期錯誤的。若程序編譯通過,在運行時仍會發生錯誤,通常,Lu程序總是記住并給出第一個運行錯誤的信息,錯誤信息包括運行出錯的表達式的類型和名稱(可見,給每一個表達式都起一個名字是非常重要的)、表達式所在的模塊、運行出錯的函數名、錯誤代碼等等,用戶可根據這些信息查找并修改錯誤。

    Lu運行時使用錯誤(異常)處理的恢復模型。模塊化編譯運行庫MLu中提供了一組函數以支持該功能:

    1、檢測輸出并清除Lu運行錯誤:err();

    2、獲得Lu運行錯誤:geterr(FunName,&FunCode,&ForHandle);

FunName:Lu字符串,返回出錯函數名。
FunCode:返回函數錯誤代碼。
ForHandle:返回出錯表達式的句柄,該句柄即編譯表達式時獲得的句柄。
返回值:運行錯誤的類型。返回值=0表示沒有運行錯誤,否則發生運行錯誤。

    3、設置Lu運行錯誤:seterr(ErrType,FunName,FunCode);

ErrType:設置運行錯誤的類型。
FunName:設置出錯的函數名,要求傳遞一個Lu字符串(長度小于80)。
FunCode:設置函數錯誤代碼。

    4、清除Lu運行錯誤:clearerr();

    以下例子用函數seterr設置了一個Lu運行錯誤,并用函數geterr捕獲了該錯誤:

f(:a,b,c,d,e)= seterr[66,"aaa",23], a=geterr[b="\[10]",&c,&d], o[b];

    Lu運行錯誤可由Lu系統、二級函數,或者由Lu腳本用戶進行設置,可在任意位置設置運行錯誤,同時可在任意位置捕獲該運行錯誤。運行錯誤一經設置,將始終存在,直到遇到函數clearerr()err()為止。在執行MLu模塊的最后,將自動調用函數err()

    捕獲Lu運行錯誤并進行處理后,可調用函數clearerr()清除運行錯誤,重啟程序代碼,通常需借助循環完成該處理過程。

34 關鍵字 [返回頁首] [返回目錄]

    在一般的編程語言中,關鍵字是事先定義的有特別意義的字,關鍵字是保留字,不能用來做標識符(如變量名) ,使用關鍵字來做變量名是一種語法錯誤,不能通過編譯。按此定義,則Lu中沒有關鍵字。Lu中只有常量、變量和函數。但有些符號常量、變量名或函數名使用很頻繁,可當作“關鍵字”來使用,不過不符合上述關鍵字的定義,例如:

f(return) = return(return+1);  //return是自變量,同時也是一個二級函數。
f(2);

    Lu允許符號常量、變量和函數用同一個標識符表示,參考標識符解釋規則。但盡量避免這種用法。

    Lu核心庫中的“關鍵字”見下表 :

關鍵字 類型 功 能
static 靜態變量 定義靜態變量。
free 靜態變量、二級函數 專用靜態變量或函數,進行銷毀表達式前的釋放工作。
common 全局變量 定義全局變量。
const 二級函數 定義永久性符號常量或暫時性符號常量。
return 二級函數 結束計算并立即返回表達式的值。
if 二級函數 條件滿足時執行計算多個語句。
which 二級函數 自定義分段函數,選擇計算函數。
while 二級函數 “當型”循環函數。
until 二級函數 “直到型”循環函數。
continue 二級函數 返回while或until循環的開始。
break 二級函數 跳出while或until循環。
Module 二級函數 創建模塊命名空間。
OutFun 二級函數 輸出模塊命名空間中的表達式。
HFor 二級函數 獲得表達式的句柄。
using 二級函數 訪問命名空間。
new 二級函數 申請新的動態對象。
del 二級函數 將動態對象放到垃圾對象緩沖區中,由系統在合適的時候銷毀它。
delete 二級函數 立即銷毀動態對象。
global 一級函數 將局部動態對象轉換為全局動態對象。
local 一級函數 將全局動態對象轉換為局部動態對象。
o 二級函數 獲得對象信息。
oset 二級函數 對象賦值。
oget 二級函數 獲得對象的值。
yield 一級函數 掛起協程并立即返回表達式的值。
status 二級函數 查詢表達式(包含協程)狀態。
abort 二級函數 中止掛起的協程。
terminate 二級函數 終止協程。
resume 二級函數 重啟一個終止的協程。

35 標識符解釋規則  [返回頁首] [返回目錄]

    1)若標識符后面有括號,表示是一個函數,否則是一個變量或常量名。

    2)若標識符后面有句點“.”,表示將該標識符聯系到一個函數,例如:a.sin(x);或者產生一個函數調用,例如:a.b 相當于 a(b)。若變量是顯示說明的,則通過句點將產生隱含的oset函數或oget函數調用,稱變量函數調用,例如:a.b 相當于 oget[a,b];a.b=c 相當于 oset[a,b:c]。

    3)如果一個變量名與常量名相同,則常量名被忽略。確定變量或常量的順行是:變量、常量、常量命名空間。

    4)如果編譯器允許使用未定義的模塊變量,當遇到一個未定義的標識符時,將被解釋為模塊變量或常量。如果該常量不存在,就解釋為模塊變量。如果該常量存在,但不存在同名的模塊變量,將解釋為常量。如果常量和同名的模塊變量同時存在,將優先解釋為模塊變量。 若要確保編譯器將未定義的標識符解釋為模塊變量,則應在前面編譯的表達式中,將該標識符明確地定義為模塊變量并至少使用一次。

    5)如果是一個普通的函數名,則確定函數的順行是:變量函數、一級函數或流程控制函數、自定義表達式、二級函數、using指定的命名空間中的函數。

    6)如果是一個命名空間中的函數,確定函數的順行是:模塊命名空間、二級函數命名空間。

    7)模塊私有表達式與一個公有表達式重名時,優先調用本模塊中的私有表達式。

36 效率 [返回頁首] [返回目錄]

    Lu中有以下提高效率的方法。

    1、將表達式中可以計算的部分放到括號中。

    例如,需要將表達式:

      F(x,y)=x-5-7+y

    寫成:F(x,y)=x-[5+7]+y或F(x,y)=x+[-5-7]+y。

    2、盡量使用自增減運算符++和--。

    例如要把 i=i+1 寫成 i++ 形式。

    3、until循環函數的速度比while循環函數要快。

    4、模塊變量和全局變量的訪問速度比自動變量快。

    5、盡量避免使用全局變量,全局變量會增加代碼維護的困難。

    6、對象成員運算符“.”將降低編譯速度,對運行速度沒有影響 。

    7、避免使用轉義字符"\[n]"開辟大的靜態空間,將影響編譯速度。

    8、盡量少用動態全局對象,即少用global函數。

37 主要技術指標  [返回頁首] [返回目錄]

    1、表達式最大長度:約2G。
    2、自變量個數:不限。
    3、動態變量、靜態變量、模塊變量、全局變量個數:不限。
    4、最多可用的表達式(含協程):不限。
    5、表達式中最多可用的字符串數:不限。
    6、自定義外部函數個數:不限。
    7、while循環、until循環最大循環次數:不限。
    8、表達式遞歸調用最多層數:受系統堆棧和自定義堆棧大小的限制,自定義堆棧最大為2G。
    9、最多可存儲的用戶自定義數據類型:約2G。
    10、執行速度:一級函數速度約為FORTRAN(或C/C++)執行速度的50%左右;其他情況,速度稍有下降;
    11、Lu32.DLL文件大小約220K~300K。
    12、內存需求:視Lu運行情況而定。


版權所有© Lu程序設計 2011-2013,保留所有權利
E-mail: [email protected]  QQ:630715621
最近更新: 2013年12月28日

pk10冠军百期错一