Swift底层进阶--018:高阶函数

Swift是一门面向协议的语言,开发中我们已经充分享受到面向协议编程的便利,但Swift相比OC还有一个更重要的优势,那就是对函数式编程提供了强大的支持。

其中Swift提供的高阶函数,常常用来作用于ArraySetDictionary中的每一个元素,它的本质也是函数,具有以下有两个特点:

  • 接受函数或者是闭包作为参数
  • 返回值是一个函数或者是闭包
map函数

map函数作用于Collection中的每一个元素,然后返回一个新的Collection

假设一个String类型的Array,要求将集合中的每一个元素都转为小写。常规代码会这样写:

let strings = ["HANK", "CAT", "Kody", "COOCI"]
var newStrings = [String]()

for ele in strings{
    newStrings.append(ele.lowercased())
}

还可以使用map函数替代常规写法:

let strings = ["HANK", "CAT", "Kody", "COOCI"]
var newStrings = strings.map{ $0.lowercased() }
print(newStrings)

//输出以下内容
//["hank", "cat", "kody", "cooci"]
  • map函数可以对数组中的每一个元素做一次处理,如同遍历的作用
  • map函数接受一个闭包作为参数, 然后它会遍历整个数组,并对数组中每一个元素执行闭包中定义的操作。 相当于对数组中的所有元素做了一个映射
  • map函数可以对一个集合类型的所有元素做一个映射操作
  • map函数不会修改实例值, 而是新建一个拷贝

查看map函数的定义:

public func map<T>(_ transform: (Output) -> T) -> Just<T>

在源码中打开Sequence.swift文件,找到map函数的定义,它其实是Sequence协议的扩展

 /// - Parameter transform: A mapping closure. `transform` accepts an
 ///   element of this sequence as its parameter and returns a transformed
 ///   value of the same or of a different type.
 /// - Returns: An array containing the transformed elements of this
 ///   sequence.
 ///
 /// - Complexity: O(*n*), where *n* is the length of the sequence.
 @inlinable
 public func map<T>(
   _ transform: (Element) throws -> T
 ) rethrows -> [T] {
   let initialCapacity = underestimatedCount
   var result = ContiguousArray<T>()
   result.reserveCapacity(initialCapacity)

   var iterator = self.makeIterator()

   // Add elements up to the initial capacity without checking for regrowth.
   for _ in 0..<initialCapacity {
     result.append(try transform(iterator.next()!))
   }
   // Add remaining elements, if any.
   while let element = iterator.next() {
     result.append(try transform(element))
   }
   return Array(result)
 }

上述源码,可以看出map函数本质是创建新的数组,对集合内的每个元素进行transform操作(闭包表达式),然后返回新数组

flatMap函数

先来看一下flatMap函数的定义:

