Swift探索(五): Enum & Optional

一:枚舉(Enum)

1. 枚舉的基本用法

Swift 中通過 enum 關(guān)鍵字來聲明一個枚舉

enum LJLEnum {
    case test_one
    case test_two
    case test_three
}

Swift 中的枚舉則更加靈活架馋,并且不需給枚舉中的每一個成員都提供值绳锅。如果一個值(所謂“原始”值)要被提供給每一個枚舉成員,那么這個值可以是字符串、字符欲逃、任意的整數(shù)值,或者是浮點類型饼暑。

enum Color: String {
    case red = "Red"
    case blue = "Blue"
    case green = "Grren"
}

enum LJLEnum: Double {
    case a = 10.0
    case b = 22.2
    case c = 33.3
    case d = 44.4
}

2. 原始值 RawValue

隱士 RawValue 是建立在 Swift 的類型判斷機制上的

enum LJLEnum: Int {
    case one, two, three = 10, four, five
}
print(LJLEnum.one.rawValue)
print(LJLEnum.two.rawValue)
print(LJLEnum.three.rawValue)
print(LJLEnum.four.rawValue)
print(LJLEnum.five.rawValue)
// 打印結(jié)果
0
1
10
11
12

可以看到 RawValue 原始值跟 OC 一樣都是從 0 稳析、 1 洗做、2 開始,當指定值時彰居,后面的枚舉值的 RawValue 會在當前值的基礎(chǔ)上進行累加操作诚纸,因此 fourfive 的值為 1112

將枚舉類型改成 String 類型

enum LJLEnum: String {
    case one, two, three = "Hello World", four, five
}
print(LJLEnum.one.rawValue)
print(LJLEnum.two.rawValue)
print(LJLEnum.three.rawValue)
print(LJLEnum.four.rawValue)
print(LJLEnum.five.rawValue)

// 打印結(jié)果
one
two
Hello World
four
five

可以看出系統(tǒng)已經(jīng)默認給了每一個枚舉成員分配了一個字符串陈惰,并且該字符串與枚舉成員值的字符串一致畦徘。
將上述代碼簡化一下,通過 LLDB 命令 swiftc xxx.swift -emit-sil 編譯成 sil 文件

enum LJLEnum: String {
    case one, two = "Hello World", three
}
var x = LJLEnum.one.rawValue

sil 文件代碼

// 枚舉的聲明
enum LJLEnum : String {
  case one, two, three
  init?(rawValue: String)
  typealias RawValue = String
  var rawValue: String { get }
}

// LJLEnum.rawValue.getter
sil hidden @$s4main7LJLEnumO8rawValueSSvg : $@convention(method) (LJLEnum) -> @owned String {
// %0 "self"                                      // users: %2, %1
bb0(%0 : $LJLEnum):
  debug_value %0 : $LJLEnum, let, name "self", argno 1 // id: %1
  switch_enum %0 : $LJLEnum, case #LJLEnum.one!enumelt: bb1, case #LJLEnum.two!enumelt: bb2, case #LJLEnum.three!enumelt: bb3 // id: %2

bb1:                                              // Preds: bb0
  %3 = string_literal utf8 "one"                  // user: %8
  %4 = integer_literal $Builtin.Word, 3           // user: %8
  %5 = integer_literal $Builtin.Int1, -1          // user: %8
  %6 = metatype $@thin String.Type                // user: %8
  // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:)
  %7 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %8
  %8 = apply %7(%3, %4, %5, %6) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %9
  br bb4(%8 : $String)                            // id: %9

bb2:                                              // Preds: bb0
  %10 = string_literal utf8 "Hello World"         // user: %15
  %11 = integer_literal $Builtin.Word, 11         // user: %15
  %12 = integer_literal $Builtin.Int1, -1         // user: %15
  %13 = metatype $@thin String.Type               // user: %15
  // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:)
  %14 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %15
  %15 = apply %14(%10, %11, %12, %13) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %16
  br bb4(%15 : $String)                           // id: %16

bb3:                                              // Preds: bb0
  %17 = string_literal utf8 "three"               // user: %22
  %18 = integer_literal $Builtin.Word, 5          // user: %22
  %19 = integer_literal $Builtin.Int1, -1         // user: %22
  %20 = metatype $@thin String.Type               // user: %22
  // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:)
  %21 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %22
  %22 = apply %21(%17, %18, %19, %20) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %23
  br bb4(%22 : $String)                           // id: %23

// %24                                            // user: %25
bb4(%24 : $String):                               // Preds: bb3 bb2 bb1
  return %24 : $String                            // id: %25
} // end sil function '$s4main7LJLEnumO8rawValueSSvg'

