歡迎訪問 Forcal程序設計

FORCAL新手參考

目  錄

概述  
Forcal中的表達式(函數) 表達式、函數、變量、不同類型的函數調用
Forcal中的模塊 模塊、全局函數、私有函數
Forcal中的變量 自變量、動態變量、靜態變量、模塊變量和全局變量釋疑
函數的傳值調用和傳址調用 傳址調用可返回多個參數
用表達式作為函數的參數 函數句柄的使用
標準輸入輸出 printff和printf函數
工程實例 演示Forcal的功能
Forcal程序 Forcal程序由來自多個文件的若干個模塊組成
10 Forcal擴展動態庫 Forcal擴展動態庫中的函數可無縫地緊密合作完成一個復雜任務
11 指針和對象 這是復雜的東西,一帶而過
12 Forcal中的多線程 通過MForcal實現多線程
13 中文編程 這是很多人的夢想

1 概述 [返回頁首]

    Forcal是一個小巧、快速、語法簡單、具有強大可擴充性的輕量級嵌入式腳本。

    與其他語言或腳本相比,Forcal似乎有一些特殊的語法,這些特殊的語法主要體現在函數聲明、變量定義、模塊定義等方面;在語句實現上,如表達式語句、函數調用語句、流程控制語句等,與主流語言如C/C++等還是比較接近的。Forcal特殊的語法如函數聲明、變量定義、模塊定義等是簡潔高效的,不僅輸入速度快,在視覺感受上,也是一目了然的,體現了Forcal語法簡單的特點。

    本文主要介紹Forcal語法上的一些特殊之處,本文也將介紹學習Forcal必須具備的一些語法知識。但本文不對Forcal進行系統地介紹,而是將Forcal主要的語法特點和功能,概括性地介紹給初次接觸Forcal的新手。本文的介紹也許并不全面,但如果您閱讀本文不是太難,進一步系統地了解Forcal就會變得輕松愉快。

    在此之前,您應當至少學習過一門計算機語言,具備將一個數學公式轉換成計算機語言形式的表達式的能力。

2 Forcal中的表達式(函數) [返回頁首]

    Forcal表達式由一些逗號(或冒號)隔開的語句組成,通常用分號表示一個表達式的結束。如果表達式帶有名字,就定義了一個函數。如下列:

//在每行中兩個‘//’后的字符將被忽略,因而是注釋行
函數名(自變量1,自變量2,... ...)=    //在定義一個函數時,小括號(必須是小括號)和等號不可缺省
{                                 
//一對花括號{ }可以缺省
    自變量1=自變量1+自變量2,       
//語句1
    ... ...,                      
//語句i
    自變量1*自變量2                
//最后一個語句返回表達式的值
};

    這是Forcal特殊的函數定義方法,但很容易理解,與數學公式極為相似。例如定義兩個數相加:

f(x,y)=x+y;

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

    Forcal函數的完整定義為:

      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只能用在第二個冒號后。FORCAL中的所有變量均可缺省。以下都是合法的變量定義的例子:

      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;

    這是Forcal特有的變量定義方法,輸入方便,簡潔直觀,一目了然。Forcal依靠這5種變量,成為描述能力極強的腳本,可適應大型的工程。

    語法快速瀏覽:

    (1)在Forcal語句中(函數定義中,等號后面的表達式中)可任意使用三對括號:()、[]、{},只要成對使用即可。這將使表達式層次非常清晰。如:f(x)=2+exp{1/[2*(3-x)]};
    (2)如果括號不是用來說明函數參數的,則任意括號中可包含任意多個由逗號隔開的語句,括號具有一個值,即最后一個語句的值,這就是Forcal的括號運算符。如:[3,1,2]的值等于2。
    (3)一般,Forcal程序只計算無參表達式,對于有參數的表達式,只編譯,不計算。例如:

      F(x,y)=x+y;       //函數定義, 有自變量參數,只編譯,不計算
      F[2,3]+5;      
   //無參表達式,執行計算

    (4)Forcal表達式只有三種:整數表達式、實數表達式和復數表達式。一般,Forcal程序中以i:、r:、c:開頭的表達式分別是整數、實數、復數表達式,缺省是實數表達式。例如:

      i: a(x,y)=x+y;    //函數定義, 整數表達式
      r: 2.2+3;      
   //無參表達式,實數表達式
      c: (2-3i)*(3+2i); //無參表達式,復數表達式
      2.2+3;            //無參表達式,缺省是實數表達式

    (5)FORCAL不會為不同類型的函數調用進行類型轉換,用戶可根據需要自行轉換(傳遞句柄或指針參數時無需轉換)。例如:

      i:f(a,b)=a+b;              //整數表達式定義
      f(2,3);
                    //實數表達式調用整數表達式,但未進行數據轉換
      itor{f[rtoi(2),rtoi(3)]};
  //實數表達式調用整數表達式,函數rtoi將一個實數轉換成一個整數,函數itor將一個整數轉換成一個實數,計算值是正確的

      //以下函數調用無需進行數據轉換,當然這種函數調用是沒有必要的,在這里僅僅是一個例子
      a(x,y)=x+y;
                //實數表達式定義
      i:b(x,y)=a(x,y);
           //整數表達式調用實數表達式,沒有進行數據轉換
      b[2.2,3];
                  //實數表達式調用整數表達式,沒有進行數據轉換,計算值是正確的

