Skip to content

Instantly share code, notes, and snippets.

@elm4ward
Last active February 3, 2018 23:37
Show Gist options
  • Save elm4ward/3b131ede44e1f5df5ed6d49b95b28de2 to your computer and use it in GitHub Desktop.
Save elm4ward/3b131ede44e1f5df5ed6d49b95b28de2 to your computer and use it in GitHub Desktop.
// ----------------------------------------------------------------------------------------------------
// 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)
@elm4ward
Copy link
Author

updated to swift 3 / simplification

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