程式設計的科學/重複洗滌、沖洗、重複
正如 CME 第 7 章所述,假設
也就是說,y 是 x 的某個函式。如果是這樣,那麼
有時表示為
其中撇號表示微分。
由於 Sway 對命名變數持寬鬆的態度,我們可以使用相同的表示法。考慮定義一條直線
var f = line(3,-5); //equivalent to y = 3x - 5
其中 line 建構函式只是對 plus 建構函式呼叫的包裝器
function line(m,b)
{
plus(term(m,1),term(b,0));
}
然後,我們可以對我們的直線進行微分,如下所示
var f' = f . diff();
由於 f 表示一條直線,而直線的微分會產生直線的斜率,而直線的斜率始終是一個常數 m,因此我們預計評估 在不同點處始終會產生斜率
sway> f' . toString();
STRING: 3x^0 + 0x^-1
sway> f' . value(0);
EVALUATION ERROR: :mathError
stdin,line 9: exponentiation: cannot divide by zero
發生了什麼?問題是 f' 的第二項。我們最終使用 x = 0 來評估該項。什麼是x ^ -1當 x 為零時?它是 。這就是導致除零錯誤的原因。我們應該怎麼辦?每當一項的指數變為零時,我們應該在微分時強制指數保持為零。
這是一個新的 diff 版本,用於 term
function diff()
{
if (n == 0)
{
term(0,0);
}
else
{
term(a * n,n - 1);
}
}
它有效嗎?在重新制作 f 和 f' 後,我們有
sway> f' . toString();
STRING: 3x^0 + 0x^0
sway> f' . value(0);
REAL_NUMBER: 3.000000000
sway> f' . value(5); REAL_NUMBER: 3.000000000
我們看到它確實有效。
如果我們再次重複微分過程,我們會發現斜率在任何給定點的變化速度。對於一條直線,斜率從不改變,因此我們預計導數的導數(也稱為二階導數)始終為零。
var f'' = f' . diff(); sway> f'' . toString(); STRING: 0x^0 + 0x^0 sway> f'' . value(0); INTEGER: 0 sway> f'' . value(5); INTEGER: 0
已確認。
我們真的需要對視覺化做點什麼。它正在打印出我們實際上不需要看到的項。讓我們透過使 term' 的 toString 方法更復雜來簡化輸出。請實現以下規則,用於 term 的 toString 方法
- 如果係數為零,則 toString 應返回 "0"
- 如果指數為零,則 toString 應返回 "" + a
- 如果係數和指數都是 1,則 toString 應返回 "x"
- 如果指數為一,則 toString 應返回 "" + a + "x"
- 如果係數為一,則 toString 應返回 "x^" + n
- 否則,toString 應返回 "" + a + "x^" + n
較早的規則優先於較晚的規則。在實現這些規則並重新制作後f, f',以及f'',我們得到以下視覺化
sway> f . toString(); STRING: 3x + -5 sway> f' . toString(); STRING: 3 + 0 sway> f'' . toString(); STRING: 0 + 0
稍微好一點。現在我們需要改進 plus 以丟棄值為零的常數項。我們可以在 plus 建構函式的主體中做到這一點
- 如果第一個引數等效於零,則 plus 應返回第二個引數
- 如果第二個引數等效於零,則 plus 應返回第一個引數
- 否則,plus 應返回這個
你的邏輯應該像這樣
function plus(p,q)
{
...
if (p is equivalent to zero)
{
q;
}
else if (q is equivalent to zero)
{
p;
}
else
{
this;
}
}
我們如何確定 plus 的引數是否等效於零?由於我們將零表示為具有零係數的項,因此我們可以使用類似於此的邏輯
if (p is :term && p . coefficient == 0)
現在,我們使用項來表示零的技巧開始露出了它的醜陋面目。我們的程式碼對於可能在沒有閱讀本文的情況下閱讀我們的程式碼的人來說(出於維護目的)變得不明顯。零是一個常數,因為它永遠不會改變;零始終是零。另一方面,一項通常會改變,具體取決於自變數的值。這會導致認知失調,從而增加了理解我們程式碼的難度。
讓我們透過構建一個 'constant' 建構函式來解決這個問題。此建構函式將具有 term 的所有方法,但將更好地表示一個永不改變的值。這是一個嘗試
function constant(value)
{
function toString() { "" + value; }
function diff() { ... }
function y(x) { ... }
this;
}
diff 和 y 的實現留作練習,它們應該實現以下邏輯
- 常數的微分是一個常數零
- 常數的 y 值始終是常數的值,無論自變數的值如何
現在,我們可以在 plus 中像這樣測試零物件
if (p is :constant && p . value == 0)
看看這段程式碼多麼容易理解。我們現在更新 term 中的 diff 方法以利用我們新的 constant 建構函式。
function term(coefficient,exponent)
{
...
function diff()
{
if (n == 0)
{
constant(0);
}
else
{
term(a * n,n - 1);
}
}
...
this;
}
有了這些更改,並在重新制作後f, f',以及f''使用 plus、term 和 constant 的新版本,我們得到了以下視覺化
sway> f . toString(); STRING: 3x + -5 sway> f' . toString(); STRING: 3 sway> f'' . toString(); STRING: 0
好多了。
反覆對一條直線進行微分相當無趣。高階多項式[1] 更有趣。與其像這樣逐個構建一個包含許多項的高階多項式,
var a = term(1,0); var b = plus(term(2,1),a); var c = plus(term(3,2),b); var y = plus(term(4,3),c);
我們可以整體構建它
var y = plus(term(4,3),plus(term(3,2),plus(term(2,1),term(1,0))));
或者,由於這種整個比例構建可能難以閱讀,因此可以使用中綴運算子語法構建 y
var y = term(4,3) plus term(3,2) plus term(2,1) plus term(1,0);
無論你如何製作 y(這完全取決於你的喜好),我們都可以對其進行逐次微分
var y' = y . diff(); var y'' = y' . diff(); var y''' = y'' . diff(); sway> y . toString(); STRING: 4x^3 + 3^x2 + 2x + 1 sway> y' . toString(); STRING: 12x^2 + 6^x + 2 sway> y'' . toString(); STRING: 24x + 6 sway> y''' . toString(); STRING: 24
為了進行數值檢查,讓我們評估 y'' 在 x = 1 和 2 處的值。
sway> y'' . value(1); INTEGER: 30 sway> y'' . value(2); INTEGER: 54
物件 y''' 應該表示常數 24
sway> y''' . value(1); INTEGER: 24 sway> y''' . value(2); INTEGER: 24
一切看起來都很好。
1. 從數學上證明一項的簡化視覺化規則。
2. 為 minus 建構函式新增簡化。簡化類似於 plus,但稍微複雜一些。
3. 為 times 建構函式新增簡化。如果任一引數為零,則應返回常數零,如果其中一個引數為常數一,則返回另一個引數。否則,返回 this。
4. 為 div 建構函式新增簡化。如果分子(第一個引數)為常數零,則應返回常數零。如果分母(第二個引數)為常數一,則應返回分子。否則,應返回 this。
5. 使用gnuplot在sway中表示並繪製。什麼是? 將其繪製在x的函式圖上。它代表什麼?什麼是? 將其繪製在x的函式圖上。它代表什麼?什麼是? 將其繪製在x的函式圖上。
6. 使用sway或紙筆完成第82頁上的1-3題。
- ↑ 多項式的階數越高,構成該多項式的項中最大的指數就越大。