3 Forcal中的模塊 [返回頁首]

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

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

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

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

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

    通常,可以用編譯符#MODULE##END#定義一個模塊,用編譯符~輸出模塊中的全局表達式。另外,若表達式名稱前有編譯符!(首先解釋為立即執行編譯符,而不是邏輯非運算符),在編譯后將立即執行;若表達式名稱前有編譯符:,只編譯,不執行。如下例:

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


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

    編譯指令小結:一般,Forcal程序中#MODULE#、#END#、~、i:、r:、c:、:、!等稱為編譯指令,用以確定一個表達式的類型、所在模塊、是否私有函數等屬性。這些編譯指令必須位于表達式的開頭,有些指令能同時使用,有些指令不能同時使用,并且在使用時有一定的次序,按先后順序依次為:

    1)編譯指令#MODULE#和#END#必須在表達式的最前面,一般單獨成行。#MODULE#表示開始一個子模塊的編譯,#END#表示回到主模塊的編譯。

    2)~表示該表達式是一個全局表達式,否則是私有表達式。該編譯符對主模塊(0#模塊)中的表達式無效,主模塊(0#模塊)中的表達式都是私有表達式。

    3)編譯指令i:、r:、c:不能混合使用,只能使用其中的一個,強制指定表達式的類型。如果都沒有用,表達式按實數類型進行編譯。

    4)編譯指令:、!不能混合使用,只能使用其中的一個。“:”表示該表達式只編譯,不執行;“!”表示該表達式編譯后立即執行,但以后執行時不再自動執行。

    如果表達式前沒有使用任何一個編譯指令,則按實數類型編譯為私有表達式,若該表達式是無參表達式,則執行模塊時將自動執行。

4 Forcal中的變量 [返回頁首]

    本節介紹自變量、動態變量、靜態變量、模塊變量和全局變量的功能用法,這是學好Forcal的必備基礎知識,如果你對此非常熟悉,可以跳過這一節。

    自變量用于向表達式傳遞參數,例如:

      F(x,y)=x+y;       //函數定義,自變量x,y用于向函數傳遞參數
     
F[2,3]+5;         //簡單的函數調用

    動態變量只在表達式執行時起作用,一旦表達式執行完畢,動態變量也隨之失效。例如:

      F(x: t)= t=1,x+t; //函數定義,t為動態變量,每次進入該函數,t開始起作用,退出該函數時,t的值不會保留。動態變量應先賦值,后使用
      F[2];      
       //簡單的函數調用

    靜態變量存在于表達式的整個生命周期,每次表達式執行完畢,靜態變量的值被保留。FORCAL在編譯表達式時,將所有靜態變量初始化為0。例如:

      F(:static,t)= t++;//函數定義,t為靜態變量,初始值為0。每次調用該函數,返回值增1
      F[];      
        //簡單的函數調用
      F[];      
        //簡單的函數調用

    模塊變量可被同一模塊的所有表達式所訪問,其他模塊的表達式無法訪問;全局變量可被所有的表達式所訪問。例如:

#MODULE#                            //定義一個子模塊
  a(::mm,common,cc)= mm=11,cc=22;
   //mm是模塊變量,cc是全局變量
  b(::mm)= mm;                     