public func flatMap<ElementOfResult>(_ transform: (Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]

打开SequenceAlgorithms.swift文件,找到flatMap函数的定义:

  /// - Parameter transform: A closure that accepts an element of this
  ///   sequence as its argument and returns a sequence or collection.
  /// - Returns: The resulting flattened array.
  ///
  /// - Complexity: O(*m* + *n*), where *n* is the length of this sequence
  ///   and *m* is the length of the result.
  @inlinable
  public func flatMap<SegmentOfResult: Sequence>(
    _ transform: (Element) throws -> SegmentOfResult
  ) rethrows -> [SegmentOfResult.Element] {
    var result: [SegmentOfResult.Element] = []
    for element in self {
      result.append(contentsOf: try transform(element))
    }
    return result
  }

上述源码,flatMap函数中的闭包参数,同样是Sequence类型,但其返回类型为SegmentOfResult

在函数体的泛型定义中,SegmentOfResult的类型其实就是Sequence。 而flatMap函数返回的类型是SegmentOfResult.Element的数组

从函数的返回值来看,与map函数的区别在于flatMap函数会将Sequence中的元素进行“压平”,返回的类型会是Sequence中元素类型的数组,而map函数返回的是闭包返回类型T的数组

相比较map函数来说,flatMap函数最主要的两个作用一个是“压平”,一个是过滤nil

案例1

二维数组“压平”

let nums = [[1, 2, 3], [4, 5, 6]]

var result1 = nums.map{$0}
var result2 = nums.flatMap{$0}

print("map:\(result1)")
print("flatMap:\(result2)")

//输出以下内容
//map:[[1, 2, 3], [4, 5, 6]]
//flatMap:[1, 2, 3, 4, 5, 6]

上述代码,使用map函数输出的还是二维数组,但使用flatMap函数变成一维数组

案例2

过滤nil

let strings = ["HANK", "CAT", "Kody", "COOCI", nil]

var result1 = strings.map{$0}
var result2 = strings.flatMap{$0}

print("map:\(result1)")
print("flatMap:\(result2)")

//输出以下内容
//map:[Optional("HANK"), Optional("CAT"), Optional("Kody"), Optional("COOCI"), nil]
//flatMap:["HANK", "CAT", "Kody", "COOCI"]

上述代码,使用map函数,数组内元素变为Optional类型,同时nil值也被保留。使用flatMap函数,会把数组内元素进行Optional解包,并且过滤nil

let number: String? = String(20)
let restult = number.map{Int($0)}

上述代码,numberString可选类型,使用map函数将其转为Int类型,此时restult类型变为两层可选类型,使用时需要解包两次

let number: String? = String(20)
let restult = number.flatMap{Int($0)}

同样的代码,使用flatMap函数,restult依然保持为可选类型

打开Optional.swift文件,找到map函数的定义:

  /// - Parameter transform: A closure that takes the unwrapped value
  ///   of the instance.
  /// - Returns: The result of the given closure. If this instance is `nil`,
  ///   returns `nil`.
  @inlinable
  public func map<U>(
    _ transform: (Wrapped) throws -> U
  ) rethrows -> U? {
    switch self {
    case .some(let y):
      return .some(try transform(y))
    case .none:
      return .none
    }
  }

上述源码,当进入case .some的代码分支,返回时又进行了.some()的调用,增加了一层可选

找到flatMap函数的定义:

  /// - Parameter transform: A closure that takes the unwrapped value
  ///   of the instance.  
  /// - Returns: The result of the given closure. If this instance is `nil`,
  ///   returns `nil`.
  @inlinable
  public func flatMap<U>(
    _ transform: (Wrapped) throws -> U?
  ) rethrows -> U? {
    switch self {
    case .some(let y):
      return try transform(y)
    case .none:
      return .none
    }
  }

上述源码,当进入case .some的代码分支,直接进行transform操作,将结果返回

  • flatMap函数依然会遍历数组的元素,并对这些元素执行闭包中定义的操作
  • map函数不同的是,它对最终的结果进行了所谓的“压平”操作。多维数组flatMap之后,变成一维数组
  • flatMap函数返回后,数组的元素会过滤nil值,同时它会把Optional解包
compactMap函数

序列可选类型,在Swift 4.1之后,应该将flatMap替换为compactMap

打开SequenceAlgorithms.swift文件,找到compactMap函数的定义:

  /// - Parameter transform: A closure that accepts an element of this
  ///   sequence as its argument and returns an optional value.
  /// - Returns: An array of the non-`nil` results of calling `transform`
  ///   with each element of the sequence.
  ///
  /// - Complexity: O(*m* + *n*), where *n* is the length of this sequence
  ///   and *m* is the length of the result.
  @inlinable // protocol-only
  public func compactMap<ElementOfResult>(
    _ transform: (Element) throws -> ElementOfResult?
  ) rethrows -> [ElementOfResult] {
    return try _compactMap(transform)
  }

  // The implementation of flatMap accepting a closure with an optional result.
  // Factored out into a separate functions in order to be used in multiple
  // overloads.
  @inlinable // protocol-only
  @inline(__always)
  public func _compactMap<ElementOfResult>(
    _ transform: (Element) throws -> ElementOfResult?
  ) rethrows -> [ElementOfResult] {
    var result: [ElementOfResult] = []
    for element in self {
      if let newElement = try transform(element) {
        result.append(newElement)
      }
    }
    return result
  }
}

