Skip to content

Commit

Permalink
Merge pull request #123 from dn-m/generalize-destructure
Browse files Browse the repository at this point in the history
Generalize destructured over Sequence, rather than Collection
  • Loading branch information
jsbean authored Jul 24, 2018
2 parents dc5dbd5 + 5b780f9 commit b7bec1b
Show file tree
Hide file tree
Showing 6 changed files with 33 additions and 56 deletions.
11 changes: 4 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,12 @@
Algebraic and data structures in Swift 4.2. The `Structure` package consists of four modules:

## Destructure
Module which includes a single extension of `Collection`, which breaks it into a `head` and `tail` for functional-style recursive implementations of algorithms.
Module which includes a single extension of `Sequence`, which breaks it into a `head` and `tail` for functional-style recursive implementations of algorithms.

```Swift
extension Collection {
/// 2-tuple containing the `head` `Element` and `tail` `[Element]` of `Self`
public var destructured: (Element, SubSequence)? {
guard let first = first else { return nil }
return (first, dropFirst())
}
extension Sequence {
/// 2-tuple containing the `head` and `tail` of a given `Sequence`.
public var destructured: (Element, AnySequence<Element>)?
}

```
Expand Down
21 changes: 9 additions & 12 deletions Sources/Algorithms/Combinatorics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,18 @@ extension Collection {

/// All of the permutations of each of the elements in each of the given sequences.
public var permutations: [[Element]] {
func permute <C> (_ values: C) -> [[Element]] where C: Collection, C.Element == Element {

func injecting <S> (_ value: S.Element, into values: S) -> [[S.Element]] where S: Sequence {
guard let (head, tail) = values.destructured else { return [[value]] }
return [[value] + values] + injecting(value, into: tail).map { [head] + $0 }
}

func permute <S> (_ values: S) -> [[Element]] where S: Sequence, S.Element == Element {
guard let (head, tail) = values.destructured else { return [[]] }
return permute(tail).flatMap { injecting(head, into: $0) }
}
return permute(self)

return self.isEmpty ? [] : permute(self)
}
}

Expand All @@ -54,13 +61,3 @@ extension Sequence where SubSequence: Sequence {
return zip(self,dropFirst())
}
}

/// Inject the given `value` into each possible index of the given `values`.
internal func injecting <C> (_ value: C.Element, into values: C) -> [[C.Element]]
where C: Collection
{
guard let (head, tail) = values.destructured else { return [[value]] }
return [[value] + values] + injecting(value, into: tail).map { [head] + $0 }
}


17 changes: 0 additions & 17 deletions Sources/DataStructures/CollectionExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,6 @@ extension Collection {
public subscript (safe index: Index) -> Element? {
return indices ~= index ? self[index] : nil
}

/// - Returns: The permutations of the values contained herein.
public var permutations: [[Element]] {
func permute <C> (_ values: C) -> [[Element]] where C: Collection, C.Element == Element {
guard let (head, tail) = values.destructured else { return [[]] }
return permute(tail).flatMap { injecting(head, into: $0) }
}
return permute(self)
}
}

extension MutableCollection where Self: BidirectionalCollection {
Expand Down Expand Up @@ -115,11 +106,3 @@ extension RangeReplaceableCollection where Self: BidirectionalCollection {
return replaced
}
}

/// - Returns: Two-dimensional array of `C.Element` values (helper for `Collection.permutations`).
func injecting <C> (_ value: C.Element, into values: C) -> [[C.Element]]
where C: Collection
{
guard let (head, tail) = values.destructured else { return [[value]] }
return [[value] + values] + injecting(value, into: tail).map { [head] + $0 }
}
9 changes: 5 additions & 4 deletions Sources/Destructure/Destructure.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
//
//

extension Collection {
extension Sequence {

/// 2-tuple containing the `head` `Element` and `tail` `[Element]` of `Self`
public var destructured: (Element, SubSequence)? {
guard let first = first else { return nil }
return (first, dropFirst())
public var destructured: (Element, AnySequence<Element>)? {
var iterator = makeIterator()
guard let first = iterator.next() else { return nil }
return (first, AnySequence(IteratorSequence(iterator)))
}
}
19 changes: 9 additions & 10 deletions Tests/AlgorithmsTests/CombinatoricsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,26 @@ import XCTest
class CombinatoricsTests: XCTestCase {

func testCartesianProductOfTwoArrays() {
let result = cartesianProduct([1,2,3],[4,5])
let a = [1,2,3]
let b = [4,5]
let result = cartesianProduct(a,b)
let expected = [(1,4),(1,5),(2,4),(2,5),(3,4),(3,5)]
XCTAssertEqual(result.count, 6)
XCTAssertEqual(result.count, expected.count)
zip(result,expected).forEach { a,b in
XCTAssertEqual(a.0,b.0)
XCTAssertEqual(a.1,b.1)
}
}

func testInjecting() {
let values = [1,2,3]
let result = injecting(0, into: values)
let expected = [[0, 1, 2, 3], [1, 0, 2, 3], [1, 2, 0, 3], [1, 2, 3, 0]]
XCTAssertEqual(result.count, expected.count)
zip(result,expected).forEach { (a,b) in XCTAssertEqual(a,b) }
func testPermutationsEmpty() {
let values: [Int] = []
let permutations = values.permutations
XCTAssert(values.permutations.isEmpty)
}

func testPermutations() {
let array = [1,2,3]
let expected = [[1, 2, 3], [2, 1, 3], [2, 3, 1], [1, 3, 2], [3, 1, 2], [3, 2, 1]]
XCTAssertEqual(expected.count, array.permutations.count)
zip(array.permutations, expected).forEach { a,b in XCTAssertEqual(a,b) }
XCTAssertEqual(array.permutations, expected)
}
}
12 changes: 6 additions & 6 deletions Tests/DestructureTests/DestructureTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import Destructure
class DestructureTests: XCTestCase {

func testArraySliceDestructured() {
let arraySlice: ArraySlice<Int> = [1,2,3]
let (a,b) = arraySlice.destructured!
let values = [1,2,3]
let (a,b) = values.destructured!
XCTAssertEqual(1, a)
XCTAssertEqual([2,3], b)
XCTAssertEqual([2,3], b.map { $0 })
}

func testArraySliceDestructuredNil() {
Expand All @@ -24,10 +24,10 @@ class DestructureTests: XCTestCase {
}

func testArrayDestructured() {
let array: Array<Int> = [1,2,3]
let (a,b) = array.destructured!
let values = [1,2,3]
let (a,b) = values.destructured!
XCTAssertEqual(1, a)
XCTAssertEqual([2,3], b)
XCTAssertEqual([2,3], b.map { $0 })
}

func testArrayDestructuredNil() {
Expand Down

0 comments on commit b7bec1b

Please sign in to comment.