//輸出模塊變量mm
  c(::common,cc)= cc;               
//輸出全局變量cc
#END#                              
//子模塊定義結束


#MODULE#                   
        //定義一個子模塊
  b(::mm)= mm;                     
//輸出模塊變量mm,mm沒有預先賦值,輸出的是隨機值
  c(::common,cc)= cc;               
//輸出全局變量cc
#END#

    注意:全局變量在整個Forcal系統中只有一個備份,故所有表達式均可訪問;Forcal系統中有多個模塊,模塊變量在同一模塊中只有一個備份,只有本模塊的表達式可以訪問;每個模塊由多個表達式組成,自變量、動態變量及靜態變量只有所在的表達式可以訪問到。另外,靜態變量在一個表達式中只有一個備份,但自變量和動態變量不是這樣,每當該表達式被調用時,系統都將重新給他們分配變量空間,只不過自變量自然地接受函數調用時所傳過來的值,而動態變量中是一些隨機值,故動態變量要先賦值,后使用。

    以下例子演示了動態變量與靜態變量及模塊變量的區別:

      SetRealStackMax(1000);                       //設置實數堆棧為1000
      //階乘函數Fact的遞歸實現;故意使用了動態變量t,每次進入函數Fact,都將給t分配空間,故函數能正常工作。若將t改成靜態變量或模塊變量,函數將不能正常工作,可以試一下

      Fact(n:t)= t=n,which{t<=1,1,Fact(t-1)*t};  
 //階乘函數Fact的遞歸實現,使用動態變量t
      //Fact(n:static,t)= t=n,which{t<=1,1,Fact(t-1)*t};  //階乘函數Fact的遞歸實現,使用靜態變量t
      //Fact(n::t)= t=n,which{t<=1,1,Fact(t-1)*t};        //階乘函數Fact的遞歸實現,使用模塊變量t
      Fact(3);                                     //計算3!
      Fact(5);
                                     //計算5!
      Fact(10);
                                    //計算10!

    給上面的例子添加驗證代碼,如下所示:

      SetRealStackMax(1000);                       //設置實數堆棧為1000
      //階乘函數Fact的遞歸實現;故意使用了動態變量t,每次進入函數Fact,都將給t分配空間,故函數能正常工作。若將t改成靜態變量或模塊變量,函數將不能正常工作,可以試一下

      Fact(n:s,t)={  
                             //階乘函數Fact的遞歸實現,使用動態變量t。若使用靜態變量t,可改為:Fact(n:s,static,t);若使用模塊變量t,可改為:Fact(n:s:t)
        t=n,
        printff{"函數Fact調用前的t={1,r}\r\n",t},
        s=which{t<=1,1,Fact(t-1)*t},
        printff{"函數Fact調用后的t={1,r}\r\n",t},
        s
      };

      Fact(3);                                     //計算3!
      Fact(5);
                                     //計算5!
      Fact(10);
                                    //計算10!

5 函數的傳值調用和傳址調用 [返回頁首]

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

      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

    以下函數交換了兩個數的值:

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

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

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

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

    例子:

      F(x,y)=sin[x]+0.8-y;       //定義二元函數;
      sum("F",1,2,0.01,2,5,0.1); //對函數F循環求和:x自1到2,增量為0.01;y自2到5,增量為0.01。

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

    例子:

      i: aa(x)=x+8;                             //定義一元函數aa;
      i: (:x)=sys::call[1,HFor("aa",1),7,&x],x; //調用一元函數aa,并將參數7傳遞給該函數;sys::call是Forcal系統擴展庫FcSystem32W的一個函數。

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

7 標準輸入輸出 [返回頁首]

    Forcal數據類型擴展動態庫FcData中提供了Forcal自己的標準輸入輸出函數printff和scanfs;而標準輸入輸出庫FcIO中提供了printf、sprintf、fprintf、sscanf、nsscanf、fscanf等函數,這些函數與C語言中的相應函數的功能用法基本相同。這里僅舉例說明printff和printf的用法:

!using["io"];    //使用命名空間io
printff{"Hello Forcal!大家好!\r\n"};   
//輸出字符串
printf {"Hello Forcal!大家好!\r\n"};   
//輸出字符串
printff{"整數i={1,i,-6},實數r={2,r,15.6}\r\n",123,123.456789};   
//輸出數據
printf {"整數i=%-6d,實數r=%15.6e\r\n",        123,123.456789};   
//輸出數據

