跳轉到內容

Sway 參考手冊/賦值

來自華夏公益教科書,開放的書籍,開放的世界

一旦變數被宣告,就可以使用賦值運算子改變它的繫結。考慮以下與直譯器的互動

   sway> var BLACK = 1;        //initialization!
   INTEGER: 1
   
   sway> var BROWN = 2;
   INTEGER: 2
   
   sway> var GREEN = 3;
   INTEGER: 3
   
   sway> var eyeColor = BLACK;
   INTEGER: 1
   
   sway> eyeColor;
   INTEGER: 1
   
   sway> eyeColor = GREEN;     //assignment!
   INTEGER: 3
   
   sway> eyeColor == BROWN;    //equality?
   SYMBOL: :false
   
   sway> eyeColor == GREEN;
   SYMBOL: :true

運算子/變數 = (等於號)繫結到賦值函式。然而,賦值函式不是一個真正的函式,不像那些繫結到 + 和 * 的函式。回想一下,+ 之類的東西會在將它們組合起來之前評估兩邊的內容(回想一下,兩邊的內容在一般情況下被稱為運算元)。對於 =,左運算元不會被評估(如果被評估了,賦值

   eyeColor = GREEN
   

會嘗試將 1 的含義更改為 3)。一般來說,一個不評估所有引數的運算子被稱為特殊形式[1].

現在我們可以看到變數定義有兩個步驟。在第一步中,變數被建立,在第二步中,一個值被分配給該變數。

前面互動中給直譯器的最後兩個表示式指的是 == (相等)運算子。運算子 == 如果它的運算元指的是同一件事則返回 true,否則返回 false。在上面的互動中,變數 BLACK、GREEN 和 BROWN 不應該改變它們最初的值。我們透過使用(主要)大寫字母來命名變數來表示那些不應該改變值的變數(這個約定借鑑了早期的程式語言)。使用大寫字母強調了(不太)變數的常量性質。

在上面的與直譯器的互動中,我們使用整數 1、2 和 3 來表示黑色、棕色和綠色。透過抽象化 1、2 和 3,並賦予它們有意義的名稱(即 BLACK、BROWN 和 GREEN),我們發現很容易閱讀分配和測試眼睛顏色的程式碼。我們這樣做是因為很難記住哪個整數分配給哪個顏色。如果沒有變數 BLACK、BROWN 和 GREEN,我們必須在某處記下一些小筆記來提醒自己是什麼是什麼。以下是與直譯器互動的等效版本,不使用變數 BLACK、GREEN 和 BROWN。

   sway> var eyeColor = 1;
   INTEGER: 1
   
   sway> eyeColor;
   INTEGER: 1
   
   sway> eyeColor = 3;
   INTEGER: 3
   
   sway> eyeColor == 2;
   SYMBOL: :false
   
   sway> eyeColor == 3;
   SYMBOL: :true
   

在這個互動中,test eyeColor == 3 的意義並不那麼明顯。

另一種常量方法是使用符號。由於 Sway 有符號作為基元,我們可以完全拋棄類似常量的變數和整數

   sway> var eyeColor = :black;
   SYMBOL: :black
   
   sway> eyeColor;
   SYMBOL: :black
   
   sway> eyeColor = :green;
   SYMBOL: :green
   
   sway> eyeColor == :brown;
   SYMBOL: :false
   
   sway> eyeColor == :green;
   SYMBOL: :true
   sway> println("eye color is ",eyeColor);
   eye color is green
   SYMBOL: :green 
   

注意這個互動版本的可讀性提高了多少。還要注意,符號在列印時沒有冒號。

賦值的優先順序和結合性

[edit | edit source]

賦值在二元運算子中優先順序最低。它也是右結合的。右結合允許像這樣的語句

   a = b = c = d = 0;

它方便地將零同時分配給四個變數,並且等同於

   (a = (b = (c = (d = 0))));

因為運算子的右結合性質。由於賦值運算的結果值是賦值的值,所以這個語句按預期工作。

工作原理

[edit | edit source]

Sway 對賦值採用了新穎的方法。每次檢索一個值時,它的地址都會(通常)儲存在內部暫存器中。賦值運算子利用這一事實,透過

  1. 查詢左側的值
  2. 檢查內部暫存器中是否有有效地址
  3. 將右側的值寫入有效地址

例如,賦值

    x = 3;

透過查詢x的值來執行。x的當前值被忽略,但內部暫存器現在包含了x的位置。然後將 3 的值複製到地址,x就有了新的值。

透過這種方法,可以寫入陣列和列表元素以及物件

   a[y] = z;
   head(tail(items)) = :green;
   a . f() . b = "hello";

賦值有一些限制。例如,不能使用賦值運算子改變列表的尾部。例如,這種嘗試將會失敗

   tail(items) = list(a,b,c);

因為列表的尾部在 Sway 中沒有正常的地址。要改變列表的尾部,可以使用tail=運算子,而不是

   items tail= list(a,b,c);

或者

   tail=(items,list(a,b,c));

如果你喜歡函式呼叫語法。

有一個head=運算子用於改變列表的頭部,但它繫結到普通的賦值運算子,所以你可以使用兩者中的任何一個。

賦值給Thunk

[edit | edit source]

如果 thunk 中的程式碼有常規的 Sway 地址,可以使用賦值運算子來更新該地址的值。這就是forEach函式的工作方式

   function forEach($target,items,$body)
       {
       while (items != null)
           {
           $target = head(items);
           force($body);
           items = tail(items);
           }
       }

在這裡,thunk $target 被重複地用列表的(新)頭部更新。一旦使用賦值運算子進行了更新,forEach 的主體就會被強制執行,並重復該過程。forEach 函式可以使用簡單的變數呼叫,例如

   var i;
   forEach(i,range(0,4))
       {
       println("i is ",i);
       }

或者使用陣列或列表位置呼叫,例如

   var i = array(1,2,3);
   forEach(i[2],range(0,4))
       {
       println("i[2] is ",i[2]);
       }

或者使用物件成員呼叫,例如

   function bundle(a,b) { this; }
   var i = bundle(1,2);
   forEach(i . a,range(0,4))
       {
       println("i . a is ",i . a);
       }

腳註

[edit | edit source]
  1. 與大多數語言不同,Sway 允許使用者定義特殊形式。


變數和環境 · 函式

華夏公益教科書