Swift运算符
Swift标准库包括程序员可能期望来自C系列中另一种语言的大多数运算符,以及一些方便的添加,如nil-coalescing operator(??
)和模式匹配operator(~=
),以及运算符类型检查(is
),型铸造(as
,as?
,as!
)以及形成开放或封闭范围(...
,..<
)。
中缀运营商
Swift 对二元运算符使用中 缀
表示法(而不是反向波兰表示法)。中缀运算符根据其关联性和优先级按以下顺序分组如下:
BitwiseShiftPrecedence
<< | 按位左移 |
>> | 按位右移 |
MultiplicationPrecedence
* | 乘 | / | 划分 | % | 剩余 | ||
&* | 乘以,忽略溢出 | &/ | 划分,忽略溢出 | &% | 剩余,忽略溢出 | ||
& | 按位AND |
AdditionPrecedence
+ | 加 | - | 减去 | &+ | 添加溢出 |
& - | 减去溢出 | 按位OR | ^ | 按位异或 |
RangeFormationPrecedence
… < | 半开放范围 | … | 封闭的范围 |
CastingPrecedence
是 | 键入检查 | 如 | 输入 |
NilCoalescingPrecedence
?? | 没有合并 |
ComparisonPrecedence
< | 少于 | <= | 小于等于 | |
> | 比…更棒 | > = | 大于或等于 | |
== | 等于 | != | 不相等 | |
=== | 相同 | !== | 不一样 | |
〜= | 模式匹配 |
LogicalConjunctionPrecedence
&& | 逻辑和 |
LogicalDisjunctionPrecedence
逻辑或 |
DefaultPrecedence
AssignmentPrecedence
= | 分配 | * = | 乘以并分配 | / = | 划分并分配 | ||
%= | 剩余并分配 | + = | 添加并分配 | - = | 减去并分配 | ||
<< = | 左移位和分配 | >> = | 右移位和分配 | &= | 按位AND和赋值 | ||
^ = | 按位异或并分配 | 按位OR和赋值 | && = | 逻辑AND和分配 | |||
逻辑OR和赋值 |
运算符优先级组最初使用数字优先级定义。例如,乘法运算符定义的优先级值为150,因此它们在加法运算符之前进行求值,这些运算符定义了优先级值140。
在Swift 3中,运算符更改为通过部分排序来定义优先级以形成DAG
或有向非循环图
。有关此更改的详细信息,请阅读Swift Evolution提议 SE-0077改进的操作员声明。
一元运算符
除了采用两个操作数的二元运算符之外,还有一元运算符,它采用单个操作数。
前缀运算符
前缀运算符在它们运行的表达式之前出现。Swift默认定义了一些:
-
+
:一元加 -
-
:一元减去 -
!
:逻辑不 -
~
:按位NOT
在Swift 3中删除了递增/递减(++
/ --
)运算符。这是 在语言作为开源发布后作为Swift Evolution
过程的一部分进行的第一次更改之一 。在提案中,Chris Lattner
描述了这些运算符如何令人困惑,并争论为什么语言中不需要它们。
后缀运算符
一元运算符也可以在它们的操作数之后出现,就像postfix变种一样。这些不太常见; Swift标准库仅声明开放式范围后缀运算符...
。
三元运营商
三元?:
运算符很特殊。它需要三个操作数和函数,如单行if-else
语句:评估左侧的逻辑条件,?
并:
根据结果在左侧或右侧生成表达式:
true ? "Yes" : "No" // "Yes"
在Swift中, 定义低于 和高于。但是,一般来说,最好保持三元运算符的使用简单(或完全避免使用它们)。TernaryPrecedence
DefaultPrecedence
AssignmentPrecedence
运算符重载
声明运算符后,它可以与类型方法或顶级函数关联。当操作员可以根据操作数的类型解析不同的函数时,我们说运算符是过载的。
可以在+
运营商处找到最重要的超载示例。在许多语言中,+
可用于1 + 2 => 3
对数组和其他集合([1] + [2] => [1, 2]
)执行算术加法()或连接。
开发人员可以通过使用适当的参数数量和类型为运算符符号声明新函数来重载标准运算符。
然而,这种语言使用是有争议的。(任何C ++开发人员都非常渴望用恐怖故事来谴责你,这可能造成非确定性的破坏)
默认情况下,+
运算符连接两个数组的元素,并使用通用函数定义实现。
如果要声明一个专用函数,该函数重载+
for Double
values 数组以执行成员添加,它将覆盖先前的连接行为:
// ?
func + (lhs: [Double], rhs: [Double]) -> [Double] {
return zip(lhs, rhs).map(+)
}
[1.0, 3.0, 5.0] + [2.0, 4.0, 6.0] // [3.0, 7.0, 11.0]
这就是运算符重载的原罪: 模糊的语义。
这+
对数字有用 - 这就是数学。但是,如果你真的想到它, 为什么要将两个字符串连接在一起呢?1 + 2
不是12
(Javascript
除外)。这真的很直观吗?…或只是熟悉。
在决定是否使现有运算符超载时要记住一些事项。
相比之下,PHP .
用于字符串连接,而SQL用于||
; Objective-C本身没有运算符,但会附加带有空格的连续字符串文字。
定义自定义运算符
Swift最令人兴奋的功能之一(虽然也有争议)是定义自定义运算符
的能力。
考虑使用**
许多编程语言中的指数运算符,但是从Swift中找不到。它将左手数字提升到右手数字的幂。(^
通常用于上标的符号已由 按位XOR
运算符使用)。
Exponentiation具有比乘法更高的运算符优先级,并且由于Swift没有我们可以使用的内置优先级组,我们首先需要自己声明一个:
precedencegroup ExponentiationPrecedence {
associativity: right
higherThan: MultiplicationPrecedence
}
现在我们可以声明运算符本身:
infix operator ** : ExponentiationPrecedence
最后,我们使用new运算符实现顶级函数:
import Darwin
func ** (lhs: Double, rhs: Double) -> Double {
return pow(lhs, rhs)
}
2 ** 3 // 8
我们需要导入Darwin模块来访问标准数学函数pow(_:_:)
。(或者,我们可以导入Foundation而不是相同的效果。)
创建自定义运算符时,请考虑提供变异变量:
infix operator **= : AssignmentPrecedence
func **= (lhs: inout Double, rhs: Double) {
lhs = pow(lhs, rhs)
}
var n: Double = 10
n **= 1 + 2 // n = 1000
使用数学符号
自定义操作员可使用的字符的组合 /
,=
,-
,+
,!
,*
,%
,<
,>
,&
,|
,^
,或~
,并在找到的任何字符 数学运算符的Unicode块,等等。
在您自己的代码中覆盖或定义新运算符时,请确保遵循以下准则:
- 除非其含义明显且无可争议,否则不要创建运算符。寻找任何潜在的冲突以确保语义一致性。
- 注意自定义运算符的优先级和关联性,并且只在必要时定义新的运算符组。
- 如果有意义,请考虑为自定义运算符实现赋值变量(例如
+=
for+
)