8 工程實例 [返回頁首]

    這些例子用到了5個庫:核心庫forcal32w.dll、Forcal數據類型擴展庫FcData32W.dll、徐士良算法庫XSLSF32W.dll、IMSL庫FcIMSL32W.dll和優化庫FcOpt32W.dll。核心庫是必然要使用的,后面三個是Forcal擴展動態庫。這些庫可以單獨工作,也可以協同工作,共同完成計算任務。注意徐士良算法庫XSLSF32W.dll中提供了命名空間“XSLSF”,IMSL庫FcIMSL32W.dll中提供了命名空間“IMSL”,優化庫FcOpt32W.dll中提供了命名空間“fcopt”,用using函數啟用命名空間可簡化函數的輸入。

8.1 簡單例子

    (1)求函數f(x)=ln(x)/sqrt(x)在0~1上的積分值,注意這個函數當x=0時有奇點。準確值為-4。

f(x)=ln(x)/sqrt(x);   //一元函數定義
main(:val,err)=
{
    val=IMSL::QDAGS[HFor("f"),0,1,0,0.001,&err],
//得到積分值及絕對誤差
    printff{"val={1,r}, err={2,r}\r\n",val,err} 
//用函數printff輸出計算結果
};

    結果:

val=-4.0000000000000853, err=1.354472090042691e-013

    (2)計算函數f(x,y)=y*cos(x+y*y)的近似積分值。外層區間取[0,1],內層區間取[-2*x,5*x]。如下圖:

f(x,y)=y*cos(x+y*y);  //被積函數
g(x)=-2*x;           
//積分下限
h(x)=5*x;            
//積分上限
IMSL::TWODQ[HFor("f"),0,1,HFor("g"),HFor("h"),0.001,0,6,0]; 
//計算積分值

    結果:-8.288981913740839e-002

    (3)計算下圖中的三重積分

    方法1:用計算多重積分的高斯方法

f(x,y,z)=x*y*z;                      //被積函數
yx(j,x1,x2,x3,y0,y1)=
{
    which{
        j==0 : [ y0=0, y1=1 ],      
//返回第一層積分下限及上限
        j==1 : [ y0=0, y1=1-x1 ],   
//返回第二層積分下限及上限
        j==2 : [ y0=0, y1=1-x1-x2 ] 
//返回第三層積分下限及上限
    }
};
XSLSF::igaus[HFor("f"),HFor("yx"),5,5,5];
//返回積分值

    結果:1.388888889097291e-003

    方法2:用單重積分和二重積分構造三重積分:第一層用單重積分函數QDAGS,第二、三層用二重積分函數TWODQ

fyz(y,z::x)=x*y*z; //x是模塊變量
gyz(y)=0;         
//積分下限
hyz(y::x)=1-x-y;  
//積分上限
//以下函數f中,x=_x的意思是將自變量_x傳給模塊變量x,模塊變量x在其他函數中將用到
f(_x::x)= x=_x, IMSL::TWODQ[HFor("fyz"),0,1-x,HFor("gyz"),HFor("hyz"),0,0.001,2,0];
IMSL::QDAGS[HFor("f"),0,1,0,0.001,0];
//得到積分值

    結果:1.388888888888889e-003

    方法3:用單重積分和二重積分構造三重積分:第一、二層用二重積分函數TWODQ,第三層用單重積分函數QDAGS

fz(z::x,y)=x*y*z;
f(_x,_y::x,y)= x=_x,y=_y, IMSL::QDAGS[HFor("fz"),0,1-x-y,0,0.001,0];
g(x)=0;      
//積分下限
h(x)=1-x;
    //積分上限
IMSL::TWODQ[HFor("f"),0,1,HFor("g"),HFor("h"),0,0.001,2,0];

    結果:1.388888888888889e-003

    (4)求解含積分的方程

