跳轉到內容

程式設計科學/最簡單的事

來自華夏公益教科書

如果你學習過CME的第4章,你就會學習到對簡單多項式函式(稱為)進行微分的冪法則。一項具有以下形式

   

其中a被稱為項的係數,n被稱為項的指數

以下是項的冪法則

   

例如,考慮多項式

   

如果冪法則正確,則導數應該為

   

讓我們測試一下。我們首先定義一個函式來表示多項式

   function y(x)
       {
       2 * (x ^ 3);
       }

和以前一樣,因變數成為函式名,自變數成為形式引數。


在我們繼續之前,現在是時候學習關於使用Sway與檔案,因為在直譯器中輸入函式定義可能相當痛苦。

因此,在本冊書的其餘部分,即使顯示了與直譯器的互動,你也應該將Sway程式儲存在檔案中,並從檔案中執行Sway程式。


導數,或可以用這個函式來表示

   function dy/dx(x)
       {
       6 * (x ^ 2);
       }

有沒有辦法測試冪法則是否正確?回想一下我們上一章中修改過的比率函式

   function ratio(x,dx,y)
       {
       var dy = y(x + dx) - y(x);
       dy / dx;
       }

如你所知,它計算的比率是給定x的導數的近似值。請注意,我們已將形式引數的名稱從w更改為xdw更改為dxh更改為y,以反映我們現在正在處理多項式的事實。[1]

我們認為ratio(對於較小的dx)會產生一個數字,該數字應該接近函式dy/dx產生的數字。

讓我們定義一個測試函式

   function test(x)
       {
       var dx = 0.000001;
       
       println("for x = ",x);
       inspect(ratio(x,dx,y));
       inspect(dy/dx(x));
       println();
       }

給定x的值,測試函式將打印出兩個結果。現在來說一些命名法。當我們使用ratio函式時,我們是在數值地求導數。也就是說,我們使用dx的一個小數字,找到相應的dy,然後計算比率。另一方面,使用冪法則(如函式dy/dx所體現的),我們符號地求導數,因為我們從未真正計算過實際的比率。

    sway> test(10);
    for x = 10
    ratio(x,dx,y) is 600.00005965
    dy/dx(x) is 600
      
    sway> test(20);
    for x = 20
    ratio(x,dx,y) is 2400.0001231
    dy/dx(x) is 2400
      
    sway> test(30);
    for x = 30
    ratio(x,dx,y) is 5400.0001837
    dy/dx(x) is 5400

對於這個測試,我們看到我們的數值解和符號解之間有很好的一致性。

顯然,符號解是首選,因為

  • 它是精確的
  • 它更容易計算

你可能會問自己,我們能編寫一個程式來符號地求導數嗎?

我們方法的缺陷

[edit | edit source]

如果我們能使用符號方法來求導數,而不是使用基於比率的數值方法,那就太好了。問題是,我們用來表示一項的函式

   function y(x)
       {
       2 * (x ^ 3);
       }

的係數為2,指數為3,是硬編碼的。如果我們得到了這樣的函式,但不知道內部細節(例如硬編碼指數的值),我們就無法建立導數函式,因為我們需要知道指數的值才能做到這一點,並且沒有辦法獲取它。我們需要想辦法從函式中提取指數,以便我們可以使用它來構建導數函式。

下一節將向你展示一種從函式中提取元件的方法。該方法使用物件


如果你不知道什麼是物件,或者如何在Sway中建立物件,請閱讀使用物件的相關內容,這些內容位於Sway 參考手冊中。


好的,現在你已經熟悉了物件,我們可以開始使用物件編寫一個程式來符號地求導數。

多項式物件和冪法則

[edit | edit source]

我們不使用函式來表示多項式y,而是使用物件。透過學習參考手冊,你應該知道物件只是一個環境,環境只是一個變數及其值的表格。你應該還知道,你使用函式來建立物件,而建立物件的函式通常稱為建構函式。最後,你應該知道,為了定義一個建構函式,你需要讓該函式返回預定義變數this

以下是一個簡單多項式物件的建構函式。我們將建構函式命名為term

   function term(a,n)
      {
      function value(x)
          {
          a * (x ^ n);
          }
       this;
       }

請注意,建構函式包含一個內部函式,用於在給定x的情況下計算y的值,就像之前一樣。我們使用運算子來呼叫該函式。

   sway> var y = term(2,3);
   OBJECT: <OBJECT 1671>
   sway> y . value(4);
   INTEGER: 128
   sway> y . value(5);
   INTEGER: 250

實際上,確實是128,而確實是250。

現在是重點!將多項式項儲存為物件而不是函式,使我們能夠提取係數a和指數n

   sway> y . a;
   INTEGER: 2;
   sway> y . n;
   INTEGER: 3;

我們甚至可以漂亮地列印物件y來檢視其所有欄位

   sway> pp(y);
   <OBJECT 2561>:
       context: <OBJECT 749>
       dynamicContext: <OBJECT 749>
       callDepth: 1
       constructor: <function term(a,n)>
       this: <OBJECT 2561>
       value: <function value(x)>
       a: 2
       n: 3
   OBJECT: <OBJECT 2561>

