Skip to content

Commit

Permalink
Updating to Swift3
Browse files Browse the repository at this point in the history
  • Loading branch information
Chris Pilcher committed Aug 30, 2016
1 parent 21b05b1 commit e6cfec4
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 72 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ script:
- xcodebuild test -project ./AVL\ Tree/Tests/Tests.xcodeproj -scheme Tests
- xcodebuild test -project ./Binary\ Search/Tests/Tests.xcodeproj -scheme Tests
# - xcodebuild test -project ./Binary\ Search\ Tree/Solution\ 1/Tests/Tests.xcodeproj -scheme Tests
# - xcodebuild test -project ./Bloom\ Filter/Tests/Tests.xcodeproj -scheme Tests
- xcodebuild test -project ./Bloom\ Filter/Tests/Tests.xcodeproj -scheme Tests
# - xcodebuild test -project ./Bounded\ Priority\ Queue/Tests/Tests.xcodeproj -scheme Tests
# - xcodebuild test -project ./Breadth-First\ Search/Tests/Tests.xcodeproj -scheme Tests
# - xcodebuild test -project ./Bucket\ Sort/Tests/Tests.xcodeproj -scheme Tests
Expand Down
18 changes: 9 additions & 9 deletions Bloom Filter/BloomFilter.playground/Contents.swift
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
//: Playground - noun: a place where people can play

