Swift初步入门

本文介绍苹果Apple公司开发的Swift编程语言,帮助感兴趣但尚未接触Swift编程语言的人初步了解它。主要介绍Swift里的一些基本语句及其功能和规则。

一、普通变量和常量及数值

Swift和C++,Java一样,也是一种强类型语言,即变量有固定的类型,必须在声明时就明确下来。不像在Python中一个已有的变量的类型可以任意变化,Swift的变量类型在程序中是不可改变的。变量的声明语句用以下方式确定变量的类型。

var z:Float

该语句,声明了一个名称为z,类型为Float(即浮点小数)的变量。

另外,在声明变量类型的同时,也可同时给变量赋予一个初始值。

var y:UInt = 4

该代码在创建UInt类型的变量y的同时,给它赋予了初始值4。

事实上,Swift在解析数值及表达式时,已经给其赋予默认的类型了。所以,如果声明变量的语句带有赋值过程,则可以不明确声明变量类型

var x = 4.5
print(type(of:x)) // “Double” is printed

此处变量x的类型未被直接声明,故系统把4.5的默认类型Double赋予x。事实上4.5可以是Double类型也可以是Float类型,但系统自动选择了默认项。所以,若对变量类型有明确要求,还是应当在声明里明确。

注:如果声明变量的语句没有赋值过程,则必须声明变量类型,否则会报错(无法确定类型)

var x // This sentence can not compile

Swift也支持常量,即声明时就有确定的值且运行时值不可改变。声明常量的方式后如下

let w:Int = 10

类型也可声明或不声明,但赋予初始值的过程必须有,否则会报错。

变量的名称不一定是英文字母,可以是汉字或阿拉伯字母等。例如以下句子是合法的

var 冬=6
var پ=6

注意第二行,变量名是阿拉伯字母"پ",但由于阿拉伯字母是从右向左书写,故而显示在最右方,但事实上在等号前面。虽然理论上允许,但强烈不建议用阿拉伯字母或希伯来字母等从右向左书写的字母作为Swift的变量名称。

除此之外,Swift还支持可选类型(Optional),即变量仍有确定的类型,但允许变量值不存在。

var M:Int?

该语句声明一个整数可选型变量M。该变量的值可以是一个整数,也可以是nil(表示值不存在)。所以以下两个语句均合法:

M = 10
M = nil

但可选类型的变量不能直接赋值给非可选类型的变量。例如,以下句子会报错:

var y1:Int = M

但可以对可选类型的变量进行强制展开(unwrap),将其强行转化为非可选类型的变量进行赋值。当然,如果该可选变量的值是nil,则运行时会报错。

var y1:Int = M!

这个语句编译可以通过,但运行时有可能出错。

事实上Swift还支持一种逻辑,就是当需要使用可选变量的值,但如果该可选变量的值不存在(nil),那么也有一个专门的值进行代替。

var y1:Int = M ?? 20

该语句的意思是:当可选变量M的值存在(不为nil)时,将当前的M值赋给y1;当可选变量M的值不存在(为nil)时,将20赋给y1。这个写法将两条逻辑汇聚(coalesce)在了一起。

最后,Swift还有一个小功能,就是可以给变量类型赋予一个别名。例如,对于布尔型变量,有的用户不喜欢Bool这个类型名,则可以给其赋予一个别名bin。

typealias bin=Bool

所以之后,要声明一个布尔型变量,可以用如下语句:

var b:bin = false

二、打印功能

Swift可以通过一个叫print的函数打印变量值和文字。该功能对程序调试有帮助。

var ou = "Hello Tom"
var a = 2+3
print(a,ou)
/*print result: 
5 Hello Tom\n
*/

该函数可以输出变量a和ou的值,中间用空格隔开,全部输出完成后换行。

若不愿意用空格隔开变量,或在全部输出后输出换行符,则可以在print函数里附加一些参数

print(a,ou, separator:"&", terminator: " End\n")
/*print result:
5&Hello Tom End\n
*/

此时&被用于隔开变量,全部输出后输出End+换行符。

关于函数的具体使用方式,见第九章。

三、分支结构

和大多数语言一样,Swift中有两种方式表达分支结构。

If语句

var a:Int = 5

