Skip to content

Commit 796132a

Browse files
committed
Merge pull request #179 from CodaFi/set-ops
Set Ops
2 parents 54328ae + 80252ed commit 796132a

File tree

3 files changed

+127
-11
lines changed

3 files changed

+127
-11
lines changed

Swiftz/ArrayExt.swift

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,52 @@
88

99
/// MARK: Array extensions
1010

11+
public enum ArrayMatcher<A> {
12+
case Nil
13+
case Cons(A, [A])
14+
}
15+
16+
/// Destructures a list into its constituent parts.
17+
///
18+
/// If the given list is empty, this function returns .Empty. If the list is non-empty, this
19+
/// function returns .Cons(hd, tl)
20+
public func match<T>(l : [T]) -> ArrayMatcher<T> {
21+
if l.count == 0 {
22+
return .Nil
23+
} else if l.count == 1 {
24+
return .Cons(l[0], [])
25+
}
26+
let hd = l[0]
27+
let tl = Array<T>(l[1..<l.count])
28+
return .Cons(hd, tl)
29+
}
30+
31+
/// Returns the first element in the list, or None if the list is empty.
32+
public func head<A>(l : [A]) -> Optional<A> {
33+
switch match(l) {
34+
case .Nil:
35+
return .None
36+
case .Cons(let x, _):
37+
return .Some(x)
38+
}
39+
}
40+
41+
/// Returns the tail of the list, or None if the list is empty.
42+
public func tail<A>(l : [A]) -> Optional<[A]> {
43+
switch match(l) {
44+
case .Nil:
45+
return .None
46+
case .Cons(_, let xs):
47+
return .Some(xs)
48+
}
49+
}
50+
51+
/// Adds an element to the front of a list.
52+
public func cons<T>(lhs : T, var rhs : [T]) -> [T] {
53+
rhs.insert(lhs, atIndex: 0)
54+
return rhs
55+
}
56+
1157
/// Safely indexes into an array by converting out of bounds errors to nils.
1258
public func safeIndex<T>(array : Array<T>)(i : Int) -> T? {
1359
return indexArray(array, i)

Swiftz/Set.swift

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,26 @@ import Darwin
1111
/// An immutable unordered sequence of distinct values. Values are checked for uniqueness using
1212
/// their hashes.
1313
public struct Set<A : Hashable> {
14-
let bucket : Dictionary<A, Bool> = Dictionary()
14+
private let bucket : Dictionary<A, Bool> = Dictionary()
1515

16-
var array : [A] {
16+
/// Returns all elements of the receiver in an Array in no particular order.
17+
public var toArray : [A] {
1718
var arr = [A]()
1819
for (key, _) in bucket {
1920
arr.append(key)
2021
}
2122
return arr
2223
}
2324

25+
/// Returns all elements of the receiver in a List in no particular order.
26+
public var toList : List<A> {
27+
var list : List<A> = []
28+
for (key, _) in bucket {
29+
list = List(key, list)
30+
}
31+
return list
32+
}
33+
2434
public var count : Int {
2535
return bucket.count
2636
}
@@ -42,7 +52,7 @@ public struct Set<A : Hashable> {
4252
///
4353
/// If the receiver has no values this function will return nil.
4454
public func any() -> A? {
45-
let ar = self.array
55+
let ar = self.toArray
4656
if ar.isEmpty {
4757
return nil
4858
} else {
@@ -116,8 +126,8 @@ public struct Set<A : Hashable> {
116126

117127
/// Computes and returns the union of the reicever and a given set.
118128
public func union(set : Set<A>) -> Set<A> {
119-
var current = self.array
120-
current += set.array
129+
var current = self.toArray
130+
current += set.toArray
121131
return Set(array: current)
122132
}
123133

@@ -128,22 +138,48 @@ public struct Set<A : Hashable> {
128138
if contains(item) {
129139
return self
130140
} else {
131-
var arr = array
141+
var arr = toArray
132142
arr.append(item)
133143
return Set(array:arr)
134144
}
135145
}
136146

147+
/// Removes an item from the set.
148+
///
149+
/// If the item is not a member the receiver is returned unaltered.
150+
public func remove(item : A) -> Set<A> {
151+
if !contains(item) {
152+
return self
153+
} else {
154+
return Set(array: toArray.filter { $0.hashValue != item.hashValue })
155+
}
156+
}
157+
137158
/// Returns the set of elements in the receiver that pass a given predicate.
138-
public func filter(f : A -> Bool) -> Set<A> {
159+
public func filter(p : A -> Bool) -> Set<A> {
139160
var array = [A]()
140161
for x in self {
141-
if f(x) {
162+
if p(x) {
142163
array.append(x)
143164
}
144165
}
145166
return Set(array: array)
146167
}
168+
169+
/// Partition the set into two sets, one with all elements that satisfy the predicate and one
170+
/// with all elements that don't satisfy the predicate.
171+
public func partition(p : A -> Bool) -> (Set<A>, Set<A>) {
172+
var satis = [A]()
173+
var non = [A]()
174+
for x in self {
175+
if p(x) {
176+
satis.append(x)
177+
} else {
178+
non.append(x)
179+
}
180+
}
181+
return (Set(array: satis), Set(array: non))
182+
}
147183

148184
/// Maps a function over the elements of the receiver and aggregates the result in a new set.
149185
public func map<B>(f : A -> B) -> Set<B> {
@@ -154,6 +190,16 @@ public struct Set<A : Hashable> {
154190

155191
return Set<B>(array: array)
156192
}
193+
194+
/// Applies a binary function to reduce the elements of the receiver to a single value.
195+
public func reduce<B>(f : B -> A -> B, initial : B) -> B {
196+
return toArray.reduce(initial, combine: uncurry(f))
197+
}
198+
199+
/// Applies a binary operator to reduce the elements of the receiver to a single value.
200+
public func reduce<B>(f : (B, A) -> B, initial : B) -> B {
201+
return toArray.reduce(initial, combine: f)
202+
}
157203
}
158204

159205
extension Set : ArrayLiteralConvertible {
@@ -166,7 +212,7 @@ extension Set : ArrayLiteralConvertible {
166212

167213
extension Set : SequenceType {
168214
public func generate() -> SetGenerator<A> {
169-
let items = self.array
215+
let items = self.toArray
170216
return SetGenerator(items: items[0..<items.count])
171217
}
172218
}
@@ -186,11 +232,11 @@ public struct SetGenerator<A> : GeneratorType {
186232

187233
extension Set : Printable, DebugPrintable {
188234
public var description: String {
189-
return "\(self.array)"
235+
return "\(self.toArray)"
190236
}
191237

192238
public var debugDescription: String {
193-
return "\(self.array)"
239+
return "\(self.toArray)"
194240
}
195241
}
196242

SwiftzTests/SetTests.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,18 @@ class SetTests: XCTestCase {
1818
XCTAssert(set.count == 5, "Should be 5 items")
1919
}
2020

21+
func testAdd() {
22+
let set = Set(array:[1,2,3,4,4,4,5,5,5])
23+
XCTAssert(set.add(5).count == 5, "Adding duplicate element changes nothing.")
24+
XCTAssert(set.add(6).count == 6, "Adding unique element changes count")
25+
}
26+
27+
func testRemove() {
28+
let set = Set(array:[1,2,3,4,4,4,5,5,5])
29+
XCTAssert(set.remove(6).count == 5, "Removing unique element changes nothing.")
30+
XCTAssert(set.remove(5).count == 4, "Removing member element changes count")
31+
}
32+
2133
func testEquality() {
2234
var set1 = Set(array:[1,2,3,4,4,4,5,5,5])
2335
var set2 = Set(array:[1,2,3,4,4,4,5,5,5])
@@ -61,6 +73,18 @@ class SetTests: XCTestCase {
6173
XCTAssert(newSet == Set(array: [4,5,6]), "Should be equal")
6274
}
6375

76+
func testPartition() {
77+
let set = Set(array: [1,2,3,4,5,5,4,4,5,5])
78+
let (s, n) = set.partition(>3)
79+
XCTAssert(s == [4, 5], "Expected partitioned elements to be 4 and 5")
80+
XCTAssert(n == [1, 2, 3], "Expected partitioned elements to be 1, 2, and 3")
81+
}
82+
83+
func testReduce() {
84+
let set = Set<Int>(array: [1,2,3,4,5,5,4,4,5,5])
85+
XCTAssert(set.reduce({ $0 + $1 }, initial: 0) == 15, "Expected reduction to yield sum.")
86+
}
87+
6488
func testIntersectsSet() {
6589
let set = Set(array: [1,2,3,4,5,5,4,4,5,5])
6690
XCTAssert(set.interectsSet(Set(arrayLiteral: 9,0,5)), "Should be true")

0 commit comments

Comments
 (0)