Skip to content

Commit 31341e6

Browse files
author
Dave Abrahams
authored
Merge pull request #10976 from d-ronnqvist/reduce-with-inout
[stdlib] Implement SE-0171: Reduce with inout
2 parents bd02831 + bc235af commit 31341e6

File tree

6 files changed

+266
-1
lines changed

6 files changed

+266
-1
lines changed

benchmark/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ set(SWIFT_BENCH_MODULES
9090
single-source/RGBHistogram
9191
single-source/RangeAssignment
9292
single-source/RecursiveOwnedParameter
93+
single-source/ReduceInto
9394
single-source/ReversedCollections
9495
single-source/SetTests
9596
single-source/SevenBoom
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
//===--- ReduceInto.swift -------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import TestsUtils
14+
import Foundation
15+
16+
// Sum
17+
18+
@inline(never)
19+
public func run_SumUsingReduce(_ N: Int) {
20+
let numbers = [Int](0..<1000)
21+
22+
var c = 0
23+
for _ in 1...N*1000 {
24+
c = c &+ numbers.reduce(0) { (acc: Int, num: Int) -> Int in
25+
acc &+ num
26+
}
27+
}
28+
CheckResults(c != 0)
29+
}
30+
31+
@inline(never)
32+
public func run_SumUsingReduceInto(_ N: Int) {
33+
let numbers = [Int](0..<1000)
34+
35+
var c = 0
36+
for _ in 1...N*1000 {
37+
c = c &+ numbers.reduce(into: 0) { (acc: inout Int, num: Int) in
38+
acc = acc &+ num
39+
}
40+
}
41+
CheckResults(c != 0)
42+
}
43+
44+
// Filter
45+
46+
@inline(never)
47+
public func run_FilterEvenUsingReduce(_ N: Int) {
48+
let numbers = [Int](0..<100)
49+
50+
var c = 0
51+
for _ in 1...N*100 {
52+
let a = numbers.reduce([]) { (acc: [Int], num: Int) -> [Int] in
53+
var a = acc
54+
if num % 2 == 0 {
55+
a.append(num)
56+
}
57+
return a
58+
}
59+
c = c &+ a.count
60+
}
61+
CheckResults(c != 0)
62+
}
63+
64+
@inline(never)
65+
public func run_FilterEvenUsingReduceInto(_ N: Int) {
66+
let numbers = [Int](0..<100)
67+
68+
var c = 0
69+
for _ in 1...N*100 {
70+
let a = numbers.reduce(into: []) { (acc: inout [Int], num: Int) in
71+
if num % 2 == 0 {
72+
acc.append(num)
73+
}
74+
}
75+
c = c &+ a.count
76+
}
77+
CheckResults(c != 0)
78+
}
79+
80+
// Frequencies
81+
82+
@inline(never)
83+
public func run_FrequenciesUsingReduce(_ N: Int) {
84+
let s = "thequickbrownfoxjumpsoverthelazydogusingasmanycharacteraspossible123456789"
85+
86+
var c = 0
87+
for _ in 1...N*100 {
88+
let a = s.reduce([:]) {
89+
(acc: [Character: Int], c: Character) -> [Character: Int] in
90+
var d = acc
91+
d[c, default: 0] += 1
92+
return d
93+
}
94+
c = c &+ a.count
95+
}
96+
CheckResults(c != 0)
97+
}
98+
99+
@inline(never)
100+
public func run_FrequenciesUsingReduceInto(_ N: Int) {
101+
let s = "thequickbrownfoxjumpsoverthelazydogusingasmanycharacteraspossible123456789"
102+
103+
var c = 0
104+
for _ in 1...N*100 {
105+
let a = s.reduce(into: [:]) {
106+
(acc: inout [Character: Int], c: Character) in
107+
acc[c, default: 0] += 1
108+
}
109+
c = c &+ a.count
110+
}
111+
CheckResults(c != 0)
112+
}

benchmark/utils/main.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ import RC4
9595
import RGBHistogram
9696
import RangeAssignment
9797
import RecursiveOwnedParameter
98+
import ReduceInto
9899
import ReversedCollections
99100
import SetTests
100101
import SevenBoom
@@ -273,6 +274,10 @@ addTo(&precommitTests, "EqualSubstringString", run_EqualSubstringString)
273274
addTo(&precommitTests, "EqualSubstringSubstring", run_EqualSubstringSubstring)
274275
addTo(&precommitTests, "EqualSubstringSubstringGenericEquatable", run_EqualSubstringSubstringGenericEquatable)
275276
addTo(&precommitTests, "ErrorHandling", run_ErrorHandling)
277+
addTo(&precommitTests, "FilterEvenUsingReduce", run_FilterEvenUsingReduce)
278+
addTo(&precommitTests, "FilterEvenUsingReduceInto", run_FilterEvenUsingReduceInto)
279+
addTo(&precommitTests, "FrequenciesUsingReduce", run_FrequenciesUsingReduce)
280+
addTo(&precommitTests, "FrequenciesUsingReduceInto", run_FrequenciesUsingReduceInto)
276281
addTo(&precommitTests, "Hanoi", run_Hanoi)
277282
addTo(&precommitTests, "HashTest", run_HashTest)
278283
addTo(&precommitTests, "Histogram", run_Histogram)
@@ -443,6 +448,8 @@ addTo(&precommitTests, "SuffixCountableRange", run_SuffixCountableRange)
443448
addTo(&precommitTests, "SuffixCountableRangeLazy", run_SuffixCountableRangeLazy)
444449
addTo(&precommitTests, "SuffixSequence", run_SuffixSequence)
445450
addTo(&precommitTests, "SuffixSequenceLazy", run_SuffixSequenceLazy)
451+
addTo(&precommitTests, "SumUsingReduce", run_SumUsingReduce)
452+
addTo(&precommitTests, "SumUsingReduceInto", run_SumUsingReduceInto)
446453
addTo(&precommitTests, "SuperChars", run_SuperChars)
447454
addTo(&precommitTests, "TwoSum", run_TwoSum)
448455
addTo(&precommitTests, "TypeFlood", run_TypeFlood)

stdlib/public/core/SequenceAlgorithms.swift.gyb

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,61 @@ extension Sequence {
596596
}
597597
return accumulator
598598
}
599+
600+
/// Returns the result of combining the elements of the sequence using the
601+
/// given closure.
602+
///
603+
/// Use the `reduce(into:_:)` method to produce a single value from the
604+
/// elements of an entire sequence. For example, you can use this method on an
605+
/// array of integers to filter adjacent equal entries or count frequencies.
606+
///
607+
/// This method is preferred over `reduce(_:_:)` for efficiency when the
608+
/// result is a copy-on-write type, for example an Array or a Dictionary.
609+
///
610+
/// The `updateAccumulatingResult` closure is called sequentially with a
611+
/// mutable accumulating value initialized to `initialResult` and each element
612+
/// of the sequence. This example shows how to build a dictionary of letter
613+
/// frequencies of a string.
614+
///
615+
/// let letters = "abracadabra"
616+
/// let letterCount = letters.reduce(into: [:]) { counts, letter in
617+
/// counts[letter, default: 0] += 1
618+
/// }
619+
/// // letterCount == ["a": 5, "b": 2, "r": 2, "c": 1, "d": 1]
620+
///
621+
/// When `letters.reduce(into:_:)` is called, the following steps occur:
622+
///
623+
/// 1. The `updateAccumulatingResult` closure is called with the initial
624+
/// accumulating value---`[:]` in this case---and the first character of
625+
/// `letters`, modifying the accumulating value by setting `1` for the key
626+
/// `"a"`.
627+
/// 2. The closure is called again repeatedly with the updated accumulating
628+
/// value and each element of the sequence.
629+
/// 3. When the sequence is exhausted, the accumulating value is returned to
630+
/// the caller.
631+
///
632+
/// If the sequence has no elements, `updateAccumulatingResult` is never
633+
/// executed and `initialResult` is the result of the call to
634+
/// `reduce(into:_:)`.
635+
///
636+
/// - Parameters:
637+
/// - initialResult: The value to use as the initial accumulating value.
638+
/// - updateAccumulatingResult: A closure that updates the accumulating
639+
/// value with an element of the sequence.
640+
/// - Returns: The final accumulated value. If the sequence has no elements,
641+
/// the result is `initialResult`.
642+
@_inlineable
643+
public func reduce<Result>(
644+
into initialResult: Result,
645+
_ updateAccumulatingResult:
646+
(_ partialResult: inout Result, Element) throws -> ()
647+
) rethrows -> Result {
648+
var accumulator = initialResult
649+
for element in self {
650+
try updateAccumulatingResult(&accumulator, element)
651+
}
652+
return accumulator
653+
}
599654
}
600655

