Last active
August 10, 2020 04:49
-
-
Save Cortado-J/69bbd8a79037832be22e9e4509bcc05f to your computer and use it in GitHub Desktop.
Enables simple refactoring of lazy processing of 'streams' of values.
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
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< | |
///<<< Support Code for Lazy Streams | |
///<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< | |
/// | |
/// Enables simple refactoring of lazy processing of 'streams' of values. | |
/// A stream is taken here ot mean a lazy sequence. | |
/// | |
/// The code is not generic so if streams of different types are required | |
/// then extra sctions will be needed. | |
/// Simple to construct for a new type T though: | |
/// 1) Make a copy of the "standard code" whose type is nearest to T. | |
/// 2) Change types in the code from old type to T. | |
/// 3) If necessary create type conversions. See section below: "Conversion between types of streams" | |
///<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< | |
/// <-<-<-<-<-<-<-<-<-<-<-< | |
/// Stream of Ints (Standard code) | |
/// <-<-<-<-<-<-<-<-<-<-<-< | |
protocol StreamOfInts: LazySequenceProtocol where Element == Int {} | |
extension LazySequence: StreamOfInts where Element == Int {} | |
extension LazyMapSequence: StreamOfInts where Element == Int {} | |
extension LazyFilterSequence: StreamOfInts where Element == Int {} | |
extension StreamOfInts { | |
/// Ensure unique Int | |
/// Uses Store struct - see below. | |
func unique(in store: Store<Int>) -> some StreamOfInts { | |
filter{ store.unique($0) } | |
} | |
/// Allow debugging as in: .debug{ " Value= \($0)" } | |
func debug(_ perform: @escaping (Int) -> String) -> some StreamOfInts { | |
map{ | |
print(perform($0)) | |
return $0 | |
} | |
} | |
} | |
/// <-<-<-<-<-<-<-<-<-<-<-< | |
/// <-<-<-<-<-<-<-<-<-<-<-< | |
/// Stream of Strings (Standard code) | |
/// <-<-<-<-<-<-<-<-<-<-<-< | |
protocol StreamOfStrings: LazySequenceProtocol where Element == String {} | |
extension LazySequence: StreamOfStrings where Element == String {} | |
extension LazyMapSequence: StreamOfStrings where Element == String {} | |
extension LazyFilterSequence: StreamOfStrings where Element == String {} | |
extension StreamOfStrings { | |
/// Ensure unique Int | |
/// Uses Store struct - see below. | |
func unique(in store: Store<String>) -> some StreamOfStrings { | |
filter{ store.unique($0) } | |
} | |
/// Allow debugging as in: .debug{ " Value= \($0)" } | |
func debug(_ perform: @escaping (String) -> String) -> some StreamOfStrings { | |
map{ | |
print(perform($0)) | |
return $0 | |
} | |
} | |
} | |
/// <-<-<-<-<-<-<-<-<-<-<-< | |
/// <-<-<-<-<-<-<-<-<-<-<-< | |
/// Conversion between types of streams | |
/// <-<-<-<-<-<-<-<-<-<-<-< | |
/// Convert Int to String | |
extension StreamOfInts { | |
func asString() -> some StreamOfStrings { | |
map{ String($0) } | |
} | |
} | |
/// Convert String to Int | |
extension StreamOfStrings { | |
func asInt() -> some StreamOfInts { | |
compactMap{ Int($0) } | |
} | |
} | |
/// <-<-<-<-<-<-<-<-<-<-<-< | |
/// <-<-<-<-<-<-<-<-<-<-<-< | |
/// Uniqueness filtering | |
/// <-<-<-<-<-<-<-<-<-<-<-< | |
/// We want to allow checking for uniqueness of values in a stream and allow filtering out | |
/// any repeated values. | |
/// In eager environments we can just do for example Array(Set(array)) but here we are staying lazy | |
/// so we'll need to store elements that have gone before and compare any new elements with the store. | |
/// Here's the store | |
class Store<T> where T: Hashable { | |
var history: Set<T> = [] | |
func reset() { | |
history = [] | |
} | |
func unique(_ value: T) -> Bool { | |
if history.contains(value) { return false } | |
history.insert(value) | |
return true | |
} | |
} | |
/// <-<-<-<-<-<-<-<-<-<-<-< | |
///<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< | |
///<<< End of Support code | |
///<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< | |
extension StreamOfInts { | |
func squaredAndCopyIncremented() -> some StreamOfInts { | |
map{ $0 * $0 } | |
.flatMap{ [$0, $0+1] } | |
} | |
} | |
extension StreamOfStrings { | |
var dropFirstAndLast: some StreamOfStrings { | |
map{ String($0.dropFirst().dropLast()) } | |
} | |
} | |
func testLazySequence() { | |
let numbers = 1...10 | |
let result: some StreamOfInts | |
= numbers | |
.lazy | |
.debug{ "A: \($0) = Started\( $0 >= 5 ? "" : " dropped in next step:")" } | |
.filter{ $0 >= 5 } | |
.debug{ "B: \($0) = Keep >= 5" } | |
.squaredAndCopyIncremented() | |
.debug{ "C: \($0) = Squared and Copy Incremented" } | |
.map{ $0 * 2 } | |
.debug{ "D: \($0) = Multiplied by 2" } | |
.squaredAndCopyIncremented() | |
.debug{ "E: \($0) = Squared and Copy Incremented again! \( $0 % 3 != 0 ? "" : " dropped in next step:")" } | |
.filter{ $0 % 3 != 0 } | |
.debug{ "F: \($0) = Keep non-multiples of 3" } | |
.asString() | |
.debug{ "G: \($0) = As String" } | |
.dropFirstAndLast | |
.debug{ "H: \($0) = Dropped First and Last" } | |
.unique(in: Store<String>()) /// Filter out any which have previously been found in this stream of Strings | |
.debug{ "I: \($0) = Unique check" } | |
.asInt() | |
.debug{ "J: \($0) = As Int again" } | |
.unique(in: Store<Int>()) /// Filter out any which have previously been found in this stream of Ints | |
.debug{ "K: \($0) = Unique check" } | |
print(Array(result)) | |
} | |
testLazySequence() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Question was asked about this on Stack Overflow which led to this Gist: https://stackoverflow.com/questions/59675790/refactoring-lazy-functional-code-in-swift-5/59676583#59676583