上述源码,在_compactMap方法内,遍历元素过程中做了可选值绑定操作,然后进行append

let strings = ["HANK", "CAT", "Kody", "COOCI", nil]
var result = strings.compactMap{$0}
print(result)

//输出以下内容
//["HANK", "CAT", "Kody", "COOCI"]

上述代码,使用compactMap函数将nil值过滤

  • 当转换闭包返回可选值并且期望得到的结果为非可选值的序列时,使用compactMap函数
  • 当对于序列中元素,转换闭包返回的是序列或者集合时,而期望得到的结果是一维数组,使用flatMap函数
filter函数

filter函数用于过滤元素,筛选出集合中满足某种条件的元素

let nums = [1, 2, 3, 4, 5]
let result = nums.filter{ $0 % 2 != 0 }
print(result)

//输出以下结果:
//[1, 3, 5]

上述代码,使用filter函数找到数组元素中的奇数

打开Sequence.swift文件,找到filter函数的定义:

  /// - Parameter isIncluded: A closure that takes an element of the
  ///   sequence as its argument and returns a Boolean value indicating
  ///   whether the element should be included in the returned array.
  /// - Returns: An array of the elements that `isIncluded` allowed.
  ///
  /// - Complexity: O(*n*), where *n* is the length of the sequence.
  @inlinable
  public __consuming func filter(
    _ isIncluded: (Element) throws -> Bool
  ) rethrows -> [Element] {
    return try _filter(isIncluded)
  }

  @_transparent
  public func _filter(
    _ isIncluded: (Element) throws -> Bool
  ) rethrows -> [Element] {

    var result = ContiguousArray<Element>()

    var iterator = self.makeIterator()

    while let element = iterator.next() {
      if try isIncluded(element) {
        result.append(element)
      }
    }

    return Array(result)
  }
  • filter函数将闭包表达式作为参数
  • 调用ContiguousArray<Element>()声明一个空集合
  • 使用self.makeIterator()生成一个迭代器
  • 通过while遍历元素
  • isIncluded(闭包表达式)作为判断条件,将符合条件的元素加入到集合中
  • 返回新数组
forEach函数

对于集合类型的元素,不必每次都通过for循环来做遍历,Sequence中同样提供了高阶函数以供使用:

let nums = [1, 2, 3, 4, 5]

nums.forEach {
    print($0)
}

上述代码,通过forEach函数遍历数组元素进行打印

打开Sequence.swift文件,找到forEach函数的定义:

  /// Using the `forEach` method is distinct from a `for`-`in` loop in two
  /// important ways:
  ///
  /// 1. You cannot use a `break` or `continue` statement to exit the current
  ///    call of the `body` closure or skip subsequent calls.
  /// 2. Using the `return` statement in the `body` closure will exit only from
  ///    the current call to `body`, not from any outer scope, and won't skip
  ///    subsequent calls.
  ///
  /// - Parameter body: A closure that takes an element of the sequence as a
  ///   parameter.
  @_semantics("sequence.forEach")
  @inlinable
  public func forEach(
    _ body: (Element) throws -> Void
  ) rethrows {
    for element in self {
      try body(element)
    }
  }
}

上述源码,forEach函数的本质还是使用for...in循环,执行闭包表达式

如果想获取当前元素的index,可以使用enumerated函数

let nums = [1, 2, 3, 4, 5]

nums.enumerated().forEach {
    print("index:\($0),emelent:\($1)")
}

//输出以下结果:
//index:0,emelent:1
//index:1,emelent:2
//index:2,emelent:3
//index:3,emelent:4
//index:4,emelent:5

上述代码,可以同时输出indexemelent

找到emelent函数的定义,返回一个EnumeratedSequence类型

@inlinable public func enumerated() -> EnumeratedSequence<Array<Element>>

打开Algorithm.swift文件,找到EnumeratedSequence定义:

@frozen
public struct EnumeratedSequence<Base: Sequence> {
  @usableFromInline
  internal var _base: Base

  /// Construct from a `Base` sequence.
  @inlinable
  internal init(_base: Base) {
    self._base = _base
  }
}

EnumeratedSequence是一个接收Sequence类型的结构体

extension EnumeratedSequence {
  @frozen
  public struct Iterator {
    @usableFromInline
    internal var _base: Base.Iterator
    @usableFromInline
    internal var _count: Int

    /// Construct from a `Base` iterator.
    @inlinable
    internal init(_base: Base.Iterator) {
      self._base = _base
      self._count = 0
    }
  }
}

IteratorEnumeratedSequence内部用到的迭代器,_count就是index

extension EnumeratedSequence.Iterator: IteratorProtocol, Sequence {
  /// The type of element returned by `next()`.
  public typealias Element = (offset: Int, element: Base.Element)

  /// Advances to the next element and returns it, or `nil` if no next element
  /// exists.
  ///
  /// Once `nil` has been returned, all subsequent calls return `nil`.
  @inlinable
  public mutating func next() -> Element? {
    guard let b = _base.next() else { return nil }
    let result = (offset: _count, element: b)
    _count += 1 
    return result
  }
}

next用来生成元素,返回一个元组类型。所以我们可以使用$0获取index,使用$1获取emelent

reduce函数
  • reduce函数可以对数组的元素进行计算
  • reduce函数可以将数组元素组合计算为一个值,并且会接受一个初始值,这个初始值的类型可以和数组元素类型不同
  • 将数组元素合并成字符串,元素之间可以指定分隔符号
let nums = [1, 2, 3, 4, 5]
let result = nums.reduce(10, +)
print(result)

//输出以下结果:
//25

上述代码,使用reduce函数将数组内元素和初始值10,通过闭包表达式进行求和

打开SequenceAlgorithms.swift文件,找到reduce函数的定义:

  /// - Parameters:
  ///   - initialResult: The value to use as the initial accumulating value.
  ///     `initialResult` is passed to `nextPartialResult` the first time the
  ///     closure is executed.
  ///   - nextPartialResult: A closure that combines an accumulating value and
  ///     an element of the sequence into a new accumulating value, to be used
  ///     in the next call of the `nextPartialResult` closure or returned to
  ///     the caller.
  /// - Returns: The final accumulated value. If the sequence has no elements,
  ///   the result is `initialResult`.
  ///
  /// - Complexity: O(*n*), where *n* is the length of the sequence.
  @inlinable
  public func reduce<Result>(
    _ initialResult: Result,
    _ nextPartialResult:
      (_ partialResult: Result, Element) throws -> Result
  ) rethrows -> Result {
    var accumulator = initialResult
    for element in self {
      accumulator = try nextPartialResult(accumulator, element)
    }
    return accumulator
  }
  • initialResult:初始值
  • nextPartialResult:闭包表达式
  • accumulator:累加器

遍历集合,将累加器和元素传入闭包表达式进行指定操作,将结果重新赋值给累加器,最后将累加器返回

案例1

对集合内每个元素进行*2操作

func customMap(collection: [Int], transform: (Int) -> Int) -> [Int]{
    return collection.reduce([Int]()){
        var arr: [Int] = $0
        arr.append(transform($1))
        return arr
    }
}

let nums = [1, 2, 3, 4, 5]
let result = customMap(collection: nums){
    $0 * 2
}

print(result)

//输出以下结果:
//[2, 4, 6, 8, 10]

上述代码,定义customMap方法

  • 方法内部使用reduce函数,初始值为空数组
  • 闭包表达式内部将$0空数组赋值给arr变量
  • $1元素传入transform闭包表达式
  • 将执行结果appendarr数组并进行返回

也可以通过reduce函数直接实现上述需求,由于传入的集合是可变数组,需要使用reduce(into:)方法,否则编译报错

编译报错

let nums = [1, 2, 3, 4, 5]