if a < 4
{
    print("a<4")
}
else if a<7
{
    print("4<=a<7")
}
else
{
    print("a>7")
}

语法和C语言基本一致,区别在于条件不一定要用括号括起来。

Switch 语句

switch a {
case 10:
    print("a = 10")
case 5:
    print("a = 5")
    fallthrough //without this, if one case is matched the switch will be exited 
case 5:
    print("a = 5")
default: break

}

Swift的Switch语句和C/C++/Python略有不同,在于退出Switch语句的条件。在C++等语言中,如果在一个case语句的代码结束后,没有break,那么下一个case语句会继续进行。但在Swift语言中,只有加入了fallthrough语句,下一个case语句才会继续进行。

在代码示例中,a值若为5,"a=5"会被打印两次(因为第一个case 5语句结束后有fallthrough)。但如果fallthrough被去除,则"a=5"只会被打印一次。

四、列表

Swift支持列表变量类型。在列表中,每一个元素都为相同类型。

var friends = ["Tom","Jerry","Helen"]

在该代码中,friends的类型是[String],即String类型的列表,每一个元素都是String类型。

如果一个列表里的元素类型不同,通常会报错。

var fakeList = ["text",5]

这一段语句无法编译。

但是,Swift也支持一种特殊的列表类型,称为Any列表。所以,如果把fakeList声明为Any型列表,则可以运行。

var fakeList = ["text",5] as [Any]

Swift列表的两种常见的操作是在列尾添加元素以及在指定位置删除元素。

friends.append("Bilqis")
friends.remove(at: 1)

注意在第二句中,1代表需要删除第二个元素,因为Swift中元素是从0开始计数的。

获取列表中指定位置的元素的代码是:

var f = friends[0]

另外,同类型的列表之间可以用+连接,把两个列表拼接在一起,形成新的列表

print(friends + friends)
/*print result:
["Tom", "Helen", "Bilqis", "Tom", "Helen", "Bilqis"]
*/

列表还有两个常见的属性:count和isEmpty。前者返回列表元素个数,后者返回列表是否为空。

print(friends.isEmpty)//False
print(friends.count)//3

五、循环结构

Swift支持for循环和while循环。

For循环

其中,for循环,可以令循环变量在一个从小到大的区间里以1为步长遍历。

例如:令变量i从1至5遍历(包括1和5)

for i in 1...5{
    print(i)
}

此时会输出1,2,3,4,5

也可以包括最小值1,不包括最大值5。这个功能在遍历列表时有用。

for i in 1..<5{
    print(i)
}

此时会输出1,2,3,4

除此之外,for循环还可以令循环变量在一个列表里遍历。

例如:令变量s遍历friends里的各个元素

for s in friends{
    print(s)
}

此时会把friends里的每一个名字一个一个输出。

While循环

Swift还支持while循环。

可以把while及循环条件写在循环的开始,实行"当循环"。

var ii = 1.0
while (ii < 10){
    ii = ii + 0.1
    print(ii)
}

也可以把while及循环条件写在循环的最后,实行"直到循环"。

ii = 1.0
repeat{
    ii = ii + 0.4
}while(ii < 10)
print(ii)

六、元组

元组(tuple)也是一个可以存放多个元素的数据类型,但和列表不同,元组不要求每一个元素类型一致。

var friends = ("Tom", "Jamila")
print(friends.1)

该元组包含两个元素,均为String类型。元组里元素的下标也同样从0开始。但和列表不同,从元组中提取元素的语句是元组.下标。以上程序会输出"Jamila"。

除了从0开始的数字,元组里元素的下标也可由用户自己定义。

var Jerry = (name:"Jerry", age:27)
print(Jerry.name)

此时,name和age成了元组Jerry里元素的下标。

七、集合

集合也是一个可以包含多个元素的Swift变量,而且也需要每个元素类型一致。但和列表不同的是,集合内的元素无先后之分,所以不能根据索引号提取元素;另外元素不能重复,相同的元素只能出现一次。

声明一个集合,要把集合内元素的类型也包括进来。

var primeNumbers = Set<Int>()

集合的操作包括添加元素(insert)以及删除元素(remove),且和列表一样,也有元素个数的属性(count)。

