Last active
January 5, 2019 01:45
-
-
Save elm4ward/eb73f85bf2a08b62b0d43301c81ece42 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ---------------------------------------------------------------------------------------------------- | |
// Iterators for Naysayer | |
// | |
// Generating understandable, reusable, simplified, ... code with Iterators | |
// | |
// http://elm4ward.github.io/swift/Iterator/protocol/2016/04/14/Iterators-for-the-naysayers.html | |
// | |
// 1. Base Protocols | |
// 2. ZipWithSum | |
// 3. ZipWithSumFrom | |
// 4. ZipWithDiff | |
// 5. Example A | |
// 6. Example B | |
// 7. Example C | |
// 8. Fold Types | |
// 9. Zip with Fold | |
// 10. Example zip with fold | |
// ---------------------------------------------------------------------------------------------------- | |
let mark = "####################################" | |
let chapter: (String) -> Void = { print("\n", mark, "# \($0)", mark, separator: "\n")} | |
// ---------------------------------------------------------------------------------------------------- | |
// MARK: - 1. Protocols | |
// | |
// Semigroup, Monoid: | |
// Scary! Run away! Hide! - No they dont hurt! ;-) | |
// They are just math names for a pattern | |
// ---------------------------------------------------------------------------------------------------- | |
/// Semigroup in disguise | |
protocol Addable { | |
static func +(l: Self, r: Self) -> Self | |
} | |
/// Monoid in disguise | |
protocol ImplicitDefault { | |
static func implicitDefault() -> Self | |
} | |
/// Wat? Wait? No cool category theory name? | |
protocol Subtractable { | |
static func -(l: Self, r: Self) -> Self | |
} | |
/// powerful int | |
extension Int: ImplicitDefault, Addable, Subtractable { | |
static func implicitDefault() -> Int { | |
return 0 | |
} | |
} | |
// ---------------------------------------------------------------------------------------------------- | |
// MARK: - 2. ZipWithSum | |
// | |
// a basic sum sequence | |
// ---------------------------------------------------------------------------------------------------- | |
/// A Sequence where the Element from the Iterator support Addable and ImplicitDefault | |
struct ZipWithSum<S>: Sequence where | |
S: Sequence, | |
S.Iterator.Element: ImplicitDefault, | |
S.Iterator.Element: Addable | |
{ | |
let sequence: S | |
func makeIterator() -> AnyIterator<(S.Iterator.Element, S.Iterator.Element)> { | |
// here comes the initial state | |
var Iterator = sequence.makeIterator() | |
var sum = S.Iterator.Element.implicitDefault() | |
return AnyIterator { | |
// 1. changing the Iterator1 | |
let element = Iterator.next() | |
switch element { | |
case let e?: | |
// 2. changing the sum | |
sum = sum + e | |
return (e, sum) | |
case .none: | |
return nil | |
} | |
} | |
} | |
} | |
/// When we have a Sequence where the Element support ImplicitDefault and Addable, you know the rest | |
extension Sequence where | |
Self.Iterator.Element: ImplicitDefault, | |
Self.Iterator.Element: Addable { | |
func zipWithSum() -> ZipWithSum<Self> { | |
return ZipWithSum(sequence: self) | |
} | |
} | |
// ---------------------------------------------------------------------------------------------------- | |
// MARK: - 3. ZipWithSumFrom | |
// | |
// a sum sequence based on a function to retrieve the value from the element | |
// ---------------------------------------------------------------------------------------------------- | |
/// Good Point, what if the value to sum up is not the Element itself but must be extracted from it? | |
struct ZipWithSumFrom<T, U>: Sequence where | |
T: Sequence, | |
U: ImplicitDefault, | |
U: Addable | |
{ | |
let sequence: T | |
let sumFrom: (T.Iterator.Element) -> U? | |
func makeIterator() -> AnyIterator<(T.Iterator.Element, U)> { | |
var Iterator1 = sequence.makeIterator() | |
var sum = U.implicitDefault() | |
return AnyIterator { | |
let element1 = Iterator1.next() | |
switch element1 { | |
case let e1?: | |
sum = sum + (self.sumFrom(e1) ?? U.implicitDefault()) | |
return (e1, sum) | |
case .none: | |
return nil | |
} | |
} | |
} | |
} | |
extension Sequence { | |
func zipWithSumFrom<U>(_ valueFunction: @escaping (Self.Iterator.Element) -> U?) -> ZipWithSumFrom<Self, U> where | |
U: Addable, | |
U: ImplicitDefault | |
{ | |
return ZipWithSumFrom(sequence: self, sumFrom: valueFunction) | |
} | |
} | |
// ---------------------------------------------------------------------------------------------------- | |
// MARK: - 4. ZipWithDiff | |
// | |
// a sequence to get the diff with the last | |
// ---------------------------------------------------------------------------------------------------- | |
/// over and over again ... what if the element supports Subtractable? | |
struct ZipWithDiff<T>: Sequence where | |
T: Sequence, | |
T.Iterator.Element: ImplicitDefault, | |
T.Iterator.Element: Subtractable | |
{ | |
let sequence: T | |
func makeIterator() -> AnyIterator<(T.Iterator.Element, T.Iterator.Element)> { | |
var Iterator1 = sequence.makeIterator() | |
var last = T.Iterator.Element.implicitDefault() | |
return AnyIterator { | |
let element1 = Iterator1.next() | |
switch element1 { | |
case let e1?: | |
let diff = e1 - last | |
last = e1 | |
return (e1, diff) | |
case .none: | |
return nil | |
} | |
} | |
} | |
} | |
extension Sequence where | |
Self.Iterator.Element: ImplicitDefault, | |
Self.Iterator.Element: Subtractable { | |
func zipWithDiff() -> ZipWithDiff<Self>{ | |
return ZipWithDiff(sequence: self) | |
} | |
} | |
// ---------------------------------------------------------------------------------------------------- | |
// MARK: - 5. Example A | |
// ---------------------------------------------------------------------------------------------------- | |
/// Yes, you don`t need the Iterators, you can master the mess | |
chapter("Yes, you don`t need the Iterators, you can master the mess") | |
var sum = 0 | |
for i in stride(from:0, to: 100, by: 1) { | |
sum = sum + i | |
print(i, sum) | |
} | |
// No, you can do way better | |
chapter("No, you can do way better") | |
for (i, sum) in stride(from:0, to: 100, by: 1).zipWithSum() { | |
print(i, sum) | |
} | |
// ---------------------------------------------------------------------------------------------------- | |
// MARK: - 6. Example B | |
// | |
// But my stuff is complicated i can`t use a simplistic Iterator... | |
// ---------------------------------------------------------------------------------------------------- | |
struct Data { | |
let name: String | |
let data: Int | |
} | |
let lotsOfData = [ | |
Data(name: "a", data: 2), | |
Data(name: "b", data: 4), | |
Data(name: "b", data: 6), | |
Data(name: "b", data: 7), | |
Data(name: "a", data: 1) | |
] | |
/// Yes, you don`t need the Iterators, you can master the mess | |
chapter("look ma no Iterators, even with structs and ifs") | |
var sum2 = 0 | |
for data in lotsOfData { | |
// sum only if 'b' | |
if data.name == "b" { | |
sum2 = sum2 + data.data | |
} | |
print(data, sum2) | |
} | |
// No, you can do way better | |
let allBsData: (Data) -> Int? = { $0.name == "b" ? $0.data : nil } | |
print("all right - this is ok") | |
for (data, sum) in lotsOfData.zipWithSumFrom(allBsData) { | |
print(sum, data) | |
} | |
// ---------------------------------------------------------------------------------------------------- | |
// MARK: - 7. Example C | |
// ---------------------------------------------------------------------------------------------------- | |
// yeah i admit, it`s a little bit awkward this time - its even super ridiculous! | |
// but we find a better way soon | |
let first: (Int, Int) -> Int? = { $0.0 } | |
let snd: ((Int, Int), Int) -> Int? = { $0.0.1 } | |
chapter("diff and sum and sum diif - all at once ") | |
for (((i, diff), sum), sumDiff) in [20,10,40,122,121,12,333,11,212,12].zipWithDiff().zipWithSumFrom(first).zipWithSumFrom(snd) { | |
print("\(i) comparing to previous ", diff , sum, sumDiff) | |
} | |
// ---------------------------------------------------------------------------------------------------- | |
// MARK: - 8. Fold Types | |
// ---------------------------------------------------------------------------------------------------- | |
protocol Foldable { | |
associatedtype B | |
associatedtype A | |
var initial: A { get } | |
func fold(a: A, b: B) -> A | |
} | |
struct Min<A: Comparable>: Foldable{ | |
init(_ initial: A) { | |
self.initial = initial | |
} | |
let initial: A | |
func fold(a: A, b: A) -> A { | |
return a < b ? a : b | |
} | |
} | |
struct Sum<A: Addable>: Foldable { | |
init(_ initial: A) { | |
self.initial = initial | |
} | |
let initial: A | |
func fold(a: A, b: A) -> A { | |
return a + b | |
} | |
} | |
struct Worst<A: Comparable>: Foldable { | |
init(_ count: Int) { | |
self.initial = [] | |
self.count = count | |
} | |
let initial: [A] | |
let count: Int | |
func fold(a: [A], b: A) -> [A] { | |
guard !a.isEmpty else { return [b] } | |
var ax = Array(a) | |
ax.append(b) | |
return Array(ax.sorted().prefix(count)) | |
} | |
} | |
struct Both<F1, F2, A>: Foldable where | |
F1: Foldable, | |
F2: Foldable, | |
F1.B == A, | |
F2.B == A | |
{ | |
let folders: (F1, F2) | |
init(_ folders: (F1, F2)) { | |
self.folders = folders | |
self.initial = (folders.0.initial, folders.1.initial) | |
} | |
let initial: (F1.A, F2.A) | |
func fold(a: (F1.A, F2.A), b: A) -> (F1.A, F2.A) { | |
let one = folders.0.fold(a: a.0, b: b) | |
let two = folders.1.fold(a: a.1, b: b) | |
return (one, two) | |
} | |
} | |
// ---------------------------------------------------------------------------------------------------- | |
// MARK: - 9. Zip with Fold | |
// ---------------------------------------------------------------------------------------------------- | |
struct ZipFold<S, F>: Sequence where | |
S: Sequence, | |
F: Foldable, | |
S.Iterator.Element == F.B | |
{ | |
let sequence: S | |
let f: F | |
func makeIterator() -> AnyIterator<(S.Iterator.Element, F.A)> { | |
var Iterator = sequence.makeIterator() | |
var fold = f.initial | |
return AnyIterator { | |
let element = Iterator.next() | |
switch element { | |
case let e?: | |
let b = self.f.fold(a: fold, b: e) | |
fold = b | |
return (e, b) | |
case .none: | |
return nil | |
} | |
} | |
} | |
} | |
extension Sequence { | |
func zipFold<F: Foldable>(f: F) -> ZipFold<Self, F> where F.B == Self.Iterator.Element { | |
return ZipFold(sequence: self, f: f) | |
} | |
} | |
// ---------------------------------------------------------------------------------------------------- | |
// MARK: - 10. Example zip with fold | |
// ---------------------------------------------------------------------------------------------------- | |
let list = [1, 4, 10, 15, -4, -10, 0, -3, 8, 22, 55, -14, -24, 0] | |
// Sum | |
chapter("What does the result for sum look like?") | |
for (i, sum) in list.zipFold(f: Sum(0)) { | |
print(i, sum) | |
} | |
// Sum 100 | |
chapter("Want to start the sum at 100?") | |
for (i, sum) in list.zipFold(f: Sum(100)) { | |
print(i, sum) | |
} | |
// Min | |
chapter("Want to see the min value up to each position?") | |
for (i, min) in list.zipFold(f: Min(0)) { | |
print(i, min) | |
} | |
// Worst 3 | |
chapter("Want to see the worst three values on each position?") | |
for (i, worstThree) in list.zipFold(f: Worst(3)) { | |
print(i, worstThree) | |
} | |
// Min and sum | |
chapter("Want to see the Min and the Sum at each position?") | |
let minSum = Both((Min(0), Sum(0))) | |
for (i, (min, sum)) in list.zipFold(f: minSum) { | |
print(i, min, sum) | |
} | |
chapter("What is the lowest value when we reach a sum of 100?") | |
for (i, (min, sum)) in list.zipFold(f: minSum) where sum > 100 { | |
print(i, min, sum) | |
} | |
// still the best way... :-P | |
chapter("Still the best way?") | |
var sumx = 0 | |
var minx = 0 | |
for i in list { | |
if i < minx { | |
minx = i | |
} | |
sumx = sumx + i | |
print(i, sumx, minx) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
swift 3 update