Last active
February 3, 2018 23:37
-
-
Save elm4ward/3b131ede44e1f5df5ed6d49b95b28de2 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
// ---------------------------------------------------------------------------------------------------- | |
// MARK: - Lazy comonadic array zipper | |
// | |
// http://elm4ward.github.io/swift/functional/comonad/2016/04/06/da-lazy-one.html | |
// | |
// Stacking functions lazily can speed up your code. | |
// | |
// 1. The struct | |
// 2. Lazy in action | |
// ---------------------------------------------------------------------------------------------------- | |
// ---------------------------------------------------------------------------------------------------- | |
// MARK: - 1. The struct | |
// ---------------------------------------------------------------------------------------------------- | |
struct ArrayZipper<S> where S: Collection, S.Index == Int { | |
let elements: S | |
let at: S.Index | |
/// Move the zipper to next position | |
func moveTo(_ position: S.Index) -> ArrayZipper<S> { | |
guard position >= elements.startIndex && position <= elements.endIndex else { return self } | |
return ArrayZipper(elements: elements, at: position) | |
} | |
/// map each value in the structure of the zipper | |
func map<U>(_ f: @escaping (S.Iterator.Element) -> U) -> ArrayZipper<LazyMapCollection<S, U>> { | |
let l: LazyMapCollection<S, U> = elements.lazy.map(f) | |
return ArrayZipper<LazyMapCollection<S, U>>(elements: l, at: at) | |
} | |
/// get the duplicated structure and map with extend function which will 'reduce' the | |
/// ArrayZipper<ArrayZipper<T>> to ArrayZipper<S> | |
func extend<U>(_ f: @escaping (ArrayZipper<S>) -> U) -> ArrayZipper<LazyMapCollection<LazyMapRandomAccessCollection<CountableRange<S.Index>, ArrayZipper<S>>, U>> { | |
return duplicate().map(f) | |
} | |
/// duplicate the structure and move at each position | |
/// will build a nested ArrayZipper | |
func duplicate() -> ArrayZipper<LazyMapRandomAccessCollection<CountableRange<S.Index>, ArrayZipper<S>>> { | |
let zippers = (elements.startIndex..<elements.endIndex).lazy.map(moveTo) | |
return ArrayZipper<LazyMapRandomAccessCollection<CountableRange<S.Index>, ArrayZipper<S>>>(elements: zippers, at:at) | |
} | |
func extract() -> S.Iterator.Element { | |
return elements[at] | |
} | |
} | |
// ---------------------------------------------------------------------------------------------------- | |
// MARK: - 2. Lazy in action | |
// ---------------------------------------------------------------------------------------------------- | |
func avg<L: Sequence>(radius: L.Index) -> (ArrayZipper<L>) -> Int where | |
L.Iterator.Element == Int, | |
L.Index == Int | |
{ | |
return { a in | |
var es: [Int] = [] | |
for i in (a.at-radius...a.at+radius){ | |
guard i > a.elements.startIndex && i < a.elements.endIndex else { continue } | |
es.append(a.elements[i]) | |
} | |
print("average of", es) | |
guard !es.isEmpty else { return 0 } | |
return es.reduce(0, +) / es.count | |
} | |
} | |
let base = [1, 10, 100, 1_000, 10_000, 100_000, 1_000_000, 10_000_000, 100_000_000] | |
let zipper = ArrayZipper(elements: base, at: 0) | |
let z = zipper | |
.moveTo(5) | |
.extend(avg(radius:1)) | |
.extend(avg(radius:2)) // 5 averages of 3 averages | |
.extract() | |
print(z) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
updated to swift 3 / simplification