-
Notifications
You must be signed in to change notification settings - Fork 179
Add recursiveMap(_:)
methods
#118
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 6 commits
27f397a
79b0aae
84cccba
eaff804
38199a0
6774fc6
5d63ffe
de6e51b
e01a2ae
a9fffdd
db8f025
4aebd4c
80cd630
edeeffd
8588e71
c7e313e
7f0afd0
2f398cf
5135e3d
fd31319
179c924
864bd80
9f43c56
8ecc2fb
25f855f
a669b68
2e05d91
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
# RecursiveMap | ||
|
||
* Author(s): [Susan Cheng](https://github.com/SusanDoggie) | ||
|
||
[ | ||
[Source](https://github.com/apple/swift-async-algorithms/blob/main/Sources/AsyncAlgorithms/AsyncRecursiveMapSequence.swift) | | ||
[Tests](https://github.com/apple/swift-async-algorithms/blob/main/Tests/AsyncAlgorithmsTests/TestRecursiveMap.swift) | ||
] | ||
|
||
Produces a sequence containing the original sequence followed by recursive mapped sequence. | ||
|
||
```swift | ||
struct Node { | ||
var id: Int | ||
var children: [Node] = [] | ||
} | ||
let tree = [ | ||
Node(id: 1, children: [ | ||
Node(id: 3), | ||
Node(id: 4, children: [ | ||
Node(id: 6), | ||
]), | ||
Node(id: 5), | ||
]), | ||
Node(id: 2), | ||
] | ||
for await node in tree.async.recursiveMap({ $0.children.async }) { | ||
print(node.id) | ||
} | ||
// 1 | ||
// 2 | ||
// 3 | ||
// 4 | ||
// 5 | ||
// 6 | ||
``` | ||
|
||
## Detailed Design | ||
|
||
The `recursiveMap(_:)` method is declared as `AsyncSequence` extensions, and return `AsyncRecursiveMapSequence` or `AsyncThrowingRecursiveMapSequence` instance: | ||
|
||
```swift | ||
extension AsyncSequence { | ||
public func recursiveMap<S>( | ||
_ transform: @Sendable @escaping (Element) async -> S | ||
) -> AsyncRecursiveMapSequence<Self, S> | ||
|
||
public func recursiveMap<S>( | ||
_ transform: @Sendable @escaping (Element) async throws -> S | ||
) -> AsyncThrowingRecursiveMapSequence<Self, S> | ||
} | ||
``` | ||
|
||
### Complexity | ||
|
||
Calling this method is O(_1_). |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the Swift Async Algorithms open source project | ||
// | ||
// Copyright (c) 2022 Apple Inc. and the Swift project authors | ||
// Licensed under Apache License v2.0 with Runtime Library Exception | ||
// | ||
// See https://swift.org/LICENSE.txt for license information | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
extension AsyncSequence { | ||
/// Returns a sequence containing the original sequence followed by recursive mapped sequence. | ||
/// | ||
/// ``` | ||
/// struct Node { | ||
/// var id: Int | ||
/// var children: [Node] = [] | ||
/// } | ||
/// let tree = [ | ||
/// Node(id: 1, children: [ | ||
/// Node(id: 3), | ||
/// Node(id: 4, children: [ | ||
/// Node(id: 6), | ||
/// ]), | ||
/// Node(id: 5), | ||
/// ]), | ||
/// Node(id: 2), | ||
/// ] | ||
/// for await node in tree.async.recursiveMap({ $0.children.async }) { | ||
/// print(node.id) | ||
/// } | ||
/// // 1 | ||
/// // 2 | ||
/// // 3 | ||
/// // 4 | ||
/// // 5 | ||
/// // 6 | ||
/// ``` | ||
/// | ||
/// - Parameters: | ||
/// - transform: A closure that map the element to new sequence. | ||
/// - Returns: A sequence of the original sequence followed by recursive mapped sequence. | ||
@inlinable | ||
public func recursiveMap<C>(_ transform: @Sendable @escaping (Element) async -> C) -> AsyncRecursiveMapSequence<Self, C> { | ||
return AsyncRecursiveMapSequence(self, transform) | ||
} | ||
} | ||
|
||
public struct AsyncRecursiveMapSequence<Base: AsyncSequence, Transformed: AsyncSequence>: AsyncSequence where Base.Element == Transformed.Element { | ||
|
||
public typealias Element = Base.Element | ||
|
||
@usableFromInline | ||
let base: Base | ||
|
||
@usableFromInline | ||
let transform: @Sendable (Base.Element) async -> Transformed | ||
|
||
@inlinable | ||
init(_ base: Base, _ transform: @Sendable @escaping (Base.Element) async -> Transformed) { | ||
self.base = base | ||
self.transform = transform | ||
} | ||
|
||
@inlinable | ||
public func makeAsyncIterator() -> AsyncIterator { | ||
return AsyncIterator(base, transform) | ||
} | ||
} | ||
|
||
extension AsyncRecursiveMapSequence { | ||
|
||
public struct AsyncIterator: AsyncIteratorProtocol { | ||
|
||
@usableFromInline | ||
var base: Base.AsyncIterator? | ||
|
||
@usableFromInline | ||
var mapped: ArraySlice<Transformed> = [] | ||
|
||
|
||
@usableFromInline | ||
var mapped_iterator: Transformed.AsyncIterator? | ||
|
||
@usableFromInline | ||
var transform: @Sendable (Base.Element) async -> Transformed | ||
|
||
@inlinable | ||
init(_ base: Base, _ transform: @Sendable @escaping (Base.Element) async -> Transformed) { | ||
self.base = base.makeAsyncIterator() | ||
self.transform = transform | ||
} | ||
|
||
@inlinable | ||
public mutating func next() async rethrows -> Base.Element? { | ||
|
||
|
||
if self.base != nil { | ||
|
||
if let element = try await self.base?.next() { | ||
await mapped.append(transform(element)) | ||
return element | ||
} | ||
|
||
self.base = nil | ||
self.mapped_iterator = mapped.popFirst()?.makeAsyncIterator() | ||
} | ||
|
||
while self.mapped_iterator != nil { | ||
|
||
if let element = try await self.mapped_iterator?.next() { | ||
await mapped.append(transform(element)) | ||
return element | ||
} | ||
|
||
self.mapped_iterator = mapped.popFirst()?.makeAsyncIterator() | ||
} | ||
|
||
return nil | ||
} | ||
} | ||
} | ||
|
||
extension AsyncRecursiveMapSequence: Sendable | ||
where Base: Sendable, Base.Element: Sendable, Transformed: Sendable { } | ||
|
||
extension AsyncRecursiveMapSequence.AsyncIterator: Sendable | ||
where Base.AsyncIterator: Sendable, Base.Element: Sendable, Transformed: Sendable, Transformed.AsyncIterator: Sendable { } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the Swift Async Algorithms open source project | ||
// | ||
// Copyright (c) 2022 Apple Inc. and the Swift project authors | ||
// Licensed under Apache License v2.0 with Runtime Library Exception | ||
// | ||
// See https://swift.org/LICENSE.txt for license information | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
extension AsyncSequence { | ||
/// Returns a sequence containing the original sequence followed by recursive mapped sequence. | ||
/// | ||
/// ``` | ||
/// struct Node { | ||
/// var id: Int | ||
/// var children: [Node] = [] | ||
/// } | ||
/// let tree = [ | ||
/// Node(id: 1, children: [ | ||
/// Node(id: 3), | ||
/// Node(id: 4, children: [ | ||
/// Node(id: 6), | ||
/// ]), | ||
/// Node(id: 5), | ||
/// ]), | ||
/// Node(id: 2), | ||
/// ] | ||
/// for await node in tree.async.recursiveMap({ $0.children.async }) { | ||
/// print(node.id) | ||
/// } | ||
/// // 1 | ||
/// // 2 | ||
/// // 3 | ||
/// // 4 | ||
/// // 5 | ||
/// // 6 | ||
/// ``` | ||
/// | ||
/// - Parameters: | ||
/// - transform: A closure that map the element to new sequence. | ||
/// - Returns: A sequence of the original sequence followed by recursive mapped sequence. | ||
@inlinable | ||
public func recursiveMap<C>(_ transform: @Sendable @escaping (Element) async throws -> C) -> AsyncThrowingRecursiveMapSequence<Self, C> { | ||
return AsyncThrowingRecursiveMapSequence(self, transform) | ||
} | ||
} | ||
|
||
public struct AsyncThrowingRecursiveMapSequence<Base: AsyncSequence, Transformed: AsyncSequence>: AsyncSequence where Base.Element == Transformed.Element { | ||
|
||
public typealias Element = Base.Element | ||
|
||
@usableFromInline | ||
let base: Base | ||
|
||
@usableFromInline | ||
let transform: @Sendable (Base.Element) async throws -> Transformed | ||
|
||
@inlinable | ||
init(_ base: Base, _ transform: @Sendable @escaping (Base.Element) async throws -> Transformed) { | ||
self.base = base | ||
self.transform = transform | ||
} | ||
|
||
@inlinable | ||
public func makeAsyncIterator() -> AsyncIterator { | ||
return AsyncIterator(base, transform) | ||
} | ||
} | ||
|
||
extension AsyncThrowingRecursiveMapSequence { | ||
|
||
public struct AsyncIterator: AsyncIteratorProtocol { | ||
|
||
@usableFromInline | ||
var base: Base.AsyncIterator? | ||
|
||
@usableFromInline | ||
var mapped: ArraySlice<Transformed> = [] | ||
|
||
@usableFromInline | ||
var mapped_iterator: Transformed.AsyncIterator? | ||
|
||
@usableFromInline | ||
var transform: @Sendable (Base.Element) async throws -> Transformed | ||
|
||
@inlinable | ||
init(_ base: Base, _ transform: @Sendable @escaping (Base.Element) async throws -> Transformed) { | ||
self.base = base.makeAsyncIterator() | ||
self.transform = transform | ||
} | ||
|
||
@inlinable | ||
public mutating func next() async throws -> Base.Element? { | ||
|
||
if self.base != nil { | ||
|
||
if let element = try await self.base?.next() { | ||
try await mapped.append(transform(element)) | ||
return element | ||
} | ||
|
||
self.base = nil | ||
self.mapped_iterator = mapped.popFirst()?.makeAsyncIterator() | ||
} | ||
|
||
while self.mapped_iterator != nil { | ||
|
||
if let element = try await self.mapped_iterator?.next() { | ||
try await mapped.append(transform(element)) | ||
|
||
return element | ||
} | ||
|
||
self.mapped_iterator = mapped.popFirst()?.makeAsyncIterator() | ||
} | ||
|
||
return nil | ||
} | ||
} | ||
} | ||
|
||
extension AsyncThrowingRecursiveMapSequence: Sendable | ||
where Base: Sendable, Base.Element: Sendable, Transformed: Sendable { } | ||
|
||
extension AsyncThrowingRecursiveMapSequence.AsyncIterator: Sendable | ||
where Base.AsyncIterator: Sendable, Base.Element: Sendable, Transformed: Sendable, Transformed.AsyncIterator: Sendable { } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it might be good here to give a bit more detail, particularly things that are helpful: how does it work with cancellation, identifying the Sensibility requirements, how do they interact with rethrows etc is good to record in the guides.
Also links to existing counterparts etc.