程式設計科學/鏈條幫的工作
有時候,正如 CME 第九章中的 SPT 指出,你會發現自己苦苦思索如何區分像這樣的複雜事物
解決方法是透過抽象細節來使表示式更簡單。令 *a* 為多項式
我們可以用項的總和來表示它
現在,*y* 可以改寫為
為了找到 *y* 對 *x* 的導數,我們使用 *鏈式法則*
換句話說,*y* 對 *x* 的微分等於(重寫後的)*y* 對(新的)*a* 的微分乘以(新的)*a* 對 *x* 的微分。
在程式設計中,我們有
var a = term(1,:x,2) plus term(17,:x,0); var y = term(3,:a,2);
var dy/dx = y . diff(:a) times a . diff(:x);
看看 *dy/dx* 代表什麼,我們有
sway> a . toString(); STRING: x^2 + 17 sway> y . toString(); STRING: 3x^2 sway> dy/dx . toString(); STRING: 6a * 2x
用手進行最終的代入(),我們得到了 *dx/dy* 的最終答案
dy/dx = 6 * (x^2 + 17) * 2x
= (6x^2 + 102) * 2x
= 12x^3 + 204x
如果能自動完成鏈式法則的代入步驟,那將是一件好事,但這樣做需要一些工作,無論是概念上還是程式設計上。我們首先擴充套件項變數抽象的概念。回想一下,最初,我們將項變數硬編碼為 *x*。接下來,我們允許 *term* 建構函式的呼叫者傳入獨立變數作為 Sway 符號。抽象的下一步是允許項變數本身成為一個項(或項的總和或其他任何東西)。如果我們這樣做,那麼項的 *diff* 方法將變成鏈式法則
function diff(wrtv)
{
term(a * n,iv,n - 1) times iv . diff(wrtv);
}
顯然,獨立變數 *iv* 不再是一個符號,而必須是一個具有 *diff* 方法的物件。因此,為了表示形式為
的項,我們需要使用物件來表示變數 *x*。這種變數物件的建構函式將類似於 term 和 plus 建構函式。也就是說,它必須具有 *value*、*toString* 和 *diff* 方法[1]
function variable(name)
{
function value(x) { x; }
function toString() { "" + name; }
function diff(wrtv)
{
if (wrtv == name)
{
constant(1);
}
else
{
constant(0);
}
}
this;
}
尋找簡單變數導數的規則是:如果與之相關的變數與獨立變數匹配,則結果為 1。如果不是,則結果為 0。我們將使用常量來表示數字 0 和 1;這樣,我們系統中的每一項,包括數字,都具有 *toString*、*value* 和 *diff* 方法。
為了簡化我們的生活,我們可以將以下邏輯新增到 *term* 建構函式的主體中。如果傳入一個符號作為獨立變數,我們將將其轉換為一個 *variable* 物件。[2] 這樣,我們就可以像以前一樣傳入符號。以下是一個新的 term 建構函式的模擬
function term(a,iv,n)
{
function value(x) { ... }
function toString() { ... }
function diff(wrtv)
{
if (n == 0)
{
constant(0);
}
else
{
term(a * n,iv,n - 1) times iv . diff(wrtv);
}
}
if (iv is :SYMBOL, iv = variable(iv));
this;
}
我們還需要修改 term 的 *toString* 方法來呼叫 *iv'* 的視覺化。以下是新的非簡化版本
function toString()
{
"" + a + iv . toString() + "^" + n;
}
讓我們測試一下修改後的系統
var t = term(4,:x,3); var t' = t . diff(:x);
sway> t . toString(); STRING: 4x^3 sway> t . iv; OBJECT: <OBJECT 1958> sway> t' . toString(); STRING: 12x^2
對於簡單變數來說,它似乎工作正常。現在讓我們試試我們原來的問題
首先,我們製作我們的多項式
var a = term(1,:x,2) plus term(17,:x,0); var y = term(3,a,2); // not :a
現在,我們視覺化它
sway> y . toString(); STRING: 31x^2 + 17x^0^2
糟糕!我們做錯了什麼?我們需要將 *iv* 的視覺化括起來
function toString()
{
"" + a + "(" + iv . toString() + ")" + "^" + n;
}
用 term 的新視覺化重新制作 *a* 和 *y* 會得到
var a = term(1,:x,2) plus term(17,:x,0); var y = term(3,a,2);
sway> y . toString(); STRING: 3(1x^2 + 17x^0)^2
如果你使用簡化的 *toString* 方法來表示項,你應該得到
3(x^2 + 17)^2
正是我們想要的!現在讓我們對 *y* 求導(你需要啟動你的 *times* 建構函式)
var y' = y . diff();
sway> y' . toString(); STRING: 6(x^2 + 17) * 2x
不錯!
我們還有兩個小問題。第一個是上面結果不是最簡單的形式。不幸的是,生成最簡單的形式是一個相當複雜的過程(因為不總是清楚哪種形式是最簡單的)。所以我們將在此止步,並感到滿意。
另一個小問題出現在我們視覺化 *a* 的時候
sway> a . toString(); STRING: (x)^2 + 17
我們對括號有點過了。顯然,當獨立變數是一個複雜物件時,我們想要使用括號。然而,當它是一個簡單變數時,我們應該避免使用括號。這項任務留作練習。
1. 解釋為什麼項的 *diff* 方法不再需要測試與之相關的變數是否與獨立變數匹配。
2. 實現 *one* 函式。
3. 修改項的簡化 *toString* 方法,使其僅在獨立變數是複雜的,且係數或指數不等於 1 時才打印括號。*提示*:建立一個 *term* 方法,如果 *iv'* 的視覺化是複雜的,則在 *iv'* 的視覺化周圍新增括號,如果它不是複雜的,則只需返回 *iv'* 的視覺化。在適當的地方從 *toString* 呼叫此方法。
4. CME 第 100 頁,1,8 使用 Sway
5. CME 第 100 頁,2,3,5,8 使用紙筆
- ↑ 這就是面向物件程式設計方法的核心:相關的物件具有相同的方法,但方法針對特定的物件進行了定製。
- ↑ 這個小技巧說明了計算機程式設計中的一個重要原則:儘可能為你的程式碼使用者做更多的事情。我們可以強制使用者傳入一個變數物件,或者我們可以允許使用者像以前一樣傳入一個符號,並自己完成工作。