print("In the first step, the set primeNumbers has the size of \(primeNumbers.count).")
primeNumbers.insert(5)
print("After inserting 5, the set primeNumber now has the size of \(primeNumbers.count).")
primeNumbers.insert(5)
print("After inserting 5 again, the set primeNumber now still has the size of \(primeNumbers.count).")
primeNumbers.insert(2)
print("After inserting 2, the set primeNumber is now \(primeNumbers)")
primeNumbers.remove(2)
print("After removing 2, the set primeNumber is now \(primeNumbers)")
/*
打印的结果

In the first step, the set primeNumbers has the size of 0.
After inserting 5, the set primeNumber now has the size of 1.
After inserting 5 again, the set primeNumber now still has the size of 1.
After inserting 2, the set primeNumber is now [2, 5]
After removing 2, the set primeNumber is now [5]


*/

显然,元素5插入第二次后,集合primeNumbers的元素个数并未改变,仍为1。

另外集合可进行并集(union),交集(intersection)以及补集(subtraction)的运算。

令primeNumbers为{5},primeNumbers2为{3,7},则primeNumber3,primeNumbers4,primeNumbers5为这两个集合的并集,交集,补集,代码如下:

var primeNumbers3 = primeNumbers.union(primeNumbers2)
print("the union of primeNumbers and primeNumbers2 is \(primeNumbers3).")
print("now primeNumbers is still \(primeNumbers).")
var primeNumber4 = primeNumbers.intersection(primeNumbers2)
print("the intersection of primeNumbers and primeNumbers2 is \(primeNumber4).")
var primeNumber5 = primeNumbers3.subtracting(primeNumbers)
print("the subtraction of primeNumber from primeNumber3 is \(primeNumber5).")

/*打印结果
the union of primeNumbers and primeNumbers2 is [5, 3, 7].
now primeNumbers is still [5].
the intersection of primeNumbers and primeNumbers2 is [].
the subtraction of primeNumber from primeNumber3 is [7, 3].
*/

八、字典

Swift中的字典是一个包含元素与元素之间1对1的对应关系的数据类型。字典中的元素分为键(Key)和值(Value),且每一个键都有一个对应的值。一个字典里的键不能重复,但不同的键对应的值可以一样。键的变量类型和值的变量类型不一定一样,但每一个键,每一个值的变量类型必须统一。(但也可以是Any)

var scores:[String:Int] = ["Chinese": 95, "Mathematics": 103, "English": 133, "Science":110]

在该代码中,字典scores的类型是[String:Int],表明字典的键是String类型,而每一个键对应的值都是Int类型。

提取字典中的元素,需要根据键提取值。例如

print(scores[”Chinese“])

这里把键"Chinese"对应的值95提取了。

另外,可以在字典中添加或更新元素。添加或更新元素也要基于键。

scores.updateValue(111, forKey: "PE")
scores.updateValue(91, forKey: "Chinese")

以上语句中,第一句是添加以"PE"为键,以111为值的元素。由于该键在字典scores中不存在,所以添加新元素。而第二句是更新键"Chinese"对应的值为91。

另外这两句语句等价于

scores["PE"] = 111
scores["Chinese"] = 91

删除字典中的元素,有两种方法:

scores.removeValue(forKey: "Mathematics")

该方法删除"Mathematics"键以及对应的值。

以下语句产生同样的效果:

scores["Mathematics"] = nil

九、函数

在Swift中,函数的定义由func开头。

func print100(){
    print(100)
}
print100()

参数输入

下面定义一个有输入的函数

func printNumber(num:Int){
    print(num)
}
printNumber(num: 4)

在Swift中,以这种形式定义的函数,在调用时,需注明输入参数的名称

func printNumber2(num a:Int){
    print(a)
}
printNumber2(num: 5)

此处,输入的参数似乎有了两个名称,一个是num,另一个是a。他们的区别在于num是外部名称,a是内部名称。在函数内部的定义中,使用该输入,应当使用内部名称,这里指a;在外部调用函数时,输入参数的名称是其外部名称。

注:在函数printNumber中,由于输入参数只指定了一个名称,所以内部名称和外部名称一致;而在函数printNumber2中,由于输入参数给了两个名称,故有内部和外部之分。

函数除了有输入以外,也可以有输出。即返回值。在Swift中,函数的返回类型也应在函数定义时给出。