我們可以看到 rawValue.getter 函數(shù)的調(diào)用奴潘,根據(jù)傳進來的枚舉成員值旧烧,通過模式匹配的方式走到不同的代碼分支,在不同的代碼分支中把不同的字符串給到當前對應的代碼分支返回值画髓。

3. 關(guān)聯(lián)值

用枚舉表達更復雜的情況掘剪,

enum Shape {
    case circle(radius: Double)
    case rectangle(width: Int, height: Int)
}

var circle = Shape.circle(radius: 10.0)
var square = Shape.rectangle(width: 5, height: 5)

4. 模式匹配

enum Week: String {
    case MONDAY
    case TUEDAY
    case WEDDAY
    case THUDAY
    case FRIDAY
    case SATDAY
    case SUNDAY
}

通過 Switch 關(guān)鍵字進行匹配

let currentWeek: Week
switch currentWeek {
    case .MONDAY: print(Week.MONDAY.rawValue)
    case .TUEDAY: print(Week.TUEDAY.rawValue)
    case .WEDDAY: print(Week.WEDDAY.rawValue)
    case .THUDAY: print(Week.THUDAY.rawValue)
    case .FRIDAY: print(Week.FRIDAY.rawValue)
    case .SUNDAY: print(Week.SUNDAY.rawValue)
    case .SATDAY: print(Week.SUNDAY.rawValue)
}

如果不想匹配所有的 case ,使用 defalut 關(guān)鍵字

let currentWeek: Week = Week.MONDAY
switch currentWeak {
    case .SATDAY, .SUNDAY: print("Happy Day")
    default : print("SAD DAY")
}

匹配關(guān)聯(lián)值

enum Shape {
    case circle(radius: Double)
    case rectangle(width: Int, height: Int)
}

let shape = Shape.circle(radius: 10.0)

// 方式一:
switch shape {
    case let .circle(radius):
        print("Circle radius:\(radius)")
    case let .rectangle(width, height):
        print("rectangle width:\(width), height\(height)")
}

// 方式二:
switch shape {
    case .circle(let radius):
        print("Circle radius:\(radius)")
    case .rectangle(let width, let height):
        print("rectangle width:\(width), height\(height)")
}

5. 枚舉的大小

5.1 沒有關(guān)聯(lián)值枚舉(No-payload enums)
enum Week: String {
    case MONDAY
    case TUEDAY
    case WEDDAY
    case THUDAY
    case FRIDAY
    case SATDAY
    case SUNDAY
}

print(MemoryLayout<Week>.size)
print(MemoryLayout<Week>.stride)

// 打印結(jié)果
1
1

可以看出不管是大小( size ) 還是步長 stride 都是 1 奈虾,在 Swift 中進行枚舉布局的時候一直是嘗試使用最少的空間來存儲 enum 夺谁,對于當前的 case 數(shù)量來說, UInt8 能夠表示 256 cases 肉微,也就意味著如果一個默認枚舉類型且沒有關(guān)聯(lián)值的 case 少于 256 匾鸥,當前枚舉類型的大小都是 1 字節(jié)。

No-payload enum.png

