CommandLineToolkit
141 строка · 4.3 Кб
1import Foundation
2import Types
3
4extension Collection where Self.Index == Int {
5public func throwingConcurrentMap<T, R>(
6mapping: (T) throws -> (R)
7) throws -> [R] where T == Element {
8return try returnOutput(
9results: concurrentMap { arg -> Either<R, Error> in
10do {
11return try Either.success(mapping(arg))
12} catch {
13return Either.error(error)
14}
15}
16)
17}
18
19public func throwingConcurrentReduce<T, K, R>(
20_ initialValue: R,
21mapping: (T) throws -> (K),
22reduction: (inout R, K) throws -> ()
23) throws -> R where T == Element {
24let result = concurrentReduce(
25([Error](), initialValue),
26mapping: { arg -> Either<K, Error> in
27do {
28return try Either.success(mapping(arg))
29} catch {
30return Either.error(error)
31}
32},
33reduction: { errorsAndAccumulator, arg in
34switch arg {
35case let .left(output):
36do {
37try reduction(&errorsAndAccumulator.1, output)
38} catch {
39errorsAndAccumulator.0.append(error)
40}
41case let .right(error):
42errorsAndAccumulator.0.append(error)
43}
44}
45)
46let errors = result.0
47guard errors.isEmpty else {
48throw CompoundError(errors: errors)
49}
50return result.1
51}
52
53public func throwingConcurrentForEach<T>(
54perform: (T) throws -> ()
55) throws where T == Element {
56_ = try throwingConcurrentMap(mapping: { try perform($0) })
57}
58
59public func concurrentMap<T, R>(
60mapping: (T) -> (R)
61) -> [R] where T == Element {
62var mappingResult = [R?](repeating: nil, count: count)
63let lock = NSLock()
64DispatchQueue.concurrentPerform(
65iterations: count,
66execute: { index in
67let iterationResult = mapping(self[index])
68lock.lock()
69mappingResult[index] = iterationResult
70lock.unlock()
71}
72)
73return mappingResult.compactMap { $0 }
74}
75
76public func concurrentCompactMap<T, R>(
77mapping: (T) -> (R?)
78) -> [R] where T == Element {
79var mappingResult = [R?](repeating: nil, count: count)
80let lock = NSLock()
81DispatchQueue.concurrentPerform(
82iterations: count,
83execute: { index in
84let optionalIterationResult = mapping(self[index])
85lock.lock()
86if let iterationResult = optionalIterationResult {
87mappingResult[index] = iterationResult
88}
89lock.unlock()
90}
91)
92return mappingResult.compactMap { $0 }
93}
94
95public func concurrentReduce<T, K, R>(
96_ initialValue: R,
97mapping: (T) -> (K),
98reduction: (inout R, K) -> ()
99) -> R where T == Element {
100var result = initialValue
101let lock = NSLock()
102DispatchQueue.concurrentPerform(
103iterations: count,
104execute: { index in
105let iterationResult = mapping(self[index])
106lock.lock()
107reduction(&result, iterationResult)
108lock.unlock()
109}
110)
111return result
112}
113
114public func concurrentForEach<T>(
115perform: (T) -> ()
116) where T == Element {
117DispatchQueue.concurrentPerform(
118iterations: count,
119execute: { index in
120perform(self[index])
121}
122)
123}
124
125private func returnOutput<R>(
126results: [Either<R, Error>]
127) throws -> [R] {
128let (errors, outputs) = results.reduce(into: ([Error](), [R]())) { errorsAndOutputs, result in
129switch result {
130case let .right(error):
131errorsAndOutputs.0.append(error)
132case let .left(output):
133errorsAndOutputs.1.append(output)
134}
135}
136guard errors.isEmpty else {
137throw CompoundError(errors: errors)
138}
139return outputs
140}
141}
142