Swift 十二讲 第二章 常量变量类型元组字符串和操作符
(Draft)
1. 类型,常量,变量
- 数值类型
Swift包含各种常见数据类型。例如各种有无符号的32位,64位整数,浮点数,布尔数等等。数据类型的max,min属性用来报告数据类型的最大最小区间。例如:
Int.max
//Playground里显示答案:9,223,372,036,854,775,807
0b,0o,0x分别用来表示二进制,八进制,十六进制数。Swift也支持1.2e4这样的缩写。详细细节可以参考官方手册。
-
Swift支持Character和String类型。但不区分字母和单字母的字符串。这两种类型都要用双引号。例如:
var aa : String = "小狗" var bb: Character = "A"
-
Swift可以给类型起个直观的别名,然后方便使用。例如
typealias 短整数 = UInt8
var cc : 短整数 = 105
//注意不要使用中文输入法里的等号;注意别忘了等号前后的空格
上面这段代码,用关键字typealias定义了一个“短整数”类型别名。然后定义了一个变量cc,其类型为“短整数”。cc被初始化为数值105。
2.变量和常量
- 在使用一个变量或者常量之前,你必须声明它们。比较下面三种变量声明然后初始化的方法:
var a = 1
var a: Int = 1
或者
var a: Int
a=1
很显然,最好用的是var a=1
。 Swift会自行推断类型。注意,初始化不是必须的。上面例子是为了说明简洁。
变量用var关键字。常量用let关键字。例如:
let k = 1
let kk : Float = 3.14
常量不能被改变。所以它在声明的时候,必须被赋值。如上面的例子所示,常量的类型推断使用起来也非常方便。
- 除了上面和其他语言类似的变量声明之外,你还可以声明计算变量。如下例子:
var ax : Int
{return 20}
ax+ax //Playground显示结果为40
你还可以在上例的大括号中使用get和set函数。但作者认为这两个函数在这里用处不大。就不展开了。有兴趣的读者可以在Playground试验。
*willset和didset这两个函数可以在变量被声明的时候附带定义。如果使用变量观测器,变量类型必须被显式指定,而且必须被初始化。如下例:
var a : Int = 0
{
willSet
{println("a将被赋值")}
didSet
{println("a已经被赋值")}
}
a = 0 //Playground的console会显示"a将被赋值"和"a已经被赋值"两行字
作者个人认为,计算变量,willSet和didSet是面向对象风格的语言要素。传统语言例如c的变量声明是不包含这些部分的。对变量来说,这三个东西的用处不大,还说不定容易引起混乱。但后面读者会看到。类的属性也使用类似的语法,那里就非常关键了。
2.元组(Tuples)
- 不同类型的变量或者常量,加上逗号分隔,加上括号,就构成了元组。元组的声明和初始化方法和变量,常量的风格是一致的。假设你熟悉了变量和常量,那么下面例子,一看就可以懂。
var a = ("狗", 1, 2.3) //使用类型推断定义了一个元组变量
println(a.0) //用位置表示元组中的单个元素,console输出为“狗”
你也可以用下面方法定义元组。效果是一样的。
var a : (String, Int, Float)
a = ("狗", 1, 2.3)
println(a.0)
- 元组的单个元素还可以有个名字,你可以用名字而不是位置来指出这个元素。
var a:(动物种类 : String, 年龄 : Int, 毛长 : Float)
a = ("狗", 1, 2.3)
println(a.动物种类)
//a.0 和 a.动物种类 是同一个元素的名字。后者更直观而已。
- 你可以对元组的类型使用类型别名,也就是关键字typealias
typealias 动物 = (动物种类 : String, 年龄 : Int, 毛长 : Float)
var a:动物
a = ("狗", 1, 2.3)
println(a.动物种类)
- 显然,Swift的元组是非常强大的。尤其是在一块程序往另一块程序输入输出信息的时候,元组这个容器可以非常好用。但笔者建议,对结构化数据,尽量用专门的结构化数据类型。专门适用的结构化数据类型可能有性能优势。例如整型数组,你写成元组,肯定性能不是很划算。
3.操作符
操作符被用来指明作用于被操作的语言名词上的一些操作。例如加减乘除,绝对值等等。这些定义都和C语言类似。Swift也支持位运算,以及=++,++等操作符。这些常用的操作符,笔者就不抄写手册了。但有以下几点,笔者认为应该特别注意。
- Overflow operators
Swift支持溢出加减乘除。例如溢出除法&/:
println(0&/0) //输出是0。Swift的溢出除法规定被0除返回0
-
类型检查和转换操作符
Swift支持三种类型检查和转换操作符:
is被用来检查是否一个instance属于一个指定的类或者协议
as被用来转换到一个子类或者协议。
as?可选转换。如果转换失败,返回nil。
这些内容会在后面详细解释。读者在此知道有这么回事就好。 -
范围操作符
(a...b)表示a到b的所有整数,包含a和b。a必须小于或者等于b。
for i in 1...5
{
println(i)
} //将输出1,2,3,4,5
也可以用小于号和两个点:
for i in 1..<6
{
println(i)
} //将输出1,2,3,4,5
- 分支选择操作符
expr1? expr_A:expr_B
上面描述的意思是,如果条件表达式expr1的值为真,则执行expr_A的代码段。不然就执行expr_B。例如:
var a = 10
a == 1 ? println(a) : println(-a)
//输出是-10
4. 字符串的加减,比较,传递和\操作
-
Swift的字符串是实际值类型。每次被传递到一个函数里,都会被复制一份。字符串被传递的不是其地址。
-
字符串可以做加法。就是把另一个字符串附加到前一个。也可以连加。例如:
let a = "我是" + "一个" + "好人"
println(a) //输出为:我是一个好人
- countElements(MyStrings)这个函数用来计算字符串包含的字符数,而不是bytes数。例如
println(countElements("我是一个好人")) //输出为6
- 字符串的比较
==表示字符相等。!=表示不等。如果一个字符串字典序在另一个之前,那么前者为小,例如:
println("blue"<"dog") //输出为true
println(("好人"<"坏人")) //输出为false。
//中文字符串的字典序很不直观啊。谁能告诉我为什么好人小于坏人...
- 字符串的前后缀
字符串有两个内建属性函数: hasPrefix和hasSuffix。
下面例子可以明确说明字符串前后缀判断函数的用法:
println(("好人".hasPrefix("好"))) //输出为true
println(("好人不坏".hasSuffix("坏"))) //输出为true
- Escape符号
我个人认为,Escape符号在编译器里面的用法,是计算机科学最简单明了,但又最深的部分之一。绝对不要小瞧了它。
Ken Thmpson的图灵奖讲话:
http://cm.bell-labs.com/who/ken/trust.html
This is an amazing piece of code. It "knows" in a completely portable way what character code is compiled for a new line in any character set. The act of knowing then allows it to recompile itself, thus perpetuating the knowledge.
这个东西的原理很简单,当编译器读到你代码里的一个\的时候,它会再读下一个字符,然后才能决定前一个字符的意思。如果是两个\,那么它的意思就是一个\。如果是\n,因为只有一个,所以它的意思就是\n本身,也就是line feed。
所以编译器对\的翻译需要两次扫描,第一次先走到\后面的字符,然后再回来决定前一个\的意思。这种两次扫描的办法,是所有编译器最基本的原理的一部分。有了这个办法,你可以逐步扩展你的编译器功能。人常听说"C的编译器是C写的"之类的说法,其根本原理其实就是在于Escape符号的用法。
- 字符串的插值
在Swift里,可以用** "\(expr)" **计算expr的值,然后结果被转换为字符串。下面是一些例子:
println("\(2+3)")
println("神奇的Escape=\(2+3)")
//上句输出是: 神奇的Escape=5
println("神奇的Escape=\\")
println("神奇的Escape=\\\(2+3)")
还有一个深入一点的例子:
let b = 20
println("\((b == 10) ? 1 : 2)")
//输出为2
记住** "\(expr)" 是个筐,里面什么都能装。当然,虽然 "\(expr)" **结构简单+原理深刻,多试验有助于理解计算机工作原理。但作者不鼓励这么写。