通過上面的打印我們可以看到碉纳,當前變量 a 勿负, bc 三個變量的地址相差 1 位劳曹,并且存儲的內(nèi)容分別是 00 奴愉, 0102 铁孵,這與我們上面說的布局理解是一致的

5.2 單個關(guān)聯(lián)值枚舉(Single-payload enums)
enum LJLBoolEnum {
    case one(Bool)
    case two
    case three
    case four
}

enum LJLIntEnum {
    case one(Int)
    case two
    case three
    case four
}

print("BoolEnum.Size:\(MemoryLayout<LJLBoolEnum>.size)")
print("BoolEnum.stride:\(MemoryLayout<LJLBoolEnum>.stride)")

print("IntEnum.Size:\(MemoryLayout<LJLIntEnum>.size)")
print("IntEnum.stride:\(MemoryLayout<LJLIntEnum>.stride)")
打印結(jié)果.png

Swift 中的 enum 中的 Single-payload enums 會使用負載類型中的額外空間來記錄沒有負載的 case 值锭硼。比如這里的 BoolEnum ,首先 Bool 類型是 1 字節(jié)蜕劝,也就是 UInt8 檀头,所以當前能表達 256case 的情況,對于 Bool 類型來說岖沛,只需要使用低位的 0 暑始, 1 這兩種情況,其他剩余的空間就可以用來表示沒有負載的 case 值婴削。

LJLBoolEnum.png

可以看到不同的 case 值確實是按照我們在開始得出來的那個結(jié)論進行布局的蒋荚。
對于 Int 類型的負載來說,其實系統(tǒng)是沒有辦法推算當前的負載所要使用的位數(shù)馆蠕,也就意味著當前 Int 類型的負載是沒有額外的剩余空間的期升,這個時候我們就需要額外開辟內(nèi)存空間來去存儲我們的 case 值,也就是 8 + 1 = 9 字節(jié)互躬。
LJLIntEnum.png

可以看出變量 a 播赁、 bc 吼渡、 d 容为、 e 的地址相差 16 位,這和上面打印的步長信息相一致寺酪。

5.3 多個關(guān)聯(lián)值枚舉(Mutil-payload enums)
enum LJLDoubleBoolEnum {
    case one(Bool)
    case two(Bool)
    case three
    case four
}

enum LJLDoubleIntEnum {
    case one(Int)
    case two(Int)
    case three
    case four
}

print("DoubleBoolEnum.Size:\(MemoryLayout<LJLDoubleBoolEnum>.size)")
print("DoubleBoolEnum.stride:\(MemoryLayout<LJLDoubleBoolEnum>.stride)")

print("DoubleIntEnum.Size:\(MemoryLayout<LJLDoubleIntEnum>.size)")
print("DoubleIntEnum.stride:\(MemoryLayout<LJLDoubleIntEnum>.stride)")

打印結(jié)果

打印結(jié)果.png

我們可以看到兩個 Bool 關(guān)聯(lián)值的枚舉的大小為 1坎背,根據(jù)上面單個關(guān)聯(lián)值枚舉的所述不難理解。對于兩個 Int 關(guān)聯(lián)值的枚舉的大小為 9寄雀,是因為創(chuàng)建一個枚舉值時有且只有一個關(guān)聯(lián)值得滤,但是還需要 1 個字節(jié)去存儲其他 case 枚舉值( threefour ) 所以當前只需要 8 字節(jié) + 1 字節(jié)
LJLDoubleBoolEnum.png

