变量¶
QL 中的变量与代数或逻辑中的变量使用方式类似。它们代表值集,这些值通常由公式限制。
这与某些其他编程语言中的变量不同,在其他编程语言中,变量代表可能包含数据的内存位置。该数据也可能随着时间的推移而改变。例如,在 QL 中,n = n + 1
是一个等式 公式,它仅在 n
等于 n + 1
时成立(因此实际上它对任何数值都不成立)。在 Java 中,n = n + 1
不是等式,而是将 1
添加到当前值,从而改变 n
值的赋值。
声明变量¶
所有变量声明都包含一个 类型 和一个变量名称。该名称可以是任何以小写字母开头的 标识符。
例如,int i
、SsaDefinitionNode node
和 LocalScopeVariable lsv
分别声明了类型为 int
、SsaDefinitionNode
和 LocalScopeVariable
的变量 i
、node
和 lsv
。
变量声明出现在不同的上下文中,例如在 select 子句 中,在 量化公式 内部,作为 谓词 的参数,以及更多其他情况。
从概念上讲,您可以将变量视为包含其类型允许的所有值,但要受任何进一步约束的限制。
例如,考虑以下 select 子句
from int i
where i in [0 .. 9]
select i
仅根据其类型,变量 i
可以包含所有整数。但是,它受公式 i in [0 .. 9]
的限制。因此,select 子句的结果是 0
到 9
(含)之间的十个数。
顺便说一下,请注意以下查询会导致编译时错误
from int i
select i
理论上,它将有无数个结果,因为变量 i
没有限制为有限数量的可能值。有关更多信息,请参阅“绑定”。
自由变量和绑定变量¶
变量可以具有不同的作用。一些变量是自由的,它们的值直接影响使用它们的 表达式 的值,或使用它们的 公式 是否成立。其他变量称为绑定变量,它们被限制为特定值集。
通过一个例子,理解这种区别可能最容易。请查看以下表达式
"hello".indexOf("l")
min(float f | f in [-3 .. 3])
(i + 7) * 3
x.sqrt()
第一个表达式没有任何变量。它查找字符串 "hello"
中 "l"
出现的位置的(从零开始的)索引,因此它评估为 2
和 3
。
第二个表达式评估为 -3
,即 [-3 .. 3]
范围内的最小值。尽管此表达式使用变量 f
,但它只是一个占位符或“虚拟”变量,您无法为其分配任何值。您可以用其他变量替换 f
,而不会改变表达式的含义。例如,min(float f | f in [-3 .. 3])
始终等于 min(float other | other in [-3 .. 3])
。这是一个绑定变量的示例。
那么表达式 (i + 7) * 3
和 x.sqrt()
呢?在这两种情况下,表达式的值取决于为变量 i
和 x
分别分配的值。换句话说,变量的值会影响表达式的值。这些是自由变量的示例。
同样,如果公式包含自由变量,那么公式是否成立取决于分配给这些变量的值 [1]。例如
"hello".indexOf("l") = 1
min(float f | f in [-3 .. 3]) = -3
(i + 7) * 3 instanceof int
exists(float y | x.sqrt() = y)
第一个公式不包含任何变量,并且它永远不会成立(因为 "hello".indexOf("l")
的值为 2
和 3
,永远不会是 1
)。
第二个公式只包含一个绑定变量,因此不受对该变量更改的影响。由于 min(float f | f in [-3 .. 3])
等于 -3
,因此此公式始终成立。
第三个公式包含一个自由变量 i
。公式是否成立取决于分配给 i
的值。例如,如果 i
被分配为 1
或 2
(或任何其他 int
),那么该公式成立。另一方面,如果 i
被分配为 3.5
,那么它就不成立。
最后一个公式包含一个自由变量 x
和一个绑定变量 y
。如果 x
被分配一个非负数,那么最后的公式成立。另一方面,如果 x
被分配为 -9
(例如),那么该公式就不成立。变量 y
不会影响公式是否成立。
有关如何计算对自由变量的赋值的更多信息,请参阅“QL 程序的评估”。
脚注
[1] | 这是一个略微简化的说法。有一些公式始终为真或始终为假,无论对它们自由变量的赋值如何。但是,在编写 QL 时,您通常不会使用它们。例如,a = a 始终为真(称为 重言式),而 x and not x 始终为假。 |