数组是一个很有用的数据结构,很多程序都建立在数组之上,我很少看到哪个应用程序不用到数组的。
而我们的俄罗斯方块显然也需要数组,而且是更为特殊的 二维数组。
为什么是二维数组呢?其实我们的整个游戏区域,可以看做是一个二维数组区域,就像这样:
我们的每一个block都占据在这样一个20x10的区域之内,20行,10列的一个200个block的二维数组让我们可以根据(x,y)坐标来确定block的位置,而这些blocks其实就是组成我们的L形,或者Z形 等等不同但我们都熟知的俄罗斯方块;
swift提供了我们array[index]供我们使用,但是我们还需要一个自定义的array2D[x,y]来更方便我们的使用,所以,让我们来自定义属于自己的二维数组吧!
确保你选中Swiftris文件夹,然后你可以选择? + N 或者 File > New > File…或者左下角的“+”号
> File 来创建新的swift类,如下图所示:
选择Swift然后点击Next, 给你的新类起个名字吧,我们叫做Array2D,意为二维数组,然后我们点击create
在编辑区域会自动打开我们刚刚建好的Array2D 类,我们照着下图进行修改
准备好了,虽然这段代码不长,但是如果你是个swift的新手,这里面却有很多学问,基本上每行都有一个知识点需要学习!
友情提醒:
-号的红色代码需要移除,+号的绿色代码需要手动加上,注释掉的#1,2,3,4
是便于给大家分段讲解,大家输入的时候可以不输入
subscript函数里面 array[(row * columns) + column] , 注意是row 而不是rows。如果你不小心写成了rows,那很显然,你的数组会越界。刚开始的时候我没有注意,就导致数组越界了。
#1
这里我们命名了一个叫做Array2D的class,在swift里面通常array是用struct 而不是用class,但是这里我们却需要一个class,因为在我们的程序里面,我们需要传引用(pass by reference),而不是传值(pass by value),class是传引用的,而struct是传值的。
关于传值和传引用的区别如果学过C++的应该不难理解,我记得Effective c++这本书里面对这部分有很详尽的解释,google一下应该也能找到很多答案,这里我就不展开描述了。
另外我们还看到,我们的class类型是<T>, 这里如果学过c++的应该也很容易理解,其实就是模板类了,T表示任意类型,可以是int,可以是string,可以是char等等等等;就是说,我们的这个array2D是一个通用的二维数组,你想在数组里面存任何都是可以的
#2
首先我们定义了一个传统的swift array,数组里面的类型和我们的二维数组类型一样 是<T>,但是我们注意到其实这里是<T?>,多了一个?
上一章节我们已经介绍过了,它表示这是一个optional的变量,也就是说可以是nil,可以不包含任何数据,而在我们的面板上,如果数组里面是ni就表示这个地方不显示任何的block
接下来是定义我们自己的init函数,init一个二维数组需要两个参数,行数和列数,前两行代码很简单,把形参中的值传给实例化后的类的两个私有变量,而用来存储数据的数组,就得用到swift原生的array类来建立了。
#3
接下来是定义我们自己的init函数,init一个二维数组需要两个参数,行数和列数,前两行代码很简单,把形参中的值传给实例化后的类的两个私有变量,而用来存储数据的数组,就得用到swift原生的array类来建立了。这里重点讲解一下关于swift array的 init函数
init(cout: repeatedValue: )
先来看一下官方文档里面的描述:
init(count:repeatedValue:) Constructs an array with a given number of elements, each initialized to the same value. Declaration init(count: Int, repeatedValue: T) Discussion The resulting array will have count elements in it, each initialized to the same value provided as the value for repeatedValue. For example: let numericArray = Array(count: 3, repeatedValue: 42) // numericArray is [42, 42, 42] let stringArray = Array(count: 2, repeatedValue: "Hello") // stringArray is ["Hello", "Hello"]
从举的两个例子来说,很容易理解了:count表示数组的大小,在我们的程序里面就是定义的rows * columns,在具体点就是20 * 10=200;
repeatedValue就表示初始化的值,这里表示所有的值都是一样的,当我第一次看到这个函数,因为教程里面写到的repeatedValue是nil,我以为是是否允许数组有重复的值呢。知道详细查阅了swift的官方文档后,才正确理解了这个函数的意思。
提醒大家:千万不能想当然,不能眼高手低,不清楚的事,一定要翻阅下资料弄清楚了。
#4
这里我们其实是定义了二维数组的查找符号。swift的array里面 这样的定义其实就是array[index] 中的[], 同理,因为我们的二维数组也需要用到符号,来找到指定位置的内容,因为同时需要两个参数,row和column,所以,这个也需要我们自己来定义,那么我们来详细看一下这个subscript函数吧。
这里可能你有会感到疑虑,-> 又是个什么意思? 其实swift是个尽量把语言描述成我们看到它时想到的意思,比如前面的?和!,一个变量后面加上?就好像在问他,你是不是optional的啊?
!就好像在说,你不是一个optional!
这里的-> 其实就是这样的意思,->前面输入一些参数,然后->后面会返回一个什么类型的东西。那这个函数的意思就是,你给我输入两个参数,一个是column,一个是row,然后经过函数的计算以后,我返回给你一个T类型的值,当然,我也可能返回给你一个空,因为T是optional的
还不是很明白,没关系,慢慢来,我们来看看官方文档里面,对subscript函数的描述,请打开这个文档
Classes, structures, and enumerations can define subscripts, which are shortcuts for accessing the member elements of a collection, list, or sequence. You use subscripts to set and retrieve values by index without needing separate methods for setting and retrieval. For example, you access elements in an Array instance as someArray[index] and elements in a Dictionary instance as someDictionary[key].
Subscripts are not limited to a single dimension, and you can define subscripts with multiple input parameters to suit your custom type’s needs
看,我们就是定义了两个输入参数
Unlike instance methods, subscripts can be read-write or read-only.
也就是说你可以定义subscript函数为读写或者只读模式,我们看到的同时有set和get的是读写模式,如果想要只读模式的,不写get关键字就是了:
As with read-only computed properties, you can drop the get keyword for read-only subscripts: subscript(index: Int) -> Int { // return an appropriate subscript value here }
我们继续来看几个用到subscript函数的例子,其实定义是这样定义, 使用的时候,和普通的数组usage一样,一对“[]”就可以了
结构体
[plain] view
plaincopy
- struct TimesTable {
- let multiplier: Int
- subscript(index: Int) -> Int {
- return multiplier * index
- }
- }
- let threeTimesTable = TimesTable(multiplier: 3)
- println("six times three is \(threeTimesTable[6])")
- // prints "six times three is 18"
结构体的subscript函数就是返回输入的参数乘以结构体里的私有变量,所以也很容易理解举得例子的值了。
字典
[plain] view
plaincopy
- var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
- numberOfLegs["bird"] = 2
最后再来看一下官方文档里面给出的二维数组的源代码,是不是和教程里面的很像呢?
[plain] view
plaincopy
- struct Matrix {
- let rows: Int, columns: Int
- var grid: [Double]
- init(rows: Int, columns: Int) {
- self.rows = rows
- self.columns = columns
- grid = Array(count: rows * columns, repeatedValue: 0.0)
- }
- func indexIsValidForRow(row: Int, column: Int) -> Bool {
- return row >= 0 && row < rows && column >= 0 && column < columns
- }
- subscript(row: Int, column: Int) -> Double {
- get {
- assert(indexIsValidForRow(row, column: column), "Index out of range")
- return grid[(row * columns) + column]
- }
- set {
- assert(indexIsValidForRow(row, column: column), "Index out of range")
- grid[(row * columns) + column] = newValue
- }
- }
- }
只不过这里的Matrix返回的是double类型的数,而我们的教程里面用到是模板T而已。看到这里应该不用再多解释,关于二维数组的所有内容都一目了然吧?