我們可以看到當前內(nèi)存存儲的分別是 0001 盒犹、 40 懂更、 4180 急膀、 81 沮协,這里在存儲當前的 case 時候會使用到 common spare bits,首先 bool 類型需要 1 字節(jié)卓嫂,也就是 8 位慷暂,對于 bool 類型來說,我們存儲的無非就是 01 晨雳,只需要用到 1 位行瑞,所以剩余的 7 位,這里我們都統(tǒng)稱為 common spare bits 悍募,對于當前的 case 數(shù)量來說我們完全可以把所有的情況放到 common spare bits 所以這里只需要 1 字節(jié)就可以存儲所有的內(nèi)容了蘑辑。
對于 0001 坠宴、 40 洋魂、 4180 喜鼓、 81 副砍,其中 04 庄岖、 8 稱之為 tag value豁翎, 01 稱之為 tag index隅忿。

一般來說心剥,有多個負載的枚舉時邦尊,當前枚舉類型的大小取決于當前最大關(guān)聯(lián)值的大小。

enum LJLEnum{
    case one(Bool)
    case two(Int)
    case three
    case four
}

print("LJLEnum.Size:\(MemoryLayout<LJLEnum>.size)")

// 打印結(jié)果
LJLEnum.Size:9

當前 LJLEnum 的大小就等于 sizeof(Int) + sizeof(rawVlaue) = 9

enum LJLEnum{
    case one(Bool)
    case two(Int, Bool, Int)
    case three
    case four
}

print("LJLEnum.Size:\(MemoryLayout<LJLEnum>.size)")
// 打印結(jié)果 
LJLEnum.Size:24

這里為什么不是 sizeof(Int) * 2 + sizeof(rawVlaue) = 17 呢优烧?對于 two (Int, Bool, Int) 類型的由于字節(jié)對齊的原因所以它的存儲大小為 8 * 3 = 24 蝉揍,又由于中間的 Bool 實際值占用 1 個字節(jié)因此中間 8 字節(jié)還有剩余控件去存儲其他的 case 值。所以這里是 24 字節(jié)畦娄。那么將 bool 放在后面呢又沾?我們試一試

enum LJLEnum{
    case one(Bool)
    case two(Int, Int, Bool)
    case three
    case four
}

print("LJLEnum.Size:\(MemoryLayout<LJLEnum>.size)")
print("LJLEnum.Stride:\(MemoryLayout<LJLEnum>.stride)")

打印結(jié)果.png

可以看到大小為 17,其實也不難理解熙卡,bool 放后面那么它的 1 字節(jié)就能放下其他的 case杖刷,那為什么這里不字節(jié)對齊呢?其實是因為最后會因為步長 24 的原因會對這里進行補齊驳癌。

注意對于只有一個 case 的枚舉滑燃,不需要用任何東?來去區(qū)分當前的 case ,所以它的大小是 0喂柒。

enum LJLEnum{
    case one
}
print("LJLEnum.Size:\(MemoryLayout<LJLEnum>.size)")

// 打印結(jié)果
LJLEnum.Size:0

6. 遞歸枚舉

遞歸枚舉是一種枚舉類型不瓶,它有一個或多個枚舉成員使用該枚舉類型的實例作為關(guān)聯(lián)值。使用遞歸枚舉時灾杰,編譯器會插入一個間接層蚊丐。你可以在枚舉成員前加上 indirect 來表示該成員可遞歸。

enum BinaryTree<T> {
    case empty
    indirect case node(left: BinaryTree, value: T, right: BinaryTree)
}

var node = BinaryTree<Int>.node(left: BinaryTree<Int>.empty, value: 10, right: BinaryTree<Int>.empty)

也可以在枚舉類型開頭加上 indirect 關(guān)鍵字來表明它的所有成員都是可遞歸的艳吠。

indirect enum BinaryTree<T> {
    case empty
    case node(left: BinaryTree, value: T, right: BinaryTree)
}

var node = BinaryTree<Int>.node(left: BinaryTree<Int>.empty, value: 10, right: BinaryTree<Int>.empty)

在匯編中查看

匯編中查看.png

可以發(fā)現(xiàn)這里調(diào)用了 swift_allocObject 麦备, 在之前的文章 Swift探索(一): 類與結(jié)構(gòu)體(上) 中我們就探討過, swift_allocObject 就是在堆空間中分配內(nèi)存空間昭娩。對于 indirect 關(guān)鍵字放在 enum 前面也就意味著當前這個 enum 的大小都是用引用類型在堆空間中存儲凛篙。當 indirect 關(guān)鍵字放在 case 前面,那么就只有這個 case note 是存儲在堆空間中栏渺,其中 case empty 是存儲在 __DATA.__common(存儲沒有初始化過的符號聲明)的 section

二:可選值(Optional)

1. 什么是可選值

class Person {
    var age: Int?
    var name: Optional<String> = nil
}

agename 我們就稱之為可選值

2.可選值的本質(zhì)

swift源碼.png

Swift 源碼中可以發(fā)現(xiàn)可選值實際上就是一個枚舉呛梆,并且有兩個 case 一個 none ,一個 some

3. 可選值的基本使用

func getOddValue(_ value: Int) -> Int? {
    if value % 2 == 0 {
        return .some(value)
    } else {
        return .none
    }
}

var array = [1, 2, 3, 4, 5, 6]
for element in array {
    let value = getOddValue(element)
    switch value {
        case .some(let value):
            array.remove(at: array.firstIndex(of: value)!)
        case .none:
            print("vlaue not exist")
    }
}

如果每一個可選值都用模式匹配的方式來獲取值在代碼書寫上就比較繁瑣磕诊,還可以使用 if let 的方式來進行可選值綁定

func getOddValue(_ value: Int) -> Int? {
    if value % 2 == 0 {
        return .some(value)
    } else {
        return .none
    }
}

var array = [1, 2, 3, 4, 5, 6]
for element in array {
    if let value = getOddValue(element) {
        array.remove(at: array.firstIndex(of: value)!)
    }
}

除了使用 if let 還可以使用 gurad let填物,和 if let 剛好相反,gurad let 守護一定有值霎终。如果沒有滞磺,直接返回。 通常判斷是否有值之后莱褒,會做具體的邏輯實現(xiàn)击困,通常代碼多如果用 if let 憑空多了一層分支, gurad let 是降低分支層次的辦法

var name: String?
var age: Int?
var height: Double?

func testIfLet() {
    if let name1 = name {
        if let age1 = age {
            if let height1 = height {
                print("姓名: \(name1), 年齡:\(age1), 身高:\(height1)cm")
            } else {
                print("height 為空")
            }
        } else {
            print("age 為空")
        }
    } else {
        print("name 為空")
    }
}

func testGuardLet() {
    guard let name1 = name else {
        print("name 為空")
        return 
    }
    
    guard let age1 = age else {
        print("age 為空")
        return
    }

    guard let height1 = height else {
        print("height 為空")
        return
    }

    print("姓名: \(name1), 年齡:\(age1), 身高:\(height1)cm")
}

2. 可選鏈

OC 中我們給一個 nil 對象發(fā)送消息什么也不會發(fā)生广凸,但是在 Swift 中是沒有辦 法向一個 nil 對象直接發(fā)送消息阅茶,但是借助可選鏈可以達到類似的效果蛛枚。

let str: String? = "abc"
let upperStr = str?.uppercased() 
print(upperStr)

var str2: String?
let upperStr2 = str2?.uppercased()
print(upperStr2)

// 打印結(jié)果
Optional("ABC")
nil

同意可選鏈對數(shù)組和喜愛

var closure: ((Int) -> ())?
print(closure?(1)) // closure 為 nil 不執(zhí)行
let dict = ["one": 1, "two": 2]
print(dict["one"]) // Optional(1)
print(dict["three"]) // nil

// 打印結(jié)果
nil
Optional(1)
nil

3. ?? 運算符(空合并運算符)

( a ?? b ) 將對可選類型 a 進行空判斷,如果 a 包含一個值就進行解包脸哀,否則就返回一個默認值 b 坤候。

  • 表達式 a 必須是 Optional 類型
  • 默認值 b 的類型必須要和 a 存儲值的類型保持一致
