Skip to content

Instantly share code, notes, and snippets.

@elm4ward
Last active January 5, 2019 01:45
Show Gist options
  • Save elm4ward/eb73f85bf2a08b62b0d43301c81ece42 to your computer and use it in GitHub Desktop.
Save elm4ward/eb73f85bf2a08b62b0d43301c81ece42 to your computer and use it in GitHub Desktop.
// ----------------------------------------------------------------------------------------------------
// 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)
}
@elm4ward
Copy link
Author

swift 3 update

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment