8.6 赋值表达式

赋值是将新值存入变量的表达式。例如,以下表达式将值 1 赋给变量 z

z = 1

执行此表达式后,变量 z 的值为 1。无论 z 之前的旧值是什么,都会丢失。这里的 = 符号称为赋值运算符

赋值也可以存储字符串值。例如,以下表达式将值 "this food is good" 存入变量 message

thing = "food"
predicate = "good"
message = [ "this " , thing , " is " , predicate ]
⇒   "this food is good"

(这也演示了字符串的连接。)

大多数运算符(加法、连接等)除了计算值外没有其他效果。如果你不打算使用这个值,那么最好不要使用该运算符。赋值运算符则不同:它确实返回一个值,但即使你忽略了该值,赋值操作仍然会通过改变变量的方式产生影响。我们称这种效果为副作用

赋值的左侧操作数不一定是一个变量(参见 变量)。它可以是矩阵的元素(参见 索引表达式),或是返回值列表(参见 调用函数)。这些统称为左值,意味着它们可以出现在赋值运算符的左侧。右侧操作数可以是任何表达式,它产生新值,赋值操作将其存入指定的变量、矩阵元素或返回值列表中。

需要强调的是,变量 没有 固定的类型。变量的类型就是它当前所持有值的类型。在下面的代码片段中,变量 foo 先具有数值,后又具有字符串值:

>> foo = 1
foo = 1
>> foo = "bar"
foo = bar

当第二次赋值给 foo 一个字符串时,它就丢弃了之前的数值。

将标量赋值给一个已建立索引的矩阵,会将索引指向的所有元素设置为该标量值。例如,如果 a 是一个至少有两列的矩阵,

a(:, 2) = 5

a 第二列中所有元素的值设为 5。

当赋值将向量、矩阵或数组的元素设置在超出变量当前大小的位置或维度上时,数组的大小会增大以容纳新值:

>> a = [1, 2, 3]
a = 1 2 3
>> a(4) = 4
a = 1 2 3 4
>> a(2, :) = [5, 6, 7, 8]
a =
   1   2   3   4
   5   6   7   8

如果试图增大数组导致结果大小不明确,则会引发错误:

>> a(9) = 10
-| error: Invalid resizing operation or ambiguous assignment to an
out-of-bounds array element

这是因为添加第 9 个元素时,对值 10 应放置的数组位置存在歧义,不同的可能性需要不同的数组扩展方式来容纳该赋值。

只要赋值是明确的,就可以使用比填充新扩展数组所需更少的指定元素进行赋值。在这种情况下,数组将自动用 值填充:

>> a = [1, 2]
a =   1   2
>> a(4) = 5
a =   1   2   0   5
>> a(3, :) = [6, 7, 8, 9]
a =
   1   2   0   5
   0   0   0   0
   6   7   8   9
>> a(4, 5) = 10
a =
    1    2    0    5    0
    0    0    0    0    0
    6    7    8    9    0
    0    0    0    0   10

对于所有内置类型, 值采用该对象类型所对应的空值。

数值数组:

>> a = int32 ([1, 2])
a = 1, 2
>> a(4) = 5
a = 1 2 0 5

逻辑数组:

>> a = [true, false, true]
a = 1 0 1
>> d(5) = true
d = 1 0 1 0 1

字符数组:

>> a = "abc"
a = abc
>> a(5) = "d"
a = abcd
>> double (a)
ans = 97 98 99 0 100

元胞数组:

>> e = {1, "foo", [3, 4]};
>> e(5) = "bar"
e =
{
  [1,1] = 1
  [1,2] = foo
  [1,3] =

     3   4

  [1,4] = [](0x0)
  [1,5] = bar
}

结构体数组:

>> a = struct("foo",1,"bar",2);
>> a(3) = struct("foo",3,"bar",9)
a =

  1x3 struct array containing the fields:

    foo
    bar

>> a.foo
ans = 1
ans = [](0x0)
ans = 3
>> a.bar
ans = 2
ans = [](0x0)
ans = 9

请注意,Octave 目前无法将任意对象类型连接到数组中。这种行为必须在对象类中显式定义,否则尝试连接将导致错误。另请参见 面向对象编程

赋值一个空矩阵 [] 在大多数情况下可删除矩阵和向量的行或列。另请参见 空矩阵。例如,对于 4×5 的矩阵 A,赋值

A (3, :) = []

将删除 A 的第三行,而

A (:, 1:2:5) = []

将删除第一、三、五列。

删除数组对象的一部分必然会导致对象大小的调整。如果删除操作可以在某个维度上一致地减小大小——例如向量中的一个元素,或矩阵的一行或一列——那么该维度的大小将减小,同时其余维度保持不变。但如果无法保持维度完整性,对象将按列主序展开为向量:

>> a = [1, 2, 3, 4; 5, 6, 7, 8]
a =
   1   2   3   4
   5   6   7   8
>> a(:, 3) = []
a =
   1   2   4
   5   6   8
>> a(4) = []
a = 1 5 2 4 8

赋值本身是一个表达式,因此它具有一个值。因此 z = 1 这个表达式的值为 1。这意味着你可以将多个赋值写在一起:

x = y = z = 0

这条语句将值 0 存入所有三个变量。它是这样工作的:z = 0 的值为 0,存入 y,然后 y = z = 0 的值也为 0,再存入 x

对于值列表的赋值也是如此,因此以下表达式是有效的:

[a, b, c] = [u, s, v] = svd (a)

这完全等价于

[u, s, v] = svd (a)
a = u
b = s
c = v

在这样的表达式中,每个部分的值数量不必一致。例如,表达式

[a, b] = [u, s, v] = svd (a)

等价于

[u, s, v] = svd (a)
a = u
b = s

但是,表达式左侧的值数量不能超过右侧的值数量。例如,以下操作将返回错误:

[a, b, c, d] = [u, s, v] = svd (a);
-| error: element number 4 undefined in return list

符号 ~ 可以用作左值列表中的占位符,指示对应的返回值应被忽略而不存储:

[~, s, v] = svd (a);

这比使用哑变量更简洁且内存效率更高。此处右侧表达式的 nargout 值不受影响。如果将赋值用作表达式,则返回值是以逗号分隔的列表,其中被忽略的值将被删除。

一种非常常见的编程模式是用给定值递增现有变量,如下所示:

a = a + 2;

使用 += 运算符可以简写为:

a += 2;

类似的运算符也存在于减法(-=)、乘法(*=)和除法(/=)中。以下表达式

expr1 op= expr2

被求值为

expr1 = (expr1) op (expr2)

这里的 op 可以是 +-*/ 中的任意一个,前提是 expr2 是一个没有副作用的简单表达式。如果 expr2 还包含赋值运算符,则该表达式的求值方式为

temp = expr2
expr1 = (expr1) op temp

其中 temp 是一个占位临时值,用于存储 expr2 的求值结果。因此,表达式

a *= b+1

被求值为

a = a * (b+1)

不是

a = a * b + 1

你可以在任何需要表达式的地方使用赋值。例如,可以写 x != (y = 1)y 设为 1,然后测试 x 是否等于 1。但这种风格往往会使程序难以阅读。除非是一次性程序,否则应重写代码以消除这种嵌套赋值,这通常不难做到。


版权所有 © 2024-2026 Octave中文网

ICP备案/许可证号:黑ICP备2024030411号-2