• Stars
    star
    375
  • Rank 110,209 (Top 3 %)
  • Language
    Swift
  • License
    MIT License
  • Created almost 9 years ago
  • Updated over 7 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A μframework of extensions for SequenceType in Swift 2.0, inspired by Python's itertools, Haskell's standard library, and other things.

Build Status Carthage compatible

SwiftSequence

Full reference here.

(If you're looking for data structures in Swift, those have been moved to here)

SwiftSequence is a lightweight framework of extensions to SequenceType. It has no requirements beyond the Swift standard library. Every function and method has both a strict and a lazy version, unless otherwise specified.

To make an eager sequence lazy, the lazy property can be used:

[1, 2, 3].lazy.hop(2)

// 1, 3

Contributing

Contributions welcome! Anything at all, as long as it's related to sequences, Linux-compatible, and has a test or two.

Contents

  • [ScanReduce] (#scanreduce)
  • [TakeDrop] (#takedrop)
  • [Hopping and Slicing] (#hopping-and-slicing)
  • [Interpose] (#interpose)
  • [Combinations] (#combinations)
  • [Permutations] (#permutations)
  • [Cycle] (#cycle)
  • [Categorise] (#categorise)
  • [ChunkWindowSplit] (#chunkwindowsplit)
  • [Enumerate] (#enumerate)
  • [Finding] (#finding)
  • [NestedSequences] (#nestedsequences)
  • [Zip] (#zip)

ScanReduce

Reduce

func reduce
    (@noescape combine: (accumulator: Generator.Element, element: Generator.Element) throws -> Generator.Element)
    rethrows -> Generator.Element?

This function works the same as the standard library reduce, except it takes the initial value to be the first element of self. It returns an optional, which is nil if self is empty.

[1, 2, 3, 4].reduce(+) == 10

Scan

Returns an array of the intermediate results of calling combine on successive elements of self and an accumulator:

[1, 2, 3].scan(0, combine: +)
 
[1, 3, 6]

The last element of this array will be equal to the result of calling reduce with the same arguments.

There is also, like reduce, a version that takes the first element of the sequence to be initial:

[1, 2, 3, 4, 5].scan(+)

[3, 6, 10, 15]

This also is evaluated lazily if the sequence it is called on is lazy.

TakeDrop

prefixWhile

This function returns all of the elements of self up until an element returns false for predicate:

[1, 2, 3, 4, 5, 2].prefixWhile { $0 < 4 }

[1, 2, 3]

Note that it's not the same as filter: if any elements return true for the predicate after the first element that returns false, they're still not returned.

dropWhile

Similar in behaviour to prefixWhile, this function drops the first elements of self that return true for a predicate:

lazy([1, 2, 3, 4, 5, 2]).dropWhile { $0 < 4 }

4, 5, 2

breakAt

Returns a tuple, the first element being the prefix of self of maximum length n, the second being the remaining elements of self.

[1, 2, 3, 4].breakAt(3) == ([1, 2, 3], [4])

This also has a version which takes a predicate, which returns a tuple where the first element is the prefix of self up until the first element that returns true for isBreak.

Hopping and Slicing

This allows subscripting of several CollectionTypes in a way similar to Python's slicing. For instance, you can slice in an open-ended way:

let array = [0, 1, 2, 3, 4, 5, 6]
array[3...] == [3, 4, 5, 6]
array[...4] == [0, 1, 2, 3, 4]
array[..<4] == [0, 1, 2, 3]

You can also mutate via slices:

var array = [0, 1, 2, 3, 4, 5, 6]
array[...3] = [10, 11, 12, 13]
array == [10, 11, 12, 13, 4, 5, 6]

Or, you can hop over elements in the array:

let array = [0, 1, 2, 3, 4, 5, 6]
array[2..., by: 2] // [2, 4, 6]

Interpose

These functions allow lazy and eager insertion of elements into sequences at regular intervals.

Interpose

This function returns self, with element inserted between every element:

[1, 2, 3].interpose(10)

[1, 10, 2, 10, 3]

The intervals at which to insert element can be specified:

[1, 2, 3, 4, 5].interpose(10, n: 2)

[1, 2, 10, 3, 4, 10, 5]

More than one element can be interposed:

[1, 2, 3].interpose([10, 20])

[1, 10, 20, 2, 10, 20, 3]

And, again, the interval can be specified:

[1, 2, 3, 4, 5].interpose([10, 20], n: 2)

[1, 2, 10, 20, 3, 4, 10, 20, 5]

interdig

This function allows you to combine two sequences by alternately selecting elements from each:

interdig([1, 2, 3], [10, 20, 30])

[1, 10, 2, 20, 3, 30]

The length of the interdigitations can be specified:

interdig([1, 2, 3, 4, 5], [10, 20, 30, 40, 50, 60], s0Len: 2, s1Len: 3)

[1, 2, 10, 20, 30, 3, 4, 40, 50, 60, 5]

Combinations

These functions return combinations with or without repetition, lazily or eagerly. The lazy version of these function doesn't maintain laziness of the underlying sequence, but they produce combinations on-demand, with neither future nor past combinations stored in memory, e.g:

let lazySequence = [1, 2, 3].lazy

let lazyCombos = lazySequence.lazyCombinations(2)

// Here, lazySequence was evaluated, but no combinations have yet been evaluated.

var g = lazyCombos.generate()

g.next() == [1, 2]

// Here, only one combination has been evaluated, and only that combination is stored in memory

g.next() == [1, 3]

// Here, two combinations have been evaluated, but no extra combinations have been stored in memory.

Combinations

Returns combinations without repetitions of self of length n

Combinations are returned in lexicographical order, according to the order of self

[1, 2, 3].combinations(2)

[1, 2], [1, 3], [2, 3]

To have combinations generate lazily and on-demand, use lazyCombinations().

Example Recipe:

extension CollectionType {
  func powerSet() -> FlatMapSeq<LazyForwardCollection<Self>, ComboSeq<[Self.Generator.Element]>> {
    var i = 0
    return lazy(self).flatMap { _ in self.lazyCombinations(++i) }
  }
}

[1, 2, 3]       // [1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]
  .powerSet()

Combinations with Repetition

Returns combinations with repetition of self of length n.

Combinations are returned in lexicographical order, according to the order of self.

[1, 2, 3].combinationsWithRep(2)

[1, 1], [1, 2], [1, 3], [2, 2], [2, 3], [3, 3]

To have combinations generate lazily and on-demand, use lazyCombinationsWithRep().

Permutations

Lexicographical Permutations

These functions return self, permuted, in lexicographical order. If self is not the first permutation, lexicographically, not all permutations will be returned. (to ensure all permutations are returned, sort() can be used). This function can operate on a collection of Comparable elements, or, is the closure isOrderedBefore is provided, it can operate on any collection. In terms of laziness, it behaves the same as the combination functions: forcing the evaluation of the underlying collection, but capable of lazily producing each new permutation. To access the lazy version, use the versions of these functions with the lazy prefix. (e.g., lexPermutations() becomes lazyLexPermutations())

[1, 2, 3].lexPermutations()

[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
[3, 2, 1].lexPermutations()

[[3, 2, 1]]
[1, 2, 3].lexPermutations(<)

[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
[1, 2, 3].lexPermutations(>)

[[1, 2, 3]]

Permutations

These functions use the same algorithm as the lexicographical permutations, but the indices of self are permuted. (Indices aren't returned: they're just used for the permutation)

[1, 2, 3].permutations()

[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
lazy([3, 2, 1]).lazyPermutations()

[3, 2, 1], [3, 1, 2], [2, 3, 1], [2, 1, 3], [1, 3, 2], [1, 2, 3]

Cycle

These functions return a cycle of self. The number of cycles can be specified, if not, self is cycled infinitely.

When called on a LazySequenceType, the sequence returned is lazy, otherwise, it's eager. (the infinite cycle is always lazy, however)

[1, 2, 3].cycle(2)

[1, 2, 3, 1, 2, 3]
[1, 2, 3].cycle()

1, 2, 3, 1, 2, 3, 1...

Categorise

These functions can be used to group elements of a sequence on certain conditions.

categorise

func categorise<U : Hashable>(keyFunc: Generator.Element -> U) -> [U:[Generator.Element]] 

This categorises all elements of self into a dictionary, with the keys of that dictionary given by the keyFunc.

[1, 2, 3, 4, 5, 6].categorise { $0 % 2 }


[
  0: [2, 4, 6],
  1: [1, 3, 5]
]

(this function has no lazy version)

Frequencies

This returns a dictionary where the keys are the elements of self, and the values are their respective frequencies:

[1, 1, 1, 2, 2, 3].freqs()

[
  1: 3,
  2: 2,
  3: 1
]

(this function has no lazy version)

Uniques

Returns self with duplicates removed:

[1, 2, 3, 2, 2, 4, 1, 2, 5, 6].uniques()

[1, 2, 3, 4, 5, 6]

Grouping

Since categorise() and freqs() can't have lazy versions, these grouping functions give similar behaviour. Instead of categorising based on the whole sequence, they categories based on adjacent values.

This groups adjacent equal values:

lazy([1, 2, 2, 3, 1, 1, 3, 4, 2]).group()

[1], [2, 2], [3], [1, 1], [3], [4], [2]

This groups adjacent equal values according to a closure:

lazy([1, 3, 5, 20, 22, 18, 6, 7]).group { abs($0 - $1) < 5 }

[1, 3, 5], [20, 22, 18], [6, 7]

This groups adjacent values that return the same from keyFunc:

lazy([1, 3, 5, 2, 4, 6, 6, 7, 1, 1]).groupBy { $0 % 2 }

[1, 3, 5], [2, 4, 6, 6], [7, 1, 1]

ChunkWindowSplit

These functions divide up a sequence.

Chunk

This function returns self, broken up into non-overlapping arrays of length n:

[1, 2, 3, 4, 5].chunk(2)

[[1, 2], [3, 4], [5]]

Window

This function returns self, broken up into overlapping arrays of length n:

[1, 2, 3, 4, 5].window(3)

[[1, 2, 3], [2, 3, 4], [3, 4, 5]]

Enumerate

This just adds the function specEnumerate() which is the same as the standard library enumerate(), except that the indices it returns are specific to the base, rater than just Ints. So, for instance, this:

"hello".characters.specEnumerate()

Would return a sequence of (String.Index, Character) (rather than (Int, Character), which is what the standard library enumerate() returns). There is no eager version of this function (as there is no eager enumerate())

Finding

The methods here are intended to replace patterns like this:

[1, 2, 3, 4, 5, 6].filter { n in n > 4 }.first
[1, 2, 3, 4, 5, 6].filter { n in n % 2 == 0 }.count

Where filter is used in a chain with one of the CollectionType properties, like first or count.

These chains are needlessly inefficient: they allocate and fill an array unnecessarily. Even if the lazy property is used, the actual operations are unexpected. The code below, for instance:

var i = 0

[1, 2, 3, 4, 5, 6]
  .lazy
  .filter { n in
    print(++i)
    return n > 4
}.first

Will print one through ten.

first, last, and count methods are all provided, which take predicates, as well as indicesOf and lastIndexOf.

A partition method is also available, which splits self into a tuple of arrays which satisfy and do not satisfy predicate, respectively.

NestedSequences

Transpose

Allows both lazy and eager transposition. When lazily transposing, each row is evaluated eagerly, but only that row:

let transposed = lazy([
  [1, 2, 3],
  [1, 2, 3],
  [1, 2, 3]
]).transpose()

var g = transposed.generate()

g.next() == [1, 1, 1]

// Each row is an eager array, but only that row is evaluated.

g.next() == [2, 2, 2]

// It's safe to use with single-pass sequences, also, as each sequence is only evaluated once.

Product

Both lazy and eager Cartesian Products.

product([1, 2], [3, 4])

[[1, 3], [1, 4], [2, 3], [2, 4]]

lazyProduct([1, 2], [3, 4])

[1, 3], [1, 4], [2, 3], [2, 4]

Zip

These functions allow you to zip two sequences of different lengths together, and to specify the padding for the shorter sequence. If unspecified, the padding is nil. There is no eager version of this function (there is no eager standard library zip)

zipWithPadding([1, 2, 3], [1, 2], pad0: 100, pad1: 900)

(1, 1), (2, 2), (3, 900)
zipWithPadding([1, 2, 3], [1, 2])

(1?, 1?), (2?, 2?), (3?, nil)

More Repositories

1

SwiftDataStructures

Data structures in Swift, including a Trie, Tree, List, and Deque
Swift
64
star
2

agda-ring-solver

A fast, easy-to-use ring solver for agda with step-by-step solutions
Agda
38
star
3

monus-weighted-search

Efficient search weighted by an ordered monoid with monus.
Haskell
16
star
4

site

Source code for doisinkidney.com
HTML
14
star
5

semiring-num

library with a semiring class and some useful semirings
Haskell
11
star
6

Using-Protocols-to-Build-a-very-Generic-Deque

Swift
8
star
7

type-indexed-queues

Queues with verified and unverified versions
Haskell
8
star
8

groupBy

An example replacement for Data.List.groupBy
Haskell
8
star
9

arity-generic-liftA

Provides an arity-generic version of the liftA2, liftA3... liftAn functions
Haskell
8
star
10

agda-playground

Agda
7
star
11

finiteness-in-cubical-type-theory

Agda
7
star
12

masters-thesis

Agda
5
star
13

Monty-Hall

Swift
5
star
14

agda-avl

TeX
4
star
15

constrained-monads

A library for monads with constraints over the types they contain
HTML
4
star
16

binary-tree

Haskell
4
star
17

PretendDependSwift

A library for pretending that Swift is dependently typed
Swift
3
star
18

agda-ring-solver-report

TeX
3
star
19

lean-peano

Haskell
3
star
20

SwiftTrie

Swift
3
star
21

weighted

WeightedT monad transformer.
Haskell
2
star
22

strict-writer

Deprecated in favour of http://hackage.haskell.org/package/writer-cps-mtl-0.1.0.0
Haskell
2
star
23

verified-avl

Haskell
2
star
24

hstrie

Haskell
2
star
25

quickselect

implementation of introselect in haskell
Haskell
2
star
26

hakyll-series

Add Series Functionality to Hakyll
Haskell
2
star
27

ConstArray

Swift
1
star
28

difference-monoid

Haskell
1
star
29

puzzles

Haskell
1
star
30

bfs

HTML
1
star
31

agda-binary

Agda
1
star
32

Deques-Queues-and-Lists-in-Swift-with-indirect

Swift
1
star
33

levels-monad

Haskell
1
star
34

agda-poly

Agda
1
star
35

generic-church

Haskell
1
star
36

ContiguousDeque

Swift
1
star
37

prob-presentation

Python
1
star
38

mddoctest

Test code examples in markdown files
Haskell
1
star
39

recursion-schemes-extras

Haskell
1
star
40

monadic-heap

Haskell
1
star
41

SubSum

Swift
1
star
42

team-software-project

Python
1
star
43

agda-indexed-fingertree

Agda
1
star
44

project-euler

1
star
45

pure-arrays

Haskell
1
star