var age: Int? = 10
var x = age ?? 0
print(x)

// 打印結(jié)果
10
?? 空運算符源碼.png

4. 運算符重載

源碼中我們可以看到除了重載了 ?? 運算符, Optional 類型還重載了 == 企蹭, ?= 等等運算符,實際開發(fā)中我們可以通過重載運算符簡化我們的表達式智末。

struct Vector {
    let x: Int
    let y: Int
}

extension Vector {
    static func + (fistVector: Vector, secondVector: Vector) -> Vector {
        return Vector(x: fistVector.x + secondVector.x, y: fistVector.y + secondVector.y)
    }
    static prefix func - (vector: Vector) -> Vector {
        return Vector(x: -vector.x, y: -vector.y)
    }
    static func - (fistVector: Vector, secondVector: Vector) -> Vector {
        return fistVector + -secondVector
    }
}

var x = Vector(x: 10, y: 20)
var y = Vector(x: 20, y: 30)
var z = x + y
print(z)

var w = -z
print(w)

// 打印結(jié)果
Vector(x: 30, y: 50)
Vector(x: -30, y: -50)

根據(jù)官方文檔創(chuàng)建一個自定義運算符 已有運算符

infix operator **: AdditionPrecedence
// 運算組名稱: LJLPrecedence 優(yōu)先級低于: AdditionPrecedence 結(jié)合方式: 左結(jié)合
precedencegroup LJLPrecedence {
    lowerThan: AdditionPrecedence
    associativity: left
}

struct Vector {
    let x: Int
    let y: Int
}

extension Vector {
    static func + (fistVector: Vector, secondVector: Vector) -> Vector {
        return Vector(x: fistVector.x + secondVector.x, y: fistVector.y + secondVector.y)
    }
    static prefix func - (vector: Vector) -> Vector {
        return Vector(x: -vector.x, y: -vector.y)
    }
    static func - (fistVector: Vector, secondVector: Vector) -> Vector {
        return fistVector + -secondVector
    }
    
    static func ** (fistVector: Vector, secondVector: Vector) -> Vector {
        return Vector(x: fistVector.x * secondVector.x, y: fistVector.y * secondVector.y)
    }

}

var x = Vector(x: 10, y: 20)
var y = Vector(x: 20, y: 30)
var z = x ** y
var w = x + y ** x
print(z)
print(w)

// 打印結(jié)果
Vector(x: 200, y: 600)
Vector(x: 300, y: 1000)

5. 隱士解析可選類型

隱式解析可選類型是可選類型的一種谅摄,使用的過程中和非可選類型無異。它們之間唯一的區(qū)別是系馆,隱式解析可選類型是你告訴對 Swift 編譯器送漠,在運行時訪問時,值不會為 nil由蘑。

var age: Int
var age1: Int!
var age2: Int?

let x = age1 % 2
let y = age2 % 2

隱式解析.png

其中 age1 不用做解包的操作闽寡,編譯器已經(jīng)做了

@IBOutlet weak var btn: UIButton!

IBOutlet 類型是 Xcode 強制為可選類型的,因為它不是在初始化時賦值的尼酿,而是在加載視圖的時候爷狈。可以把它設置為普通可選類型裳擎,但是如果這個視圖加載正確涎永,它是不會為空的。

6. 可選值有關(guān)的高階函數(shù)

  • map :這個方法接受一個閉包鹿响,如果可選值有內(nèi)容則調(diào)用這個閉包進行轉(zhuǎn)換
var dict = ["one": "1", "two": "2"]
let result = dict["one"].map{ Int($0) } 
print(result)

// 打印結(jié)果
Optional(Optional(1))