func stringAppendChars(originStr:String, appendChars:[Character]) ->String{
    var newStr = originStr
    for c in appendChars{
        newStr.append(c)
    }
    return newStr
}
var s = "I am listen"
var newS = stringAppendChars(originStr: s, appendChars: ["i","n","g","."])
print("The corrected sentence is \"\(newS)\".")

在该函数的定义中,第一行的末尾的->String表明该函数返回的类型是String。

到这里有人会问:在C++,Java等语言中调用函数,输入参数都不用注明变量名,直接按顺序给出参数就行了,Swift里函数输入就一定要注明变量名吗?

不一定。Swift中,有一种定义函数的形式,可以使函数在被调用时输入参数不用注明变量名。

func substract(_ a:Int, _ b:Int)->Int{ //Don't forget the space between _ and a!
    return a-b
}
print("5-2=\(substract(5, 2)).")

在该函数中,各输入的外部变量名均为_,表明没有外部名称。因此,函数在外部被调用时,无需注明变量名。

另外,一个函数的输入参数,个数并不一定是固定的,也可以是任意的。例如,一个函数可以输入任意个参数,然后返回所有参数的和。这类函数被称为变参函数(variadic function)。

func prod(_ numbers:Float...)->Float{
    var result:Float = 1
    for n in numbers{
        result = result * n
    }
    return result
}
var productResult = prod(1.2,5.3,5)
print("the product is \(productResult)")

上面这个函数可以输入任意个Float参数,然后计算所有参数的乘积并返回。输入的类型就是参数类型再加三个英文句点即可。

在这里,我们可以回到第二章节的print打印函数。

print(a,ou, separator:"&", terminator: " End\n")

同时附上该内置函数的定义的第一行:

这个函数可以输入任意个参数,类型为Any,内部名称为items,无外部名称,所以输入a和ou时无需注明变量名。但除此之外,还有separator和terminator参数。当需要输入这些参数时,必须注明变量名,因为这两个输入参数的外部名称没有定为_。

另外,和Python类似,Swift函数的输入也可定义默认值,故而在调用函数时,也可以不传递输入变量值。

func addNumbers(a:Int = 0, b:Int = 0)->Int{
    return a+b
}
print("a+b=\(addNumbers())")

在这个例子中,addNumber()被调用时没有传递任何值,所以a和b均取默认值0。

值传递和引用传递

通常,Swift函数在被调用时,输入的变量是值传递而非引用传递。如果了解C++语言,C++语言输入变量时也是如此,但输入数组时会有不同。

例如,用以下C++程序交换两个变量

void exchange(int x, int y){
    int z = x;
    x = y;
    y = z;
}

int main(){
    int a = 4;
    int b = 3;
    exchange(a, b);
    return 0;
}

exchange函数在main中被调用后,a值仍然为4,b值仍然为3。这是因为exchange函数运行时,只是接受了a和b的数值,赋予给了exchange函数中的x和y,故函数对x和y操作时,对函数外的a,b变量没有影响。然而这样的编程方式很容易引起误会,导致程序调试难。

所以,Swift中为了避免这样的歧义,对这种编程方式进行了禁止。

func exchange(_ x: Int, _ y: Int){
    var z:Int = x
    x = y
    y = z
}
var a = 4
var b = 5
exchange(a, b)

以上这段代码,在Swift中无法编译成功,会报错:

因为Swift禁止在函数中修改值传递的输入参数。换句话说,Swift将输入参数定义为常量。

若要在Swift中,实现用函数进行变量交换的功能,应当使用引用传递,引用输入的变量本身而不是只是数值。这样的引用,需要在输入的变量类型前加上关键字inout。

func exchange(_ x: inout Int, _ y: inout Int){
    var z:Int = x
    x = y
    y = z
}

当该函数被调用时,inout类型的参数的输入方式不应是数值输入,而是变量引用。为了实现这个功能,调用函数时,应在变量前加&,表明输入的是变量的引用。

var a = 4
var b = 5
exchange(&a, &b)
print("now a=\(a), b=\(b)")

注意exchange函数被调用时的参数输入。此时,a的值会变为5,b的值会变为4。

返回函数的函数