601656
//===----------------------------------------------------------------------===//

test/Constraints/closures.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ func testMap() {
164164
}
165165

166166
// <rdar://problem/22414757> "UnresolvedDot" "in wrong phase" assertion from verifier
167-
[].reduce { $0 + $1 } // expected-error {{missing argument for parameter #1 in call}}
167+
[].reduce { $0 + $1 } // expected-error {{cannot invoke 'reduce' with an argument list of type '((_, _) -> _)'}}
168168

169169

170170

test/stdlib/Reduce.swift

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//===--- Reduce.swift - tests for the two reduce variants -----------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
// RUN: %target-run-simple-swift | %FileCheck %s
13+
// REQUIRES: executable_test
14+
15+
// CHECK: testing...
16+
print("testing...")
17+
18+
// Test the examples from the documentation of reduce(_:_:) and reduce(into:_:)
19+
20+
let numbers = [1, 2, 3, 4]
21+
let numberSum = numbers.reduce(0, { x, y in
22+
x + y
23+
})
24+
// CHECK-NEXT: 10
25+
print(numberSum)
26+
27+
let letters = "abracadabra"
28+
let letterCount = letters.reduce(into: [:]) { counts, letter in
29+
counts[letter, default: 0] += 1
30+
}
31+
// CHECK-NEXT: ["a", "b", "c", "d", "r"]
32+
print(letterCount.keys.sorted())
33+
print(letterCount["a"]!) // CHECK: 5
34+
print(letterCount["b"]!) // CHECK: 2
35+
print(letterCount["c"]!) // CHECK: 1
36+
print(letterCount["d"]!) // CHECK: 1
37+
print(letterCount["r"]!) // CHECK: 2
38+
39+
40+
// Test the two reduce methods with different levels of inference
41+
let numbers2 = Array(2..<7)
42+
43+
// Test reduce(_:_:)
44+
// CHECK-NEXT: 20
45+
let sum1 = numbers2.reduce(0) { (x: Int, y: Int) -> Int in x + y }
46+
print(sum1)
47+
48+
// CHECK-NEXT: 20
49+
let sum2 = numbers2.reduce(0) { (x, y) in x + y }
50+
print(sum2)
51+
52+
// CHECK-NEXT: 20
53+
let sum3 = numbers2.reduce(0) { $0 + $1 }
54+
print(sum3)
55+
56+
// CHECK-NEXT: 20
57+
let sum4 = numbers2.reduce(0, +)
58+
print(sum4)
59+
60+
// Test reduce(into:_:)
61+
// CHECK-NEXT: 20
62+
let sum5 = numbers2.reduce(into: 0) { (x: inout Int, y: Int) in x += y }
63+
print(sum5)
64+
65+
// CHECK-NEXT: 20
66+
let sum6 = numbers2.reduce(into: 0) { x, y in x += y }
67+
print(sum6)
68+
69+
// CHECK-NEXT: 20
70+
let sum7 = numbers2.reduce(into: 0) { $0 += $1 }
71+
print(sum7)
72+
73+
// CHECK-NEXT: 20
74+
let sum8 = numbers2.reduce(into: 0, +=)
75+
print(sum8)
76+
77+
78+
// Test that the initial value remains unmodified
79+
var original = [0, 1]
80+
let result = numbers2.reduce(into: original) { acc, x in
81+
acc.append(x)
82+
}
83+
// CHECK-NEXT: [0, 1]
84+
print(original)
85+
// CHECK-NEXT: [0, 1, 2, 3, 4, 5, 6]
86+
print(result)
87+
88+
89+
// CHECK: all done.
90+
print("all done.")

0 commit comments

Comments
 (0)