diff --git a/Package.swift b/Package.swift index 6c0a5d3..97d6d37 100644 --- a/Package.swift +++ b/Package.swift @@ -13,8 +13,16 @@ let package = Package( .library(name: "Beton", targets: ["Beton"]), .library(name: "XCTBeton", targets: ["XCTBeton"]), ], + dependencies: [ + .package(url: "https://github.com/apple/swift-async-algorithms.git", from: "0.1.0"), + ], targets: [ - .target(name: "Beton"), + .target( + name: "Beton", + dependencies: [ + .product(name: "AsyncAlgorithms", package: "swift-async-algorithms"), + ] + ), .target( name: "XCTBeton", dependencies: [ diff --git a/Sources/Beton/Collections/repeating.swift b/Sources/Beton/Collections/repeating.swift new file mode 100644 index 0000000..8cb4aa4 --- /dev/null +++ b/Sources/Beton/Collections/repeating.swift @@ -0,0 +1,91 @@ +import Foundation +import AsyncAlgorithms + +// MARK: Mappers + +/// Runs the provided callback consecutively one after another, +/// passing the current iteration count as an argument, +/// and returning the array of results in iteration order. +/// - Parameters: +/// - count: Number of times to run the given function. +/// - function: The callback to execute mapping its results to the final result. +/// - Returns: An array of all the values returned by the callback. +public func repeating(count: Int, _ function: (Int) -> T) -> [T] { + (0.. = AsyncChannel<(iteration: Int, result: T)> where T: Sendable +/// Runs the provided callback asynchronously, +/// passing the current iteration count as an argument, +/// and returning an AsyncSequence of the results. +/// **This function is by design out-of-order.** +/// - Parameters: +/// - count: Number of times to run the given function. +/// - function: The callback to execute, mapping results to the final result. +/// - Returns: A channel of results. +public func repeating( + count: Int, + _ function: @escaping (Int) async -> T +) async -> AsyncRepeatingChannel where T: Sendable { + let channel = AsyncRepeatingChannel() + + Task { + await withTaskGroup(of: Void.self) { group in + for i in 0..( + count: Int, + _ function: @autoclosure () -> T +) -> [T] { + repeating(count: count) { _ in function() } +} + +/// Runs the provided callback asynchronously, returning the results. +/// **This function is by design out-of-order.** +/// - Parameters: +/// - count: Number of times to run the given function. +/// - function: The callback to execute mapping its results to the final result. +/// - Returns: A channel of all the values returned by the callback. +public func repeating( + count: Int, + _ function: @escaping @autoclosure () async -> T +) async -> AsyncRepeatingChannel where T: Sendable { + await repeating(count: count) { _ in await function()} +} + + +// MARK: Performers + +/// Runs the provided callback `count` number of times, ignoring its results. +/// - Parameters: +/// - count: Number of times to run the given function. +/// - function: The callback to execute `count` times. +public func repeating(count: Int, _ function: @autoclosure () -> ()) { + let _ = repeating(count: count) { [function] _ in function() } +} + +/// Runs asynchronously the provided callback `count` number of times, ignoring the results. +/// - Parameters: +/// - count: Number of times to run the given function. +/// - function: The callback to execute `count` times +public func repeating( + count: Int, + _ function: @autoclosure @escaping () async -> () +) async { + let _ = await repeating(count: count) { _ in await function() } +} diff --git a/Tests/PerformanceTests/Beton/Bundle/BundleTest.swift b/Tests/PerformanceTests/Beton/Bundle/BundleTest.swift index e13fe20..7288eff 100644 --- a/Tests/PerformanceTests/Beton/Bundle/BundleTest.swift +++ b/Tests/PerformanceTests/Beton/Bundle/BundleTest.swift @@ -5,7 +5,7 @@ class BundleTest: XCTestCase { override func tearDown() { super.tearDown() XCTAssertMetric(.cpu, .cycles, .average(maximum: 5000)) - XCTAssertMetric(.cpu, .instructionsRetired, .average(maximum: 4500)) + XCTAssertMetric(.cpu, .instructionsRetired, .average(maximum: 10_000)) XCTAssertMetric(.cpu, .time, .average(maximum: 0.002)) XCTAssertMetric(.memory, .physical, .average(maximum: 60)) XCTAssertMetric(.memory, .physicalPeak, .average(maximum: 0)) @@ -15,25 +15,25 @@ class BundleTest: XCTestCase { func testLocalizationBundles() { measure(metrics: .defaults) { - Bundle.module.localizationBundles + let _ = Bundle.module.localizationBundles } } func testLocalizedString_keyOnly() { measure(metrics: .defaults) { - Bundle.module.localizedString("Test") + let _ = Bundle.module.localizedString("Test") } } func testLocalizedString_keyAndTableOnly() { measure(metrics: .defaults) { - Bundle.module.localizedString("Test", from: "Test") + let _ = Bundle.module.localizedString("Test", from: "Test") } } func testLocalizedString_keyAndValueOnly() { measure(metrics: .defaults) { - Bundle.module.localizedString("Test", fallback: "Test") + let _ = Bundle.module.localizedString("Test", fallback: "Test") } } diff --git a/Tests/UnitTests/Beton/Collections/repeatingTests.swift b/Tests/UnitTests/Beton/Collections/repeatingTests.swift new file mode 100644 index 0000000..5b92653 --- /dev/null +++ b/Tests/UnitTests/Beton/Collections/repeatingTests.swift @@ -0,0 +1,46 @@ +@testable import Beton +import XCTBeton + +class RepeatingTests: XCTestCase { + func testRepeating_sync_mapper () { + XCTAssertEqual(repeating(count: 3) { $0 + 5 }, [5, 6, 7]) + XCTAssertEqual(repeating(count: 3) { $0 + 10 }, [10, 11, 12]) + XCTAssertEqual(repeating(count: 5) { $0 + 5 }, [5, 6, 7, 8, 9]) + XCTAssertEqual(repeating(count: 5) { $0 + 10 }, [10, 11, 12, 13, 14]) + } + + func testRepeating_sync_resolver () { + XCTAssertEqual(repeating(count: 3, 5), [5, 5, 5,]) + XCTAssertEqual(repeating(count: 3, 10), [10, 10, 10]) + XCTAssertEqual(repeating(count: 5, 5), [5, 5, 5, 5, 5,]) + XCTAssertEqual(repeating(count: 5, 10), [10, 10, 10, 10, 10]) + } + + func testRepeating_sync_performer() { + var counter = 0 + + repeating(count: 3, counter += 1) + XCTAssertEqual(counter, 3) + + repeating(count: 5, counter += 2) + XCTAssertEqual(counter, 13) + } + + func testRepeating_async_mapper() async throws { + for try await item in await repeating(count: 10, { $0 + 5 }) { + XCTAssertEqual(item.result, item.iteration + 5) + } + } + + func testRepeating_async_resolver() async throws { + for try await item in await repeating(count: 10, 100) { + XCTAssertEqual(item.result, 100) + } + } + + func testRepeating_async_performer() async throws { + var a = [Int]() + await repeating(count: 13, a.append(1)) + print(a) + } +}