!using["fcopt","IMSL"];       //使用命名空間fcopt和IMSL
pp(x::p)=exp{-[(x/p)^2]};     //函數定義,用于積分
f(_p,q,y1,y2::p)=            
//函數定義,用于解方程
{
    p=_p,                    
//傳遞模塊變量
    y1=
q*QDAGS[HFor("pp"),0,p-q,0,1e-6,0]-1.99,
    y2=
q*QDAGS[HFor("pp"),0,p+q,0,1e-6,0]-2.87
};
solve[HFor("f")];             //解方程

    結果(每行前面的數是解,最后一個數是誤差,有2組解):

3.20186397420115   1.074732389098163   0.
-3.20186397420115  -1.074732389098163  0.
2.

8.2 復雜例子

    以下工程實例來自網上,例子有所修改,僅僅說明Forcal是如何解決這類復雜問題的,這些代碼體現了Forcal代碼的可讀性及可寫性。

    這些例子的運算量較大,可能將運行幾秒到十幾秒甚至幾分鐘的時間。

    (1)公式如圖所示:

 

    已知k0=2*pi/0.6328e-6,N=1.543674,求β值?

    算法分析:有3個公式,看起來似乎有些復雜,但只有一個變量,解單變量方程即可。注意最后一個式子實際上是一個常數;第二個式子中包含了一個積分項,而第二個式子包含在第一個式子等號前的積分式中,故需要做兩次積分;第一個式子等號后比較簡單,其中用到了最后一個式子。 該方程用普通解方程方法求解比較困難,難點在于解方程時的初值或初始區間的選擇,分析從略,選擇在區間k0~k0*ns(ns的意義見代碼中的數據)中對分法搜索可解此方程;使用優化庫FcOpt中的isolve函數,無需費力尋找初值,可方便地解此方程,但耗時較長。

    Forcal代碼:

//這里所有表達式屬于同一個模塊
//本例子使用模塊變量傳遞參數,模塊變量在本模塊內有相同的地址,因而可在同一個模塊的表達式之間傳遞參數
//在這里不推薦使用全局變量,全局變量可在所有模塊中的表達式之間傳遞參數,如果使用全局變量,可能影響到其他模塊中的表達式

//步驟0:使用命名空間math、XSLSF、IMSL和fcopt
!using["math","XSLSF","IMSL","fcopt"];

//步驟1:定義一些常量
//這個表達式用模塊變量傳遞一些常量,這些常量用在下面的表達式中,注意
dnx就是最后一個式子
init(::ns,k0,D,dN,N,dnx)= ns=1.520167, D=2e-6, dN=0.0235072, k0=2*pi/0.6328e-6, N=1.543674, dnx=-2*dN/pi*{1/1e-6*exp(-0.25)+sqrt(pi)/D*ERF(0.5)};

//步驟2:定義一個積分函數
//定義函數g,使用了模塊變量XX,D,該函數在調用前,XX,D必須預先賦值。
//這個函數就是第二個公式中的積分函數定義,該積分式對
ξ進行積分,所用到的x用模塊變量XX傳遞過來
g(ξ::XX,D)=exp(-(ξ^2))/(ξ^2+(XX/D)^2);

//步驟3:定義一個積分函數
//以下函數f使用了模塊變量ns,k0,D,dN,hg,XX,β,該表達式在調用前,這些變量必須預先賦值
//這個函數就是第一個公式中的等號前的積分式部分,在計算n(x)中的積分式前,將x通過模塊變量XX傳遞過去,因為QDAGS函數將調用函數g,函數g要求XX預先賦值
f(x::ns,k0,D,dN,XX,β)= XX=x, sqrt{k0*k0*{ns+dN*x/(pi*D)*exp[-((x/D)^2)]*QDAGS[HFor("g"),-0.5,0.5,0,1e-6,0]}^2-β^2};

//步驟4:定義解方程所用到的函數
//定義函數bt。有一個局部變量s,局部變量只在本函數中使用,函數退出時將銷毀。使用了模塊變量β,hf,k0,N,dnx
//這個函數就是第一個公式,不過是f(x)=0的形式,這是解方程函數所要求的
bt(t:s:β,hf,k0,N,dnx)={
    β=t,
//將自變量t傳給模塊變量β,因為QDAGS函數將調用函數f, 函數f要求β預先賦值
    s=sqrt[k0*k0*N*N-t*t],
   
QDAGS[HFor("f"),1e-10,1e-6,0,1e-6,0]-pi/4-atan{sqrt[t*t-k0*k0]/s+k0*k0*N*dnx/[2*s^3]}
};

