程式設計科學/最簡單的事
如果你學習過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更改為x,dw更改為dx,h更改為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>
我們看到,在其他欄位中,a和n的值是正確的。稍後,你將瞭解預定義欄位的含義和用途:context、dynamicContext和constructor。你已經知道this欄位了。
我們希望訪問a和n,因為冪法則需要它們來計算導數多項式。現在我們可以寫出一個表示式,讓我們計算導數
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會顯示出a和n的正確值
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函式內部不再使用變數coeff和exp,而是直接使用a和n。
最後,我們在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]從現在開始,我們將開發一個系統來尋找各種數學物件的導數(最終還有積分),這些物件的型別只受我們的想象力和注意力範圍的限制。我們系統中的每個物件都將執行(至少)三項操作。首先是在給定位置計算其值。第二是計算其導數(最終還有其積分)。第三是計算其視覺化。因此,我們所有的數學物件都將具有值、diff和toString內部函式或方法。 [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]- ↑ 通常,形式引數的名稱並不重要,因此我們可以保留ratio函式不變,它仍然可以完全正常工作。
- ↑ 在面向物件程式設計(另一種說法是使用物件程式設計)的世界中,這些內部函式被稱為方法。從現在開始,我們將使用術語方法。請記住,方法只是一個內部函式。