let result = nums.reduce(into: [Int]()){
    $0.append($1 * 2)
}

print(result)

//输出以下结果:
//[2, 4, 6, 8, 10]

上述代码,使用reduce(into:)方法,也可以实现同样的效果

案例2

找出数组当中的最大值

let result = [1, 2, 3, 4, 5].reduce(0){
    return $0 < $1 ? $1 : $0
}

print(result)

//输出以下结果:
//5

上述代码,要想找到最大值,一定会进行比较,这里的逻辑是通过累加器和元素进行比较

  • 累加器初始传入0
  • 闭包表达式内,使用累加器和元素值比较
  • 如果累加器小于元素值,将元素值赋值给累加器
  • 循环往复,最终返回的累加器就是数组内元素的最大值
案例3

找到数组内奇数,再找到距离它最近的2n次方数,最后计算出总和

extension FixedWidthInteger {
    @inlinable
    func nextPowerOf2() -> Self {
        guard self != 0 else {
            return 1
        }
        return 1 << (Self.bitWidth - (self - 1).leadingZeroBitCount)
    }
}

let result = [1, 2, 3, 4, 5].filter{
    $0 % 2 != 0
}.map{
    $0.nextPowerOf2()
}.reduce(0){
    $0 + $1
}

print(result)

//输出以下结果:
//13
  • 使用filter方法筛选出数组内奇数
  • 使用map方法将数组内元素依次通过nextPowerOf2方法,找到距离元素值最近的2n次方数
  • 使用reduce方法求和
案例4

逆序数组

let result = [1, 2, 3, 4, 5].reduce([Int]()){
   return [$1] + $0
}

print(result)

//输出以下结果:
//[5, 4, 3, 2, 1]
  • 累加器初始传入空数组
  • 将元素包装成数组和累加器的数组相加,等同于两个数组元素合并
  • 将合并后的数组赋值给累加器,直到所有元素合并完
LazySequence

假设我们有一个非常庞大的数组,想要通过map函数对每一个元素进行*2,这将是一个非常耗时的操作

let numbers = Array(1...100000)
let mapNumbers = numbers.map{ $0 * 2 }

合理的逻辑,应该使用懒加载,在元素被访问的时候进行*2操作。但这样做还能使用map函数吗?

答案当然是可以的,Swift提供了⼀个lazy关键字,它能实现这样的效果:

let numbers = Array(1...100000)
let mapNumbers = numbers.lazy.map{ $0 * 2 }

print(mapNumbers)

上述代码,直接输出mapNumbers,可以看到里面的元素没有发生任何变化

let numbers = Array(1...100000)
let mapNumbers = numbers.lazy.map{ $0 * 2 }

print(mapNumbers[0])
print(mapNumbers[1])
print(mapNumbers[2])

//输出以下结果:
//2
//4
//6

上述代码,当数组内指定元素被访问,这时该元素才会进行*2操作

通过上面的案例来看,当使⽤Lazy关键字后,得到的mapNumbers保留的还是原来的数据,只不过在访问指定元素时,才会执⾏对应的闭包操作,产⽣新值给我们

当使用lazy关键字后,返回的是一个LazySequence的结构体

查看lazy的定义:

@inlinable public var lazy: LazySequence<Array<Element>> { get }

打开LazySequence.swift文件,找到LazySequenceProtocol协议的定义:

public protocol LazySequenceProtocol: Sequence {
  /// A `Sequence` that can contain the same elements as this one,
  /// possibly with a simpler type.
  ///
  /// - See also: `elements`
  associatedtype Elements: Sequence = Self where Elements.Element == Element

  /// A sequence containing the same elements as this one, possibly with
  /// a simpler type.
  ///
  /// When implementing lazy operations, wrapping `elements` instead
  /// of `self` can prevent result types from growing an extra
  /// `LazySequence` layer.  For example,
  ///
  /// _prext_ example needed
  ///
  /// Note: this property need not be implemented by conforming types,
  /// it has a default implementation in a protocol extension that
  /// just returns `self`.
  var elements: Elements { get }
}