//步驟5:定義主函數main,當然也可以是其他名字,省略函數名也是可以的
main(:a,i,j:k0,ns)=
{
    a=array[3].free(),   
//申請工作數組,長度設為3,自動銷毀該數組
   
i=dhrt[HFor("bt"),k0,k0*ns,0.01*1e6,1e-6,a], //在區間k0~k0*ns中用函數dhrt搜索所有解
    printff{"\r\n搜索到{1,i}個實根:",i},
    j=0,(j<i).while{    
//輸出并驗證所求的解
        printff{"\r\nβ={1,r}, bt({1,r})={2,r}",a(j),bt[a(j)]},
        j++
    }
};

    結果:

搜索到1個實根:
β=15093875.755650569, bt(15093875.755650569)=7.6380607616499674e-008

    用下面的代碼代替上面的步驟5,無需費力尋找初值,可方便地解此方程,但耗時較長。

//步驟5:用isolve函數解方程 ,耗時較長
isolve[HFor("bt"), optmax,5000];

    結果(每行前面的數是解,最后一個數是誤差,有2組解):

-15093875.76549701 5.995204332975845e-015
15093875.76549701  5.995204332975845e-015
2.

    (2)數學模型:

      dy/dt=k*y*z+0.095*b*z
      dz/dt=
-b*z-0.222*z

    實驗數據(ti; yi):

 ti        yi

0.01    2.329101563
0.68   
3.851292783
1.1    
4.50093936
1.63   
6.749172247
2.07   
9.112062872
2.67   
9.691657366
3.09   
11.16928013
3.64   
10.91451823
4.65   
16.44279204
5.1    
18.29615885
5.58   
21.63989025
6.11   
25.78611421
6.63   
26.34282633
7.06   
26.50581287
7.62   
27.63951823
8.66   
35.02757626
9.04   
35.5562035
9.63   
36.10393415

    已知z在t=0.01時的初值為5000,求k和b的最優值?

    算法分析:用優化庫FcOpt中的SimOpt函數(求n維極值的單形調優法 )求最優參數值kb。用IMSL庫的微分方程組 求解函數IVPRK求理論值,與實驗值做比較,理論值與實驗值差的平方和最小為最優。

    Forcal代碼:

!using["IMSL","fcopt","math"];       //使用命名空間

//函數定義,用于計算微分方程組中各方程右端函數值,微分方程組求解函數IVPRK將調用該函數f。
//t為自變量,y和z為函數值,dy和dz為右端函數值(即微分方程的值)。
//該函數被調用時將用到參數k和b,這2個參數正好是擬合參數,用模塊變量傳遞這2個參數。
f(t,y,z,dy,dz::k,b)=
{
  dy=k*y*z+0.095*b*z,
  dz=-b*z-0.222*z
};

//目標函數定義,自變量_k,_b為需要優化的參數,需要將這些參數傳遞給對應的模塊變量k,b。
//模塊變量:數組tyArray存放實驗數據ti和yi;max為數組tyArray的長度;k,b為優化變量。
優化(_k,_b:i,s,IDO,y,z,yy,t,tt:tyArray,max,k,b)=
{
  k=_k,b=_b,   
      //傳遞優化變量,函數f中要用到
k,b
  i=0, s=0, IDO=1, t=tyArray[0,0], y=tyArray[0,1], z=5000,
  (++i<max).while{
    tt=tyArray[i,0], yy=tyArray[i,1],
    IVPRK[&IDO,HFor("f"),&t,tt,1e-6,0,&y,&z],
    s=s+[y-yy]^2
  },
  IVPRK[3,HFor("f"),&t,tt+0.1,1e-6,0,&y,&z],
  s
};

驗證(_k,_b:i,IDO,y,z,yy,t,tt:tyArray,max,k,b)=
{
  k=_k,b=_b,
  printff{"\r\n               t            y實驗值            y模擬值         z模擬值\r\n"},
  i=0, IDO=1, t=tyArray[0,0], y=tyArray[0,1], z=5000,
  (++i<max).while{
    tt=tyArray[i,0], yy=tyArray[i,1],
    IVPRK[&IDO,HFor("f"),&t,tt,1e-6,0,&y,&z],
    printff{"{1,r,18.8}{2,r,18.8}{3,r,18.8}{4,r,18.8}\r\n",tt,yy,y,z},
    i++
  },
  IVPRK[3,HFor("f"),&t,tt+0.1,1e-6,0,&y,&z]
};