public class BloomFilter<T> {
private var array: [Bool]
private var hashFunctions: [T -> Int]
fileprivate var array: [Bool]
private var hashFunctions: [(T) -> Int]

public init(size: Int = 1024, hashFunctions: [T -> Int]) {
self.array = .init(count: size, repeatedValue: false)
public init(size: Int = 1024, hashFunctions: [(T) -> Int]) {
self.array = [Bool](repeating: false, count: size)
self.hashFunctions = hashFunctions
}

private func computeHashes(value: T) -> [Int] {
private func computeHashes(_ value: T) -> [Int] {
return hashFunctions.map() { hashFunc in abs(hashFunc(value) % array.count) }
}

public func insert(element: T) {
public func insert(_ element: T) {
for hashValue in computeHashes(element) {
array[hashValue] = true
}
}

public func insert(values: [T]) {
public func insert(_ values: [T]) {
for value in values {
insert(value)
}
}

public func query(value: T) -> Bool {
public func query(_ value: T) -> Bool {
let hashValues = computeHashes(value)

// Map hashes to indices in the Bloom Filter
Expand All @@ -37,7 +37,7 @@ public class BloomFilter<T> {
// only that it may be. If the query returns false, however,
// you can be certain that the value was not added.

let exists = results.reduce(true, combine: { $0 && $1 })
let exists = results.reduce(true, { $0 && $1 })
return exists
}

Expand Down
16 changes: 8 additions & 8 deletions Bloom Filter/BloomFilter.swift
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
public class BloomFilter<T> {
private var array: [Bool]
private var hashFunctions: [T -> Int]
private var hashFunctions: [(T) -> Int]

public init(size: Int = 1024, hashFunctions: [T -> Int]) {
self.array = .init(count: size, repeatedValue: false)
public init(size: Int = 1024, hashFunctions: [(T) -> Int]) {
self.array = [Bool](repeating: false, count: size)
self.hashFunctions = hashFunctions
}

private func computeHashes(value: T) -> [Int] {
private func computeHashes(_ value: T) -> [Int] {
return hashFunctions.map() { hashFunc in abs(hashFunc(value) % array.count) }
}

public func insert(element: T) {
public func insert(_ element: T) {
for hashValue in computeHashes(element) {
array[hashValue] = true
}
}

public func insert(values: [T]) {
public func insert(_ values: [T]) {
for value in values {
insert(value)
}
}

public func query(value: T) -> Bool {
public func query(_ value: T) -> Bool {
let hashValues = computeHashes(value)

// Map hashes to indices in the Bloom Filter
Expand All @@ -35,7 +35,7 @@ public class BloomFilter<T> {
// only that it may be. If the query returns false, however,
// you can be certain that the value was not added.

let exists = results.reduce(true, combine: { $0 && $1 })
let exists = results.reduce(true, { $0 && $1 })
return exists
}

Expand Down
16 changes: 8 additions & 8 deletions Bloom Filter/README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ Performance of a Bloom Filter is **O(k)** where **k** is the number of hashing f

## The code

The code is quite straightforward. The internal bit array is set to a fixed length on initialization, which cannot be mutated once it is initialized.
The code is quite straightforward. The internal bit array is set to a fixed length on initialization, which cannot be mutated once it is initialized.

```swift
public init(size: Int = 1024, hashFunctions: [T -> Int]) {
self.array = .init(count: size, repeatedValue: false)
public init(size: Int = 1024, hashFunctions: [(T) -> Int]) {
self.array = [Bool](repeating: false, count: size)
self.hashFunctions = hashFunctions
}
```
Expand All @@ -72,7 +72,7 @@ Several hash functions should be specified at initialization. Which hash functio
Insertion just flips the required bits to `true`:

```swift
public func insert(element: T) {
public func insert(_ element: T) {
for hashValue in computeHashes(element) {
array[hashValue] = true
}
Expand All @@ -82,22 +82,22 @@ public func insert(element: T) {
This uses the `computeHashes()` function, which loops through the specified `hashFunctions` and returns an array of indices:

```swift
private func computeHashes(value: T) -> [Int] {
private func computeHashes(_ value: T) -> [Int] {
return hashFunctions.map() { hashFunc in abs(hashFunc(value) % array.count) }
}
```

And querying checks to make sure the bits at the hashed values are `true`:

```swift
public func query(value: T) -> Bool {
public func query(_ value: T) -> Bool {
let hashValues = computeHashes(value)
let results = hashValues.map() { hashValue in array[hashValue] }
let exists = results.reduce(true, combine: { $0 && $1 })
let exists = results.reduce(true, { $0 && $1 })
return exists
}
```

If you're coming from another imperative language, you might notice the unusual syntax in the `exists` assignment. Swift makes use of functional paradigms when it makes code more consise and readable, and in this case `reduce` is a much more consise way to check if all the required bits are `true` than a `for` loop.
If you're coming from another imperative language, you might notice the unusual syntax in the `exists` assignment. Swift makes use of functional paradigms when it makes code more consise and readable, and in this case `reduce` is a much more consise way to check if all the required bits are `true` than a `for` loop.

*Written for Swift Algorithm Club by Jamil Dhanani. Edited by Matthijs Hollemans.*
88 changes: 44 additions & 44 deletions Bloom Filter/Tests/BloomFilterTests.swift
Original file line number Diff line number Diff line change
@@ -1,76 +1,76 @@
import XCTest

/* Two hash functions, adapted from
http://www.cse.yorku.ca/~oz/hash.html */
http://www.cse.yorku.ca/~oz/hash.html */

func djb2(x: String) -> Int {
var hash = 5381
func djb2(_ x: String) -> Int {
var hash = 5381

for char in x.characters {
hash = ((hash << 5) &+ hash) &+ char.hashValue
}
for char in x.characters {
hash = ((hash << 5) &+ hash) &+ char.hashValue
}

return Int(hash)
return Int(hash)
}

func sdbm(x: String) -> Int {
var hash = 0
func sdbm(_ x: String) -> Int {
var hash = 0

for char in x.characters {
hash = char.hashValue &+ (hash << 6) &+ (hash << 16) &- hash
}
for char in x.characters {
hash = char.hashValue &+ (hash << 6) &+ (hash << 16) &- hash
}

return Int(hash)
return Int(hash)
}


class BloomFilterTests: XCTestCase {

func testSingleHashFunction() {
let bloom = BloomFilter<String>(hashFunctions: [djb2])
func testSingleHashFunction() {
let bloom = BloomFilter<String>(hashFunctions: [djb2])

bloom.insert("Hello world!")
bloom.insert("Hello world!")

let result_good = bloom.query("Hello world!")
let result_bad = bloom.query("Hello world")
let result_good = bloom.query("Hello world!")
let result_bad = bloom.query("Hello world")

XCTAssertTrue(result_good)
XCTAssertFalse(result_bad)
}
XCTAssertTrue(result_good)
XCTAssertFalse(result_bad)
}

func testEmptyFilter() {
let bloom = BloomFilter<String>(hashFunctions: [djb2])
func testEmptyFilter() {
let bloom = BloomFilter<String>(hashFunctions: [djb2])

let empty = bloom.isEmpty()
let empty = bloom.isEmpty()

XCTAssertTrue(empty)
}
XCTAssertTrue(empty)
}

func testMultipleHashFunctions() {
let bloom = BloomFilter<String>(hashFunctions: [djb2, sdbm])
func testMultipleHashFunctions() {
let bloom = BloomFilter<String>(hashFunctions: [djb2, sdbm])

bloom.insert("Hello world!")
bloom.insert("Hello world!")

let result_good = bloom.query("Hello world!")
let result_bad = bloom.query("Hello world")
let result_good = bloom.query("Hello world!")
let result_bad = bloom.query("Hello world")

XCTAssertTrue(result_good)
XCTAssertFalse(result_bad)
}
XCTAssertTrue(result_good)
XCTAssertFalse(result_bad)
}

func testFalsePositive() {
let bloom = BloomFilter<String>(size: 5, hashFunctions: [djb2, sdbm])
func testFalsePositive() {
let bloom = BloomFilter<String>(size: 5, hashFunctions: [djb2, sdbm])

bloom.insert(["hello", "elloh", "llohe", "lohel", "ohell"])
bloom.insert(["hello", "elloh", "llohe", "lohel", "ohell"])

print("Inserted")
print("Inserted")

let query = bloom.query("This wasn't inserted!")
let query = bloom.query("This wasn't inserted!")

// This is true even though we did not insert the value in the Bloom filter;
// the Bloom filter is capable of producing false positives but NOT
// false negatives.
// This is true even though we did not insert the value in the Bloom filter;
// the Bloom filter is capable of producing false positives but NOT
// false negatives.

XCTAssertTrue(query)
}
XCTAssertTrue(query)
}
}
10 changes: 9 additions & 1 deletion Bloom Filter/Tests/Tests.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,12 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0720;
LastUpgradeCheck = 0720;
LastUpgradeCheck = 0800;
ORGANIZATIONNAME = "Swift Algorithm Club";
TargetAttributes = {
7B2BBC7F1C779D720067B71D = {
CreatedOnToolsVersion = 7.2;
LastSwiftMigration = 0800;
};
};
};
Expand Down Expand Up @@ -145,8 +146,10 @@
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
Expand Down Expand Up @@ -189,8 +192,10 @@
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
Expand All @@ -209,6 +214,7 @@
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
};
name = Release;
};
Expand All @@ -220,6 +226,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = swift.algorithm.club.Tests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
};
name = Debug;
};
Expand All @@ -231,6 +238,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = swift.algorithm.club.Tests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
};
name = Release;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0720"
LastUpgradeVersion = "0800"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down

0 comments on commit e6cfec4

Please sign in to comment.