LazySequenceProtocol协议遵循了Sequence协议

找到lazy的定义:

extension Sequence {
  /// A sequence containing the same elements as this sequence,
  /// but on which some operations, such as `map` and `filter`, are
  /// implemented lazily.
  @inlinable // protocol-only
  public var lazy: LazySequence<Self> {
    return LazySequence(_base: self)
  }
}

lazy是一个计算属性,返回一个LazySequence对象

@frozen // lazy-performance
public struct LazySequence<Base: Sequence> {
  @usableFromInline
  internal var _base: Base

  /// Creates a sequence that has the same elements as `base`, but on
  /// which some operations such as `map` and `filter` are implemented
  /// lazily.
  @inlinable // lazy-performance
  internal init(_base: Base) {
    self._base = _base
  }
}

LazySequence初始化的时候,保留了当前的集合

extension LazySequence: Sequence {
  public typealias Element = Base.Element
  public typealias Iterator = Base.Iterator

  @inlinable
  public __consuming func makeIterator() -> Iterator {
    return _base.makeIterator()
  }
  
  @inlinable // lazy-performance
  public var underestimatedCount: Int {
    return _base.underestimatedCount
  }

  @inlinable // lazy-performance
  @discardableResult
  public __consuming func _copyContents(
    initializing buf: UnsafeMutableBufferPointer<Element>
  ) -> (Iterator, UnsafeMutableBufferPointer<Element>.Index) {
    return _base._copyContents(initializing: buf)
  }

  @inlinable // lazy-performance
  public func _customContainsEquatableElement(_ element: Element) -> Bool? { 
    return _base._customContainsEquatableElement(element)
  }
  
  @inlinable // generic-performance
  public __consuming func _copyToContiguousArray() -> ContiguousArray<Element> {
    return _base._copyToContiguousArray()
  }
}
  • LazySequence扩展,遵循Sequence协议
  • makeIterator()方法创建迭代器

打开Map.swift文件,找到在LazySequenceProtocol扩展中实现的map函数

extension LazySequenceProtocol {
  /// Returns a `LazyMapSequence` over this `Sequence`.  The elements of
  /// the result are computed lazily, each time they are read, by
  /// calling `transform` function on a base element.
  @inlinable
  public func map<U>(
    _ transform: @escaping (Element) -> U
  ) -> LazyMapSequence<Elements, U> {
    return LazyMapSequence(_base: elements, transform: transform)
  }
}
  • LazySequenceProtocol扩展,实现map函数
  • 返回一个LazyMapSequence对象
/// A `Sequence` whose elements consist of those in a `Base`
/// `Sequence` passed through a transform function returning `Element`.
/// These elements are computed lazily, each time they're read, by
/// calling the transform function on a base element.
@frozen
public struct LazyMapSequence<Base: Sequence, Element> {

  public typealias Elements = LazyMapSequence

  @usableFromInline
  internal var _base: Base
  @usableFromInline
  internal let _transform: (Base.Element) -> Element

  /// Creates an instance with elements `transform(x)` for each element
  /// `x` of base.
  @inlinable
  internal init(_base: Base, transform: @escaping (Base.Element) -> Element) {
    self._base = _base
    self._transform = transform
  }
}

LazyMapSequence初始化的时候,保存了当前集合和transform闭包表达式

extension LazyMapSequence.Iterator: IteratorProtocol, Sequence {
  /// Advances to the next element and returns it, or `nil` if no next element
  /// exists.
  ///
  /// Once `nil` has been returned, all subsequent calls return `nil`.
  ///
  /// - Precondition: `next()` has not been applied to a copy of `self`
  ///   since the copy was made.
  @inlinable
  public mutating func next() -> Element? {
    return _base.next().map(_transform)
  }
}

当访问指定元素时,会调用next()方法,然后对元素进行transform操作

所以Lazy的本质,其实就是保存当前集合和对应的操作,然后在访问具体元素的时候,执⾏对应的操作

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容