最后,Swift函数和C++有一点不同在于,C++函数的定义不允许嵌套,但Swift中是允许的。Swift函数甚至可以返回一个函数。

func addOrSubtract(o:Character)->((Int, Int)->Int){
    if (o == "+"){
        func add(_ a:Int, _ b:Int)->Int{
            return a + b
        }
        return add
    }
    else if o == "-"{
        func substract(_ a: Int, _ b: Int)->Int{
            return a - b
        }
        return substract
    }
    else{
        func void(_ a: Int, _ b: Int)->Int{
            return 0
        }
        return void
    }
}
var opp = addOrSubtract(o: "+")
print("9+9 = \(opp(9,9))")

该函数根据输入的字符,返回一个加法或减法或无效函数。注意看addOrSubtract返回的类型是((Int, Int)->Int),也是一个函数。不过这个"函数"和一般的函数有所不同,在于它没有固定的名称。这类无名函数有另一个称号:闭包(Closure)。关于闭包,可参考本专栏的另一篇文章:Swift编程语言中闭包(closure)的使用-CSDN博客

十、其它

介绍一些Swift编程细节。

(一)分号

和C++不同,Swift语句不需要以分号结尾。换行符就代表一句语句结束。

但加了分号也不会报错。如果要在一行里写多个语句,则每一个语句的末尾需加分号。

var b:Int = 4; var c:Float = 3.1;

(二)内部函数

Swift内部有一些常见的函数,如arc4random,pow,sqrt等。但调用这类函数前,需要先通过以下语句导入基本库Foundation

import Foundation 

十一、练习

最后,本文以一个练习题结尾,帮助读者复习Swift知识。

题目

学校举行一场考试,考不定项选择题,每题都有一个或多个正确答案,且每题分数一样,满分100分。评分标准:全部选对得全部分,选对但不全得一半分,但如果不答,或选了错误答案,该题不得分。

请设计一个函数,输入各题的正确答案,以及学生给出的答案,对该生进行评分,输出总分。

例如,考试有5道题,每题20分,正确答案分别为:AB,ACD,B,ABC,BD。学生给的答案是B,ACD,AB,ABC,BC。则学生5道题的得分分别为10,20,0,20,0,所以总分为50。

参考答案

func mark(correctAns: [String], studentAns:[String])->Float{
    var res:Float = 0.0
    var numOfQs = correctAns.count
    var markEachQ:Float = 100.0 / Float(numOfQs)
    for qIdx in 0...(numOfQs-1){
        if qIdx > studentAns.count - 1{
            break
        }
        var correctA = Set<Character>()
        for c in correctAns[qIdx]{
            correctA.insert(c)
        }
        var studentA = Set<Character>()
        for c in studentAns[qIdx]{
            studentA.insert(c)
        }
        if (correctA == studentA){
            res += markEachQ
        }
        else if (studentA.isSubset(of: correctA) && studentA.isEmpty == false){
            res += markEachQ / 2
        }
    }
    return res
}

var Exam1:[String:[String]] = [String:[String]]()
Exam1["correct"] = ["ABC", "BD","AB","D","BC"]
Exam1["student"] = ["BC", "BD", "AC","AD","C"]
var Exam2:[String:[String]] = [String:[String]]()
Exam2["correct"] = ["ACD", "B","ABCD","ACD","A"]
Exam2["student"] = ["ACD", "B", "AC","AD","A"]
var Exam3:[String:[String]] = [String:[String]]()
Exam3["correct"] = ["AB", "AD","C","AD","D"]
Exam3["student"] = ["AB", "D", "C","","D"]
var Exam4:[String:[String]] = [String:[String]]()
Exam4["correct"] = ["BD", "AD","D","CD","ABCD"]
Exam4["student"] = ["BD", "A", "D"]
print("Result of task 3:")
print("The mark of Exam1 is \(mark(correctAns: Exam1["correct"]!, studentAns: Exam1["student"]!))")
print("The mark of Exam2 is \(mark(correctAns: Exam2["correct"]!, studentAns: Exam2["student"]!))")
print("The mark of Exam3 is \(mark(correctAns: Exam3["correct"]!, studentAns: Exam3["student"]!))")
print("The mark of Exam4 is \(mark(correctAns: Exam4["correct"]!, studentAns: Exam4["student"]!))")

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值