Reductions.swift 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621
  1. //===----------------------------------------------------------------------===//
  2. //
  3. // This source file is part of the Swift Algorithms open source project
  4. //
  5. // Copyright (c) 2020 Apple Inc. and the Swift project authors
  6. // Licensed under Apache License v2.0 with Runtime Library Exception
  7. //
  8. // See https://swift.org/LICENSE.txt for license information
  9. //
  10. //===----------------------------------------------------------------------===//
  11. // MARK: - Exclusive Reductions
  12. extension LazySequenceProtocol {
  13. /// Returns a sequence containing the accumulated results of combining the
  14. /// elements of the sequence using the given closure.
  15. ///
  16. /// This can be seen as applying the reduce function to each element and
  17. /// providing the initial value followed by these results as a sequence.
  18. ///
  19. /// ```
  20. /// let runningTotal = [1, 2, 3, 4].lazy.reductions(0, +)
  21. /// print(Array(runningTotal))
  22. ///
  23. /// // prints [0, 1, 3, 6, 10]
  24. /// ```
  25. ///
  26. /// - Parameters:
  27. /// - initial: The value to use as the initial value.
  28. /// - transform: A closure that combines the previously reduced result and
  29. /// the next element in the receiving sequence.
  30. /// - Returns: A sequence of the initial value followed by the reduced
  31. /// elements.
  32. ///
  33. /// - Complexity: O(1)
  34. @inlinable
  35. public func reductions<Result>(
  36. _ initial: Result,
  37. _ transform: @escaping (Result, Element) -> Result
  38. ) -> ExclusiveReductionsSequence<Elements, Result> {
  39. reductions(into: initial) { result, element in
  40. result = transform(result, element)
  41. }
  42. }
  43. /// Returns a sequence containing the accumulated results of combining the
  44. /// elements of the sequence using the given closure.
  45. ///
  46. /// This can be seen as applying the reduce function to each element and
  47. /// providing the initial value followed by these results as a sequence.
  48. ///
  49. /// ```
  50. /// let runningTotal = [1, 2, 3, 4].lazy.reductions(into: 0, +)
  51. /// print(Array(runningTotal))
  52. ///
  53. /// // prints [0, 1, 3, 6, 10]
  54. /// ```
  55. ///
  56. /// - Parameters:
  57. /// - initial: The value to use as the initial value.
  58. /// - transform: A closure that combines the previously reduced result and
  59. /// the next element in the receiving sequence.
  60. /// - Returns: A sequence of the initial value followed by the reduced
  61. /// elements.
  62. ///
  63. /// - Complexity: O(1)
  64. @inlinable
  65. public func reductions<Result>(
  66. into initial: Result,
  67. _ transform: @escaping (inout Result, Element) -> Void
  68. ) -> ExclusiveReductionsSequence<Elements, Result> {
  69. ExclusiveReductionsSequence(
  70. base: elements,
  71. initial: initial,
  72. transform: transform)
  73. }
  74. }
  75. extension Sequence {
  76. /// Returns an array containing the accumulated results of combining the
  77. /// elements of the sequence using the given closure.
  78. ///
  79. /// This can be seen as applying the reduce function to each element and
  80. /// providing the initial value followed by these results as a sequence.
  81. ///
  82. /// ```
  83. /// let runningTotal = [1, 2, 3, 4].reductions(0, +)
  84. /// print(runningTotal)
  85. ///
  86. /// // prints [0, 1, 3, 6, 10]
  87. /// ```
  88. ///
  89. /// When `reductions(_:_:)` is called, the following steps occur:
  90. ///
  91. /// 1. The `initial` result is added to an array of results.
  92. /// 2. The `transform` closure is called with the `initial` result and the
  93. /// first element of the sequence, appending the result to the array.
  94. /// 3. The closure is called again repeatedly with the updated accumulating
  95. /// result and each element of the sequence, adding each result to the
  96. /// array.
  97. /// 4. When the sequence is exhausted, the results array is returned to the
  98. /// caller.
  99. ///
  100. /// If the sequence has no elements, `transform` is never executed and
  101. /// an array containing only the `initial` result is returned.
  102. ///
  103. /// - Parameters:
  104. /// - initial: The value to use as the initial value.
  105. /// - transform: A closure that combines the previously reduced result and
  106. /// the next element in the receiving sequence.
  107. /// - Returns: An array of the initial value followed by the reduced elements.
  108. ///
  109. /// - Complexity: O(_n_), where _n_ is the length of the sequence.
  110. @inlinable
  111. public func reductions<Result>(
  112. _ initial: Result,
  113. _ transform: (Result, Element) throws -> Result
  114. ) rethrows -> [Result] {
  115. try reductions(into: initial) { result, element in
  116. result = try transform(result, element)
  117. }
  118. }
  119. /// Returns an array containing the accumulated results of combining the
  120. /// elements of the sequence using the given closure.
  121. ///
  122. /// This can be seen as applying the reduce function to each element and
  123. /// providing the initial value followed by these results as a sequence.
  124. ///
  125. /// ```
  126. /// let runningTotal = [1, 2, 3, 4].reductions(into: 0, +)
  127. /// print(runningTotal)
  128. ///
  129. /// // prints [0, 1, 3, 6, 10]
  130. /// ```
  131. ///
  132. /// When `reductions(into:_:_)` is called, the following steps occur:
  133. ///
  134. /// 1. The `initial` result is added to an array of results.
  135. /// 2. The `transform` closure is called with the `initial` result and the
  136. /// first element of the sequence, appending the result to the array.
  137. /// 3. The closure is called again repeatedly with the updated accumulating
  138. /// result and each element of the sequence, adding each result to the
  139. /// array.
  140. /// 4. When the sequence is exhausted, the results array is returned to the
  141. /// caller.
  142. ///
  143. /// If the sequence has no elements, `transform` is never executed and
  144. /// an array containing only the `initial` result is returned.
  145. ///
  146. /// - Parameters:
  147. /// - initial: The value to use as the initial value.
  148. /// - transform: A closure that combines the previously reduced result and
  149. /// the next element in the receiving sequence.
  150. /// - Returns: An array of the initial value followed by the reduced elements.
  151. ///
  152. /// - Complexity: O(_n_), where _n_ is the length of the sequence.
  153. @inlinable
  154. public func reductions<Result>(
  155. into initial: Result,
  156. _ transform: (inout Result, Element) throws -> Void
  157. ) rethrows -> [Result] {
  158. var output = [Result]()
  159. output.reserveCapacity(underestimatedCount + 1)
  160. output.append(initial)
  161. var initial = initial
  162. for element in self {
  163. try transform(&initial, element)
  164. output.append(initial)
  165. }
  166. return output
  167. }
  168. }
  169. /// A sequence of applying a transform to the element of a sequence and the
  170. /// previously transformed result.
  171. public struct ExclusiveReductionsSequence<Base: Sequence, Result> {
  172. @usableFromInline
  173. internal let base: Base
  174. @usableFromInline
  175. internal let initial: Result
  176. @usableFromInline
  177. internal let transform: (inout Result, Base.Element) -> Void
  178. @inlinable
  179. internal init(
  180. base: Base,
  181. initial: Result,
  182. transform: @escaping (inout Result, Base.Element) -> Void
  183. ) {
  184. self.base = base
  185. self.initial = initial
  186. self.transform = transform
  187. }
  188. }
  189. extension ExclusiveReductionsSequence: Sequence {
  190. public struct Iterator: IteratorProtocol {
  191. @usableFromInline
  192. internal var iterator: Base.Iterator
  193. @usableFromInline
  194. internal var current: Result?
  195. @usableFromInline
  196. internal let transform: (inout Result, Base.Element) -> Void
  197. @inlinable
  198. internal init(
  199. iterator: Base.Iterator,
  200. current: Result? = nil,
  201. transform: @escaping (inout Result, Base.Element) -> Void
  202. ) {
  203. self.iterator = iterator
  204. self.current = current
  205. self.transform = transform
  206. }
  207. @inlinable
  208. public mutating func next() -> Result? {
  209. guard let result = current else { return nil }
  210. current = iterator.next().map { element in
  211. var result = result
  212. transform(&result, element)
  213. return result
  214. }
  215. return result
  216. }
  217. }
  218. @inlinable
  219. public func makeIterator() -> Iterator {
  220. Iterator(
  221. iterator: base.makeIterator(),
  222. current: initial,
  223. transform: transform)
  224. }
  225. }
  226. extension ExclusiveReductionsSequence: Collection where Base: Collection {
  227. public struct Index: Comparable {
  228. @usableFromInline
  229. internal enum Representation {
  230. case base(Base.Index, Result)
  231. case end
  232. }
  233. @usableFromInline
  234. internal let representation: Representation
  235. @inlinable
  236. internal init(_ representation: Representation) {
  237. self.representation = representation
  238. }
  239. @inlinable
  240. public static func == (lhs: Self, rhs: Self) -> Bool {
  241. switch (lhs.representation, rhs.representation) {
  242. case (.base(let lhs, _), .base(let rhs, _)):
  243. return lhs == rhs
  244. case (.end, .end):
  245. return true
  246. case (.base, .end), (.end, .base):
  247. return false
  248. }
  249. }
  250. @inlinable
  251. public static func < (lhs: Self, rhs: Self) -> Bool {
  252. switch (lhs.representation, rhs.representation) {
  253. case (.end, _):
  254. return false
  255. case (_, .end):
  256. return true
  257. case (.base(let lhs, _), .base(let rhs, _)):
  258. return lhs < rhs
  259. }
  260. }
  261. }
  262. @inlinable
  263. public var startIndex: Index {
  264. Index(.base(base.startIndex, initial))
  265. }
  266. @inlinable
  267. public var endIndex: Index {
  268. Index(.end)
  269. }
  270. @inlinable
  271. public subscript(position: Index) -> Result {
  272. switch position.representation {
  273. case .base(_, let result):
  274. return result
  275. case .end:
  276. fatalError("Cannot get element of end index.")
  277. }
  278. }
  279. @inlinable
  280. public func index(after index: Index) -> Index {
  281. switch index.representation {
  282. case .base(base.endIndex, _):
  283. return Index(.end)
  284. case .base(let index, var result):
  285. transform(&result, base[index])
  286. return Index(.base(base.index(after: index), result))
  287. case .end:
  288. fatalError("Cannot get index after end index.")
  289. }
  290. }
  291. @inlinable
  292. public func distance(from start: Index, to end: Index) -> Int {
  293. switch (start.representation, end.representation) {
  294. case let (.base(start, _), .base(end, _)):
  295. return base.distance(from: start, to: end)
  296. case let (.base(index, _), .end):
  297. return base.distance(from: index, to: base.endIndex) + 1
  298. case let (.end, .base(index, _)):
  299. return base.distance(from: base.endIndex, to: index) - 1
  300. case (.end, .end):
  301. return 0
  302. }
  303. }
  304. }
  305. extension ExclusiveReductionsSequence: LazySequenceProtocol {}
  306. extension ExclusiveReductionsSequence: LazyCollectionProtocol
  307. where Base: Collection {}
  308. extension ExclusiveReductionsSequence.Index: Hashable
  309. where Base.Index: Hashable
  310. {
  311. @inlinable
  312. public func hash(into hasher: inout Hasher) {
  313. switch representation {
  314. case .base(let base, _):
  315. hasher.combine(base)
  316. case .end:
  317. break
  318. }
  319. }
  320. }
  321. // MARK: - Inclusive Reductions
  322. extension LazySequenceProtocol {
  323. /// Returns a sequence containing the accumulated results of combining the
  324. /// elements of the sequence using the given closure.
  325. ///
  326. /// This can be seen as applying the reduce function to each element and
  327. /// providing the initial value followed by these results as a sequence.
  328. ///
  329. /// ```
  330. /// let runningTotal = [1, 2, 3, 4].lazy.reductions(+)
  331. /// print(Array(runningTotal))
  332. ///
  333. /// // prints [1, 3, 6, 10]
  334. /// ```
  335. ///
  336. /// - Parameter transform: A closure that combines the previously reduced
  337. /// result and the next element in the receiving sequence.
  338. /// - Returns: A sequence of the reduced elements.
  339. ///
  340. /// - Complexity: O(1)
  341. @inlinable
  342. public func reductions(
  343. _ transform: @escaping (Element, Element) -> Element
  344. ) -> InclusiveReductionsSequence<Elements> {
  345. InclusiveReductionsSequence(base: elements, transform: transform)
  346. }
  347. }
  348. extension Sequence {
  349. /// Returns an array containing the accumulated results of combining the
  350. /// elements of the sequence using the given closure.
  351. ///
  352. /// This can be seen as applying the reduce function to each element and
  353. /// providing the initial value followed by these results as a sequence.
  354. ///
  355. /// ```
  356. /// let runningTotal = [1, 2, 3, 4].reductions(+)
  357. /// print(runningTotal)
  358. ///
  359. /// // prints [1, 3, 6, 10]
  360. /// ```
  361. ///
  362. /// When `reductions(_:)` is called, the following steps occur:
  363. ///
  364. /// 1. The `transform` closure is called with the first and second elements
  365. /// of the sequence, appending the result to an array of results.
  366. /// 2. The closure is called again repeatedly with the updated accumulating
  367. /// result and the next element of the sequence, adding each result to the
  368. /// array.
  369. /// 3. When the sequence is exhausted, the results array is returned to the
  370. /// caller.
  371. ///
  372. /// If the sequence has no elements, `transform` is never executed and
  373. /// an empty array is returned.
  374. ///
  375. /// If the sequence has one element, `transform` is never executed and
  376. /// an array containing only that first element is returned.
  377. ///
  378. /// - Parameter transform: A closure that combines the previously reduced
  379. /// result and the next element in the receiving sequence.
  380. /// - Returns: An array of the reduced elements.
  381. ///
  382. /// - Complexity: O(_n_), where _n_ is the length of the sequence.
  383. @inlinable
  384. public func reductions(
  385. _ transform: (Element, Element) throws -> Element
  386. ) rethrows -> [Element] {
  387. var iterator = makeIterator()
  388. guard let initial = iterator.next() else { return [] }
  389. return try IteratorSequence(iterator).reductions(initial, transform)
  390. }
  391. }
  392. public struct InclusiveReductionsSequence<Base: Sequence> {
  393. @usableFromInline
  394. internal let base: Base
  395. @usableFromInline
  396. internal let transform: (Base.Element, Base.Element) -> Base.Element
  397. @inlinable
  398. internal init(
  399. base: Base,
  400. transform: @escaping (Base.Element, Base.Element) -> Base.Element
  401. ) {
  402. self.base = base
  403. self.transform = transform
  404. }
  405. }
  406. extension InclusiveReductionsSequence: Sequence {
  407. public struct Iterator: IteratorProtocol {
  408. @usableFromInline
  409. internal var iterator: Base.Iterator
  410. @usableFromInline
  411. internal var element: Base.Element?
  412. @usableFromInline
  413. internal let transform: (Base.Element, Base.Element) -> Base.Element
  414. @inlinable
  415. internal init(
  416. iterator: Base.Iterator,
  417. transform: @escaping (Base.Element, Base.Element) -> Base.Element
  418. ) {
  419. self.iterator = iterator
  420. self.transform = transform
  421. }
  422. @inlinable
  423. public mutating func next() -> Base.Element? {
  424. guard let previous = element else {
  425. element = iterator.next()
  426. return element
  427. }
  428. guard let next = iterator.next() else { return nil }
  429. element = transform(previous, next)
  430. return element
  431. }
  432. }
  433. @inlinable
  434. public func makeIterator() -> Iterator {
  435. Iterator(iterator: base.makeIterator(), transform: transform)
  436. }
  437. }
  438. extension InclusiveReductionsSequence: Collection where Base: Collection {
  439. public struct Index: Comparable {
  440. @usableFromInline
  441. internal let base: Base.Index
  442. @usableFromInline
  443. internal let result: Element?
  444. @inlinable
  445. internal init(base: Base.Index, result: Element?) {
  446. self.base = base
  447. self.result = result
  448. }
  449. @inlinable
  450. public static func < (lhs: Self, rhs: Self) -> Bool {
  451. lhs.base < rhs.base
  452. }
  453. @inlinable
  454. public static func == (lhs: Self, rhs: Self) -> Bool {
  455. lhs.base == rhs.base
  456. }
  457. }
  458. @inlinable
  459. public var startIndex: Index {
  460. Index(base: base.startIndex, result: base.first)
  461. }
  462. @inlinable
  463. public var endIndex: Index {
  464. Index(base: base.endIndex, result: nil)
  465. }
  466. @inlinable
  467. public subscript(index: Index) -> Base.Element {
  468. guard let result = index.result else {
  469. fatalError("Can't subscript using endIndex")
  470. }
  471. return result
  472. }
  473. @inlinable
  474. public func index(after index: Index) -> Index {
  475. guard let result = index.result else {
  476. fatalError("Can't advance past endIndex")
  477. }
  478. let index = base.index(after: index.base)
  479. let nextResult = index == base.endIndex
  480. ? nil
  481. : transform(result, base[index])
  482. return Index(base: index, result: nextResult)
  483. }
  484. @inlinable
  485. public func distance(from start: Index, to end: Index) -> Int {
  486. base.distance(from: start.base, to: end.base)
  487. }
  488. }
  489. extension InclusiveReductionsSequence: LazySequenceProtocol {}
  490. extension InclusiveReductionsSequence: LazyCollectionProtocol
  491. where Base: Collection {}
  492. extension InclusiveReductionsSequence.Index: Hashable
  493. where Base.Index: Hashable
  494. {
  495. @inlinable
  496. public func hash(into hasher: inout Hasher) {
  497. hasher.combine(base)
  498. }
  499. }
  500. // MARK: - Scan
  501. extension LazySequenceProtocol {
  502. @available(*, deprecated, message: "Use reductions(_:_:) instead.")
  503. @inlinable
  504. public func scan<Result>(
  505. _ initial: Result,
  506. _ transform: @escaping (Result, Element) -> Result
  507. ) -> ExclusiveReductionsSequence<Elements, Result> {
  508. reductions(initial, transform)
  509. }
  510. @available(*, deprecated, message: "Use reductions(into:_:) instead.")
  511. @inlinable
  512. public func scan<Result>(
  513. into initial: Result,
  514. _ transform: @escaping (inout Result, Element) -> Void
  515. ) -> ExclusiveReductionsSequence<Elements, Result> {
  516. reductions(into: initial, transform)
  517. }
  518. }
  519. extension Sequence {
  520. @available(*, deprecated, message: "Use reductions(_:_:) instead.")
  521. @inlinable
  522. public func scan<Result>(
  523. _ initial: Result,
  524. _ transform: (Result, Element) throws -> Result
  525. ) rethrows -> [Result] {
  526. try reductions(initial, transform)
  527. }
  528. @available(*, deprecated, message: "Use reductions(into:_:) instead.")
  529. @inlinable
  530. public func scan<Result>(
  531. into initial: Result,
  532. _ transform: (inout Result, Element) throws -> Void
  533. ) rethrows -> [Result] {
  534. try reductions(into: initial, transform)
  535. }
  536. }
  537. extension LazySequenceProtocol {
  538. @available(*, deprecated, message: "Use reductions(_:) instead.")
  539. @inlinable
  540. public func scan(
  541. _ transform: @escaping (Element, Element) -> Element
  542. ) -> InclusiveReductionsSequence<Elements> {
  543. reductions(transform)
  544. }
  545. }
  546. extension Sequence {
  547. @available(*, deprecated, message: "Use reductions(_:) instead.")
  548. @inlinable
  549. public func scan(
  550. _ transform: (Element, Element) throws -> Element
  551. ) rethrows -> [Element] {
  552. try reductions(transform)
  553. }
  554. }