Swift2.1-下标脚本下标脚本

官方文档

下标脚本

类,结构体和枚举可以定义下标脚本,下标脚本可以认为是访问集合(collection),列表或序列的成员元素。你可是使用下标脚本来设置或通过索引检索值,而不需要调用实例特定的方法。例如,用下标脚本来访问一个数组(Array)实例中的元素,可以写成这样someArray[index],访问字典(Dictionary)实例中的元素可以写为someDictionary[key]

你可以为一个类型定义多个下标脚本,并且合适的下标脚本通过重载来使用,基于你传递的下表脚本的索引值类型。下标脚本没有限制单个纬度,并且你可以使用多个输入形参来定义下表脚本满足自定义类型的需求。

下标脚本的语法

下表脚本允许你通过在实例名后面的方括号传入一个或多个索引值对该实例进行访问和赋值。语法类似于实例方法和和计算属性的混合。写下标定义的时候要带上关键字subscript,并且指定一个或多个输入参数和返回值类型。不像实例方法,下标脚本只能是读写或者只读的。此行为有点像点计算属性的getter和setter:

subscript(index: Int) -> Int {
    get {
        // return an appropriate subscript value here
    }
    set(newValue) {
        // perform a suitable setting action here
    }
}

newValue的类型和下标脚本的返回值一样正如计算属性,你可以选择不去指定setter的(newValue)形参。如果你没有提供一个默认形参,那么你可以调用newValue

与只读计算属性一样,你可以给下标脚本使用get关键字:

subscipt(index: Int) -> {
    // return an appropriate subscript value here
}

下面是下标脚本的只读属性实现的过程的,定义了一个TimeTable来代表整数的n倍:

struct TimeTable {
    let multiplier: Int
    subscript(index: Int) -> Int {
        return multiplier * index
    }
}
let threeTimesTable = TimesTable(multiplier: 3)
print("six times three is \(threeTimesTable[6])")
// prints "six times three is 18"

在这个?中,创建了一个新的TimeTable实例来表示索引值的3倍。数值3作为结构体构造函数入参初始化实例成员multiplier

你可以通过下标脚本来得到结果,比如threeTimesTable[6]。这条语句访问了threeTimesTable的第六个元素,返回63倍即18

注意

TimesTable例子是基于一个固定的数学公式。它并不适合对threeTimesTable[someIndex]进行赋值操作,这也是为什么附属脚本只定义为只读的原因。

下标脚本用法

"下标脚本"确切的意思取决于它使用的上下文。通常下标脚本是用来访问集合(collection),列表(list)或序列(sequence)中元素的快捷方式。你可以在你自己特定的类或结构体中自由实现下标脚本来提供合适的功能。

例如,Swift的字典(Dictionary)类型实现了通过下标脚本来对其实例中存放的值进行存取操作。在下标脚本中使用和字典索引相同类型的值,并且把一个字典值类型的值赋值给这个下标脚本来为字典设值:

var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
numberOfLegs["bird"] = 2

上面的?定义了一个名为numberOfLegs的变量并用一个字典字面量初始化出了包含三对键值的字典实例。numberOfLegs的字典存放值类型推断为[String:Int]。字典实例创建完成之后通过下标脚本的方式将整型值2赋值到字典实例的索引为bird的位置中。

更多关于字典(Dictionary)下标脚本的信息请参考读取和修改字典

注意 Swift中字典的附属脚本实现中,在get部分返回值是Int?,上例中的numberOfLegs字典通过附属脚本返回的是一个Int?或者说“可选的int”,不是每个字典的索引都能得到一个整型值,对于没有设过值的索引的访问返回的结果就是nil;同样想要从字典实例中删除某个索引下的值也只需要给这个索引赋值为nil即可。

下标脚本选项

下标脚本允许任意数量的输入形参索引,并且这些输入形参可以是任何类型。下标脚本也可以返回任何类型。下标脚本可以使用变量参数和可变参数,但是不能使用输入输出(in-out)参数或提供默认的参数值。

一个类或结构体可以根据自身需要提供多个下标脚本实现,在定义下标脚本时通过传入参数的类型进行区分,使用下标脚本时会自动匹配合适的下标脚本实现运行,这就是下标脚本的重载

一个下标脚本参数是最常见的情况,但只要有合适的场景也可以定义多个下标脚本参数。如下例定义了一个Matrix结构体,将呈现一个Double类型的二维矩阵。Matrix结构体的下标脚本需要两个整型参数:

struct Martix {
    let rows: Int, columns: Int
    var grid: [Double]
    init(rows: Int, columns: Int) {
    self.rows = rows
    self.columns = columns
    grid = Array(count: rows * cloumns, repeatedValue: 0.0)
    }
    func indexIsValidForRow(row: Int, column: Int) -> Bool {
        return row >= 0 && row < rows && cloumn >= 0 && cloumn < cloumns
    }
    subscript(row: Int, column: Int) -> Double {
        get {
            assert(indexIsValidForRow(row, cloumn: 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提供了一个有两个形参的构造方法,分别是rowscolumns,创建了一个足够容纳rows * columns个数的Double类型数组。通过传入数组长度和初始值0。0到数组的一个构造器,将Matrix中每个元素初始值0。0。关于数组的构造方法和析构方法请参考创建一个空数组

你可以通过传入合适的rowcolumn的数量来构造一个新的Matrix实例:

var matrix = Matrix(rows: 2, columns: 2)

上例中创建了一个新的两行两列的Matrix实例。在阅读顺序从左上到右下的Matrix实例中的数组实例grid是矩阵二维数组的扁平化存储:

grid

将值赋给带有rowcolumn下标脚本的matrix实例表达式可以完成赋值操作,下标脚本入参使用逗号分割:

matrix[0, 1] = 1.5
matrix[1, 0] = 3.2

上面两条语句分别让matrix的右上值为 1.5,坐下值为 3.2:

Matrix下标脚本的gettersetter中同时调用了下标脚本入参的rowcolumn是否有效的判断。为了方便进行断言,Matrix包含了一个名为indexIsValidForRow(_:column:)的成员方法,用来确认入参的rowcolumn值是否会造成数组越界:

func indexIsValidForRow(row: Int, column: Int) -> Bool {
    return row >= 0 && row < rows && column >= 0 && column < columns
}

断言在下标脚本越界时触发:

let someValue = matrix[2, 2]
// this triggers an assert, because [2, 2] is outside of the matrix bounds

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏菜鸟计划

javascript 变量、作用域和内存问题

一、基本类型和引用类型的值   1.基本类型和引用类型的值  基本类型值:指那些保存在栈内存中的简单数据,即这种值完全保存在内存中的一个位置,他们所占据的空间大...

3708
来自专栏企鹅号快讯

如何写好python代码

写代码好比画画,好的代码就像一件艺术品,美观、可读性高,让人看着舒服。代码是写给人看的,不是写给机器看的,遵守一定的代码规范很重要,就像写作文需要总分总结构,这...

3957
来自专栏Python小屋

详解Python序列解包

序列解包(Sequence Unpacking)是Python中非常重要和常用的一个功能,可以使用非常简洁的形式完成复杂的功能,大幅度提高了代码的可读性,减少了...

3387
来自专栏Pythonista

__slots__(面向对象进阶)

1014
来自专栏零基础使用Django2.0.1打造在线教育网站

关于JAVA你必须知道的那些事(二):封装

时隔近一年,我突然想起来这个文章还没有发完,所以就继续开始写。也不知道自己上次写到哪里了,不管了这里从面向对象的三个特性说起。

761
来自专栏开源优测

[接口测试 - 基础篇] 04 无法绕过的json解析

概述 我们先看一下什么是json。 JSON(JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交...

2948
来自专栏技术博客

C#项目代码规范

   小菜就是小菜,几个人搞出来的项目,让公司大牛稍微看了下,最后送出了惨不忍睹四个字。命名各种各样,五花八门,大写英文、小写英文、大写拼音、小写拼音、英文和拼...

1234
来自专栏ShaoYL

深刻理解----修饰变量----关键字

34911
来自专栏恰同学骚年

你必须知道的指针基础-2.指针的声明和使用及数组和指针的关系

  At first,计算机中绝大部分数据都放到内存中的,不同的数据放到不同的内存区域中。But,内存角度没有数据类型,只有二进制;数据以字节(8位二进制)为单...

431
来自专栏Java开发者杂谈

Ruby(1):入门

安装:   一般来说linux会自动装ruby,可以通过:   ruby -v 来查看ruby版本   直接使用ruby命令的话,是用来执行ruby文件的。如果...

3578

扫码关注云+社区