main(:i,k,b,min:tyArray,max)=
{
  max=18,
                    //實驗數據組數
  tyArray=arrayinitns{max,2,
//存放實驗數據ti,yi

"0.01    2.329101563
0.68   
3.851292783
1.1    
4.50093936
1.63   
6.749172247
2.07   
9.112062872
2.67   
9.691657366
3.09   
11.16928013
3.64   
10.91451823
4.65   
16.44279204
5.1    
18.29615885
5.58   
21.63989025
6.11   
25.78611421
6.63   
26.34282633
7.06   
26.50581287
7.62   
27.63951823
8.66   
35.02757626
9.04   
35.5562035
9.63   
36.10393415"

    }.free(),

  i=
SimOpt[HFor("優化"), optstarter : &k,&b,&min], //求n維極值的單形調優法
  printff{"\r\n實際迭代次數={1,i}, k={2,r}, b={3,r}, 目標函數終值={4,r}\r\n",i,k,b,min},
  驗證(k,b)
    //驗證計算結果
};

    結果:

實際迭代次數=66, k=1.4083346522762968e-004, b=-1.4674037435955751e-004, 目標函數終值=24.291248590365097

t     y實驗值    y模擬值   z模擬值
0.68 3.8512928 3.556177  4309.3894
1.63 6.7491722 5.9132372 3490.4673
2.67 9.6916574 9.2774269 2771.277
3.64 10.914518 13.002167 2234.708
5.1  18.296159 19.204465 1616.4054
6.11 25.786114 23.574479 1291.9246
7.06 26.505813 27.533614 1046.4175
8.66 35.027576 33.557006 733.74569
9.63 36.103934 36.714641 591.67934

9 Forcal程序 [返回頁首]

    一個Forcal程序由多個模塊組成,模塊之間通過全局函數、全局變量,或者通過模塊命名空間的函數進行通信,這些模塊可來自多個文件,也可在同一個文件之中。Forcal的多文件、多模塊化編譯功能可用于完成大的項目。

10 Forcal擴展動態庫 [返回頁首]

    Forcal軟件的構成是高度組合式的。Forcal32W.dll是核心庫,在此基礎上可設計種類繁多的Forcal擴展動態庫,Forcal程序由Forcal32W.dll及零個或任意多個Forcal擴展動態庫提供支持。根據完成的功能不同,一個Forcal程序的規模(包括所有支持模塊)可以很小(例如只有幾百K),也可以非常大。

    通過Forcal擴展動態庫,可向Forcal注冊種類繁多的二級函數,這些函數無縫地集成在Forcal系統之中,協同工作,共同完成復雜的任務。每個Forcal擴展動態庫可 (使用C/C++、delphi、Fortran等語言)單獨設計,然后根據需要進行加載。 這似乎也是com技術所要達到的目的,但com技術太復雜,函數調用效率似乎也不是太高。dll技術比com技術要簡單高效的多,Forcal借助于dll,出色地完成了這個任務。

11 指針和對象 [返回頁首]

    對象是一種復雜的實體,用復雜的數據結構進行描述。對象通過指針進行標識。Forcal數據類型擴展動態庫FcData就是專門來管理指針和對象的。實際上,任何一個Forcal擴展動態庫中都可以生成和管理自己的對象。

12 Forcal中的多線程 [返回頁首]

    核心庫Forcal32W.dll中輸出的函數大多數只能在單線程中使用。Forcal模塊化編譯運行庫MForcal使多線程程序中使用Forcal成為可能。

13 中文編程 [返回頁首]

    中文編程是許多人的夢想,Forcal使用Unicode字符串作為默認字符串,可很好地實現中文編程。Forcal標識符可全部使用中文,盡管本軟件包中的Forcal擴展動態庫提供的都是英文函數,但你自己來設計這些庫時,完全可以全部使用中文函數,或者提供中英文兩套函數名。

    例子:

兩數加(加數1,加數2)=加數1+加數2;
兩數加(2,3);

    中文編程或許使普通人學會編程成為可能。


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

福利彩票22选5走势图