跳转到内容

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 允许用户定义特殊形式。


变量和环境 · 函数

华夏公益教科书