Sway 參考手冊/函式
回想一下,我們評估了一系列表示式來找到直線上一點的y值
y = 5x - 3
給定一個x值
sway> var m = 5; INTEGER: 5 sway> var x = 9; INTEGER: 9 sway> var b = -3; INTEGER: -3 sway> var y = m * x + b; INTEGER: 42 sway> y; INTEGER: 42
現在,假設我們希望找到對應於不同x值的y值,或者更糟糕的是,對於不同直線上的不同x值。我們所做的所有工作都必須重複。函式是一種封裝所有這些操作的方法,以便我們可以用最小的努力重複它們。
首先,我們將定義一個不太有用的函式,它計算給定斜率為 5、y 軸截距為 -3 和x值為 9(與上面完全一樣)的y值。我們透過將函式包裝在上面的操作序列周圍來做到這一點。函式的返回值是最後一個被評估的值。
function y()
{
var m = 5;
var x = 9;
var b = -3;
m * x + b; //this quantity is returned
}
需要注意一些事項。關鍵字 function 表示正在發生函式定義。這個特定函式的名稱是y。花括號之間的部分是呼叫函式時將被評估(或執行)的程式碼。此程式碼在之前不會被評估。
您可以將此函式複製並貼上到 Sway 直譯器中。如果你這樣做,你會看到類似的東西
sway> function y()
more> {
more> var m = 5;
more> var x = 9;
more> var b = -3;
more> m * x + b; //this quantity is returned
more> }
FUNCTION: <function y()>
注意,當輸入不完整時,直譯器提示會更改為more>當輸入花括號閉合時,就會發生這種情況[1]。
一旦定義了函式,我們就可以重複找到y的值
sway> y(); INTEGER: 42 sway> y(); INTEGER: 42
y後面的圓括號表示我們希望呼叫y函式並獲取它的值。
y函式按原樣寫,並不是太有用,因為我們不能用它來計算類似的東西,比如不同x值的y值。但在我們改進函式之前,讓我們修改它,使其顯示當前環境[2]。這可能有助於你理解在函式呼叫中發生的事情。當函式體正在執行時
function y()
{
var m = 5;
var x = 9;
var b = -3;
pp(this);
m * x + b; //this quantity is returned }
當我們呼叫y的新版本時,我們看到了它的當前環境,它具有b、x和m的繫結。
sway> y(); <OBJECT 2566>: context: <OBJECT 749> dynamicContext: <OBJECT 749> callDepth: 1 constructor: <function y()> this: <OBJECT 2566> b: -3 x: 9 m: 5 INTEGER: 42
變數b、x和m被稱為區域性變數,因為它們在函式體之外是不可見的。
一個好的函式的標誌是它可以讓你計算不止一件事情。我們可以修改我們的函式以接收我們感興趣的x值。透過這種方式,我們可以計算多個y值。我們透過傳遞一個引數來做到這一點,在本例中,是x的值。
function y(x)
{
var slope = 5;
var intercept = -3;
return slope * x + intercept;
}
我們透過在函式定義括號之間放置變數名稱來為傳入的值命名。在本例中,我們選擇x作為名稱。注意,由於我們傳遞了x,所以我們不再需要(也不想)定義x,因此我們將其刪除。現在,我們可以計算無限多個x'的y值
sway> y(9); INTEGER: 42 sway> y(0); INTEGER: -3 sway> y(-2); INTEGER: -13
如果我們希望計算給定直線上不同x的y值怎麼辦?一種方法是將斜率和截距以及x一起傳遞進來
function y(x,slope,intercept)
{
return slope * x + intercept;
}
sway> y(9,5,-3);
INTEGER: 42
sway> y(0,5,-3);
INTEGER: -3
如果我們希望使用不同的直線進行計算,我們只需將新的斜率和截距與我們的x值一起傳遞進來。這當然按預期工作,但不是最好的方法。一個問題是我們必須不斷輸入斜率和截距,即使我們正在計算同一條直線上的y值。無論何時你發現自己在反覆做著同樣乏味的事情,請放心,有人已經想出了避免這種乏味的方法。因此,假設這是真的,我們如何自定義我們的函式,以便我們只需要為每條特定的直線輸入一次斜率和截距?我們將探討三種不同的方法。在進一步閱讀時,如果你理解所有正在發生的事情並不重要。重要的是,你應該知道其他方法的存在,並瞭解每種方法的優缺點
由於建立函式是一項艱苦的工作(需要大量輸入),而計算機科學家像躲避瘟疫一樣躲避艱苦的工作,所以早些時候有人想出了編寫一個自身建立函式的函式!太棒了!我們可以為我們的線問題做到這一點。我們將告訴我們的創意函式為特定的斜率和截距建立一個y函式!同時,讓我們將變數名m和b分別更改為slope和intercept
function makeLine(slope,intercept)
{
function y(x)
{
slope * x + intercept;
}
y;
}
makeLine函式建立了一個區域性y函式,然後返回它。下一個版本是等效的
function makeLine(slope,intercept)
{
function y(x)
{
slope * x + intercept;
}
}
由於makeLine做的最後一件事是定義y函式,所以y函式被呼叫makeLine返回。
因此,我們的創意函式只是定義了一個y函式,然後返回它。現在,我們可以建立很多不同的直線
sway> var a = makeLine(5,-3); FUNCTION: <function y(x)> sway> var b = makeLine(6,2); FUNCTION: <function y(x)> sway> a(9); INTEGER: 42 sway> b(9); INTEGER: 56
注意,直線a和b如何記住建立它們時提供的斜率和截距[3]。雖然這絕對很酷,但問題是許多語言(包括 C 和 Java)不允許你定義建立其他函式的函式。幸運的是,Sway 允許這樣做。
解決我們線問題的另一種方法是使用稱為物件的東西。在 Sway 中,物件只是一個環境,我們之前已經見過它們。所以這裡沒有什麼新鮮事,除了如何使用物件來實現我們的目標。在這裡,我們定義了一個函式來建立並返回一個線物件。建立一個並返回物件的函式被稱為建構函式。
function line(slope,intercept)
{
this;
}
this變數始終指向當前環境,在本例中,它包含形式引數slope和intercept的繫結。透過返回this,我們返回line的環境,我們可以隨時檢視slope和intercept的值。為了證明 slope 和 intercept 存在,我們可以使用內建的漂亮列印函式pp
sway> m = line(5,-3); OBJECT: <OBJECT 231> sway> pp(m); <OBJECT 231>: context : <object 145> dynamicContext: <object 145> constructor: <function line(slope,intercept)> this: <object 231> intercept: -3 slope : 5 OBJECT: <OBJECT 231>
我們使用'.'(點)運算子訪問物件中的變數
sway> m . slope; INTEGER: -3 sway> m . constructor; FUNCTION: <function line(slope,intercept)>
現在,我們修改y函式以接收一個線物件以及x,並使用點運算子提取線的斜率和截距
function y(line,x)
{
line . slope * x + line . intercept;
}
在這種情況下,我們建立不同的線,然後將每條線傳遞給我們的新y函式
sway> var m = line(5,-3); OBJECT: <object 231> sway> var n = line(6,2); OBJECT: <object 256> sway> y(m,9); INTEGER: 42 sway> y(n,9); INTEGER: 56
這種方法的問題是我們將線物件與查詢y值分開,但這兩個概念密切相關。例如,假設我們有拋物線物件和線物件。我們的y函式對於拋物線物件將無法正常工作,即使拋物線上 (x,y) 點的概念與直線上的點一樣有效[4]。
在面向物件的世界中,我們透過將物件和專門針對該物件工作的函式捆綁在一起來解決這個問題。在我們的例子中,我們使y函式成為線物件的一部分
function line(slope,intercept)
{
function y(x)
{
slope * x + intercept;
}
this;
}
這與函式動態建立方法非常相似,但我們返回this,而不是繫結到y的函式。現在,我們透過線物件呼叫y函式。
sway> var m = line(5,-3); OBJECT: <object 231> sway> var n = line(6,2); OBJECT: <object 256> sway> m . y(9); INTEGER: 42 sway> n . y(9); INTEGER: 56
如果我們有一個拋物線物件,它將有自己的y函式,具有不同的實現。但是,我們以同樣的方式呼叫它
sway> var p = parabola(2,0,0); OBJECT: <object 453>
sway> p . y(7); INTEGER: 49
這種方法在面嚮物件語言(如 Java)中得到支援。早期的方法(其中函式與物件分離)在過程式語言(如 C)中得到支援。
所有運算子都是函式,可以使用運算子語法呼叫。例如,以下表達式都對a和b的值求和
var sum = a + b; var sum = +(a,b);
相反,任何兩個引數的函式都可以使用運算子語法呼叫。有時使用運算子語法可以使程式碼更清晰。讓我們建立一個函式,將變數增加給定數量,類似於 C、C++ 和 Java 中同名的運算子。
function +=($v,amount)
{
$v = force($v) + amount;
}
不要擔心程式碼是如何工作的;只需注意+=函式有兩個形式引數($v 和 amount),因此接受兩個引數。我們可以呼叫+=使用函式呼叫語法增加變數
var x = 2; +=(x,1); inspect(x);
或者我們可以使用運算子語法
var x = 2; x += 1; inspect(x);
在這兩種情況下,程式碼片段的輸出都是相同的。
x is 3
使用運算子語法呼叫的函式與數學運算子具有相同的優先順序,並且是左結合的。
- ↑ 在直譯器中輸入一個長結構很繁瑣。在後面的章節中,我們將學習如何將程式碼儲存在檔案中,並讓直譯器執行該檔案中的程式碼。如果需要更改程式碼,我們只需編輯檔案。這樣,我們就不需要從頭開始將修改後的程式碼輸入直譯器。
- ↑ Sway 允許你重新定義變數和函式。
- ↑ 區域性函式 y 的上下文是 makeLine 函式的區域性環境。此環境包含 slope 和 intercept 的繫結。
- ↑ 這是一個嘗試泛化的具體例子,以便我們的函式適用於所有概念有效的物件。