我們看到,在其他欄位中,an的值是正確的。稍後,你將瞭解預定義欄位的含義和用途:contextdynamicContextconstructor。你已經知道this欄位了。

我們希望訪問an,因為冪法則需要它們來計算導數多項式。現在我們可以寫出一個表示式,讓我們計算導數

   sway> var coeff = y . a;
   sway> var exp = y . n;
   sway> var z = term(coeff * exp,exp - 1);
   OBJECT: <OBJECT 2783>
   sway> z . a;
   INTEGER: 6
   sway> z . n;
   INTEGER: 2
   sway> z . value(4)
   INTEGER: 96

當然,我們可以編寫一個函式來完成這個任務

   function powerRule(t)
       {
       var coeff = t . a;
       var exp = t . n;
       term(coeff * exp,exp - 1);
       }

請注意,powerRule函式以一個term物件作為引數,並返回一個表示導數的新term物件。這是因為新項的係數為a * n指數為n - 1正如冪法則所規定的一樣。讓我們檢查一下,確保我們的冪法則按預期工作

   sway> var y = term(2,3);
   sway> var z = powerRule(y);
   
   sway> y . value(4);
   INTEGER: 128
   sway> z . value(4);
   INTEGER: 96

確實如此!

此外,漂亮地列印z會顯示出an的正確值

   sway> pp(dy/dx);
   <OBJECT 2922>:
       context: <OBJECT 749>
       dynamicContext: <OBJECT 2808>
       callDepth: 2
       constructor: <function term(a,n)>
       this: <OBJECT 2922>
       value: <function value(x)>
       a: 6
       n: 2
   OBJECT: <OBJECT 2922>

你可能沒有注意到,我們有點缺乏對稱性。雖然項物件有一個用於計算其值的內部函式,但我們使用外部函式powerRule來計算其導數。當我們使用物件程式設計時,我們儘可能使用內部函式。因此,讓我們重寫term函式,以便它可以計算自己的導數

   function term(a,n)
       {
       function value(x)
           {
           a * (x ^ n);
           }
       function diff()
           {
           term(a * n,n - 1);
           }
       this;
       }

我們將這個新的內部函式命名為diff,以表示取我們物件的微分。請注意,我們在diff函式內部不再使用變數coeffexp,而是直接使用an

最後,我們在term中添加了第三個內部函式。它用於以比使用pp函式更簡潔的方式視覺化我們的物件。按照慣例,我們將這個函式稱為toString

   function term(a,n)
       {
       function value(x)
           {
           a * (x ^ n);
           }
       function diff()
           {
           term(a * n,n - 1);
           }
       function toString()
           {
           string(a) + "x^" + string(n);
           }
       this;
       }

toString函式將係數和指數都轉換為字串,然後將這些字串連線在一起,形成一個項的字串表示形式

   sway> y = term(2,3);
   sway> z = y . diff();
   sway> y . toString()
   STRING: "2x^3"
   sway> x . toString()
   STRING: "6x^2"

請注意,使用toString函式來提取項的係數和指數有多麼容易。另一種表達toString函式的方法是利用這樣一個事實:如果你新增一個字串和一個數字(字串位於加號的左側,數字會自動轉換為字串),再加上Sway從左到右組合數學運算子的事實,我們可以刪除n的字串轉換

   function toString()
       {
       string(a) + "x^" + n;
       }

如果我們使用空字串來開始表示式,我們也可以刪除第一個字串轉換

   function toString()
       {
       "" + a + "x^" + n;
       }

一個完整的物件系統

[edit | edit source]

從現在開始,我們將開發一個系統來尋找各種數學物件的導數(最終還有積分),這些物件的型別只受我們的想象力和注意力範圍的限制。我們系統中的每個物件都將執行(至少)三項操作。首先是在給定位置計算其值。第二是計算其導數(最終還有其積分)。第三是計算其視覺化。因此,我們所有的數學物件都將具有difftoString內部函式或方法[2]

即使是最簡單的現實世界數學物件,例如數字和變數,也需要重新建立為 Sway 物件。這樣,我們就可以隨時獲取物件的導數,而不必先詢問獲取導數是否有意義。

問題

[edit | edit source]

所有公式均使用小學優先順序編寫。

1. 為什麼 Sway 實現的 有括號2 * (x ^ 3)?

2. 使用powerRule函式,可以對導數求導嗎?解釋一下。

3. 編寫一個函式,使用 power rule 函式回答 Thompson 第 58 頁上的以下問題:1、2、4、6、7。將你的函式命名為p58。你的函式應該計算然後列印問題的答案。

4. 使用紙筆完成 Thompson 第 58 頁上的練習 3、8、9 和 10。

腳註

[edit | edit source]
  1. 通常,形式引數的名稱並不重要,因此我們可以保留ratio函式不變,它仍然可以完全正常工作。
  2. 在面向物件程式設計(另一種說法是使用物件程式設計)的世界中,這些內部函式被稱為方法。從現在開始,我們將使用術語方法。請記住,方法只是一個內部函式。


一些這樣,一些那樣 · 持續的擔憂

華夏公益教科書