上面的代碼中我們從字典中取出字符串 1 羡微,并將其轉(zhuǎn)換為 Int 類型,但因為 String 轉(zhuǎn)換成 Int 不一定能成功惶我,所以返回的是 Int? 類型妈倔,而且字典通過鍵不一定能取得到值,所以 map 返回的也是一個 Optional 绸贡,所以最后上述代碼 result 的類型為 Int?? 類型盯蝴。

  • flatMap :可以把結(jié)果展平成為單個可選值
var dict = ["one": "1", "two": "2"]
let result = dict["one"].flatMap{ Int($0) } // Optional(1)
print(result)

// 打印結(jié)果
Optional(1)

注意這個方法是作用在 Optional 的方法,而不是作用在 Sequence 上的
作用在 Sequence 上的 flatMap 方法在 Swift4.1 中被更名為 compactMap 恃轩,該方法可以將序列中的 nil 過濾出去

let array = ["1", "2", "3", nil]
let result = array.compactMap{ $0 } // ["1", "2", "3"]
print(result)
let array1 = ["1", "2", "3", "four"]
let result1 = array1.compactMap{ Int($0) } // [1, 2, 3]
print(result1)

// 打印結(jié)果
["1", "2", "3"]
[1, 2, 3]
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末结洼,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子叉跛,更是在濱河造成了極大的恐慌松忍,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,378評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件筷厘,死亡現(xiàn)場離奇詭異鸣峭,居然都是意外死亡宏所,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,970評論 3 399
  • 文/潘曉璐 我一進店門摊溶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來爬骤,“玉大人,你說我怎么就攤上這事莫换∠夹” “怎么了?”我有些...
    開封第一講書人閱讀 168,983評論 0 362
  • 文/不壞的土叔 我叫張陵拉岁,是天一觀的道長坷剧。 經(jīng)常有香客問我,道長喊暖,這世上最難降的妖魔是什么馒稍? 我笑而不...
    開封第一講書人閱讀 59,938評論 1 299
  • 正文 為了忘掉前任兽愤,我火速辦了婚禮县匠,結(jié)果婚禮上肥缔,老公的妹妹穿的比我還像新娘。我一直安慰自己巩掺,他們只是感情好偏序,可當我...
    茶點故事閱讀 68,955評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著锌半,像睡著了一般禽车。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上刊殉,一...
    開封第一講書人閱讀 52,549評論 1 312
  • 那天殉摔,我揣著相機與錄音,去河邊找鬼记焊。 笑死逸月,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的遍膜。 我是一名探鬼主播碗硬,決...
    沈念sama閱讀 41,063評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼瓢颅!你這毒婦竟也來了恩尾?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,991評論 0 277
  • 序言:老撾萬榮一對情侶失蹤挽懦,失蹤者是張志新(化名)和其女友劉穎翰意,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,522評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡冀偶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,604評論 3 342
  • 正文 我和宋清朗相戀三年醒第,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片进鸠。...
    茶點故事閱讀 40,742評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡稠曼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出客年,到底是詐尸還是另有隱情霞幅,我是刑警寧澤,帶...
    沈念sama閱讀 36,413評論 5 351
  • 正文 年R本政府宣布量瓜,位于F島的核電站蝗岖,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏榔至。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,094評論 3 335
  • 文/蒙蒙 一欺劳、第九天 我趴在偏房一處隱蔽的房頂上張望唧取。 院中可真熱鬧,春花似錦划提、人聲如沸枫弟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,572評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽淡诗。三九已至,卻和暖如春伊履,著一層夾襖步出監(jiān)牢的瞬間韩容,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,671評論 1 274
  • 我被黑心中介騙來泰國打工唐瀑, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留群凶,地道東北人。 一個月前我還...
    沈念sama閱讀 49,159評論 3 378
  • 正文 我出身青樓哄辣,卻偏偏與公主長得像请梢,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子力穗,可洞房花燭夜當晚...
    茶點故事閱讀 45,747評論 2 361

推薦閱讀更多精彩內(nèi)容