Files
swift-mirror/stdlib/public/core/Algorithm.swift
Chris Lattner 20f8f09ea8 Land: <rdar://problem/19382905> improve 'if let' to support refutable patterns and untie it from optionals
This changes 'if let' conditions to take general refutable patterns, instead of
taking a irrefutable pattern and implicitly matching against an optional.

Where before you might have written:
  if let x = foo() {

you now need to write:
  if let x? = foo() {
    
The upshot of this is that you can write anything in an 'if let' that you can
write in a 'case let' in a switch statement, which is pretty general.

To aid with migration, this special cases certain really common patterns like
the above (and any other irrefutable cases, like "if let (a,b) = foo()", and
tells you where to insert the ?.  It also special cases type annotations like
"if let x : AnyObject = " since they are no longer allowed.

For transitional purposes, I have intentionally downgraded the most common
diagnostic into a warning instead of an error.  This means that you'll get:

t.swift:26:10: warning: condition requires a refutable pattern match; did you mean to match an optional?
if let a = f() {
       ^
        ?

I think this is important to stage in, because this is a pretty significant
source breaking change and not everyone internally may want to deal with it
at the same time.  I filed 20166013 to remember to upgrade this to an error.

In addition to being a nice user feature, this is a nice cleanup of the guts
of the compiler, since it eliminates the "isConditional()" bit from
PatternBindingDecl, along with the special case logic in the compiler to handle
it (which variously added and removed Optional around these things).




Swift SVN r26150
2015-03-15 07:06:22 +00:00

446 lines
11 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
/// Returns the minimum element in `elements`. Requires:
/// `elements` is non-empty. O(count(elements))
public func minElement<
R : SequenceType
where R.Generator.Element : Comparable>(elements: R)
-> R.Generator.Element {
var g = elements.generate()
var result = g.next()!
for e in GeneratorSequence(g) {
if e < result { result = e }
}
return result
}
/// Returns the maximum element in `elements`. Requires:
/// `elements` is non-empty. O(count(elements))
public func maxElement<
R : SequenceType
where R.Generator.Element : Comparable>(elements: R)
-> R.Generator.Element {
var g = elements.generate()
var result = g.next()!
for e in GeneratorSequence(g) {
if e > result { result = e }
}
return result
}
/// Returns the first index where `value` appears in `domain` or `nil` if
/// `value` is not found.
///
/// Complexity: O(\ `count(domain)`\ )
public func find<
C: CollectionType where C.Generator.Element : Equatable
>(domain: C, value: C.Generator.Element) -> C.Index? {
for i in indices(domain) {
if domain[i] == value {
return i
}
}
return nil
}
/// Return the lesser of `x` and `y`
public func min<T : Comparable>(x: T, y: T) -> T {
var r = x
if y < x {
r = y
}
return r
}
/// Return the least argument passed
public func min<T : Comparable>(x: T, y: T, z: T, rest: T...) -> T {
var r = x
if y < x {
r = y
}
if z < r {
r = z
}
for t in rest {
if t < r {
r = t
}
}
return r
}
/// Return the greater of `x` and `y`
public func max<T : Comparable>(x: T, y: T) -> T {
var r = y
if y < x {
r = x
}
return r
}
/// Return the greatest argument passed
public func max<T : Comparable>(x: T, y: T, z: T, rest: T...) -> T {
var r = y
if y < x {
r = x
}
if r < z {
r = z
}
for t in rest {
if t >= r {
r = t
}
}
return r
}
/// Return the result of slicing `elements` into sub-sequences that
/// don't contain elements satisfying the predicate `isSeparator`.
///
/// :param: maxSplit the maximum number of slices to return, minus 1.
/// If `maxSplit + 1` slices would otherwise be returned, the
/// algorithm stops splitting and returns a suffix of `elements`
///
/// :param: allowEmptySlices if true, an empty slice is produced in
/// the result for each pair of consecutive
public func split<S: Sliceable, R:BooleanType>(
elements: S,
maxSplit: Int = Int.max,
allowEmptySlices: Bool = false,
@noescape #isSeparator: (S.Generator.Element) -> R
) -> [S.SubSlice] {
var result = Array<S.SubSlice>()
// FIXME: could be simplified pending <rdar://problem/15032945>
// (ternary operator not resolving some/none)
var startIndex: Optional<S.Index>
= allowEmptySlices ? .Some(elements.startIndex) : .None
var splits = 0
for j in indices(elements) {
if isSeparator(elements[j]) {
if startIndex != nil {
var i = startIndex!
result.append(elements[i..<j])
startIndex = .Some(j.successor())
if ++splits >= maxSplit {
break
}
if !allowEmptySlices {
startIndex = .None
}
}
}
else {
if startIndex == nil {
startIndex = .Some(j)
}
}
}
switch startIndex {
case let i?:
result.append(elements[i..<elements.endIndex])
default:
()
}
return result
}
/// Return true iff the the initial elements of `s` are equal to `prefix`.
public func startsWith<
S0 : SequenceType, S1 : SequenceType
where
S0.Generator.Element == S1.Generator.Element,
S0.Generator.Element : Equatable
>(s: S0, prefix: S1) -> Bool
{
var prefixGenerator = prefix.generate()
for e0 in s {
var e1 = prefixGenerator.next()
if e1 == nil { return true }
if e0 != e1! {
return false
}
}
return prefixGenerator.next() != nil ? false : true
}
/// Return true iff `s` begins with elements equivalent to those of
/// `prefix`, using `isEquivalent` as the equivalence test.
///
/// Requires: `isEquivalent` is an `equivalence relation
/// <http://en.wikipedia.org/wiki/Equivalence_relation>`_
public func startsWith<
S0 : SequenceType, S1 : SequenceType
where
S0.Generator.Element == S1.Generator.Element
>(s: S0, prefix: S1,
@noescape isEquivalent: (S1.Generator.Element, S1.Generator.Element) -> Bool)
-> Bool
{
var prefixGenerator = prefix.generate()
for e0 in s {
var e1 = prefixGenerator.next()
if e1 == nil { return true }
if !isEquivalent(e0, e1!) {
return false
}
}
return prefixGenerator.next() != nil ? false : true
}
/// The `GeneratorType` for `EnumerateSequence`. `EnumerateGenerator`
/// wraps a `Base` `GeneratorType` and yields successive `Int` values,
/// starting at zero, along with the elements of the underlying
/// `Base`::
///
/// var g = EnumerateGenerator(["foo", "bar"].generate())
/// g.next() // (0, "foo")
/// g.next() // (1, "bar")
/// g.next() // nil
///
/// Note:: idiomatic usage is to call `enumerate` instead of
/// constructing an `EnumerateGenerator` directly.
public struct EnumerateGenerator<
Base: GeneratorType
> : GeneratorType, SequenceType {
/// The type of element returned by `next()`.
public typealias Element = (index: Int, element: Base.Element)
var base: Base
var count: Int
/// Construct from a `Base` generator
public init(_ base: Base) {
self.base = base
count = 0
}
/// Advance to the next element and return it, or `nil` if no next
/// element exists.
///
/// Requires: no preceding call to `self.next()` has returned `nil`.
public mutating func next() -> Element? {
var b = base.next()
if b == nil { return .None }
return .Some((index: count++, element: b!))
}
/// A type whose instances can produce the elements of this
/// sequence, in order.
public typealias Generator = EnumerateGenerator<Base>
/// `EnumerateGenerator` is also a `SequenceType`, so it
/// `generate`\ s a copy of itself
public func generate() -> Generator {
return self
}
}
/// The `SequenceType` returned by `enumerate()`. `EnumerateSequence`
/// is a sequence of pairs (*n*, *x*), where *n*\ s are consecutive
/// `Int`\ s starting at zero, and *x*\ s are the elements of a `Base`
/// `SequenceType`::
///
/// var s = EnumerateSequence(["foo", "bar"])
/// Array(s) // [(0, "foo"), (1, "bar")]
///
/// Note:: idiomatic usage is to call `enumerate` instead of
/// constructing an `EnumerateSequence` directly.
public struct EnumerateSequence<Base : SequenceType> : SequenceType {
var base: Base
/// Construct from a `Base` sequence
public init(_ base: Base) {
self.base = base
}
/// Return a *generator* over the elements of this *sequence*.
///
/// Complexity: O(1)
public func generate() -> EnumerateGenerator<Base.Generator> {
return EnumerateGenerator(base.generate())
}
}
/// Return a lazy `SequenceType` containing pairs (*n*, *x*), where
/// *n*\ s are consecutive `Int`\ s starting at zero, and *x*\ s are
/// the elements of `base`::
///
/// > for (n, c) in enumerate("Swift") { println("\(n): '\(c)'" )}
/// 0: 'S'
/// 1: 'w'
/// 2: 'i'
/// 3: 'f'
/// 4: 't'
public func enumerate<Seq : SequenceType>(
base: Seq
) -> EnumerateSequence<Seq> {
return EnumerateSequence(base)
}
/// Return `true` iff `a1` and `a2` contain the same elements in the
/// same order.
public func equal<
S1 : SequenceType, S2 : SequenceType
where
S1.Generator.Element == S2.Generator.Element,
S1.Generator.Element : Equatable
>(a1: S1, a2: S2) -> Bool
{
var g1 = a1.generate()
var g2 = a2.generate()
while true {
var e1 = g1.next()
var e2 = g2.next()
if (e1 != nil) && (e2 != nil) {
if e1! != e2! {
return false
}
}
else {
return (e1 == nil) == (e2 == nil)
}
}
}
/// Return true iff `a1` and `a2` contain equivalent elements, using
/// `isEquivalent` as the equivalence test. Requires: `isEquivalent`
/// is an `equivalence relation
/// <http://en.wikipedia.org/wiki/Equivalence_relation>`_
public func equal<
S1 : SequenceType, S2 : SequenceType
where
S1.Generator.Element == S2.Generator.Element
>(a1: S1, a2: S2,
@noescape isEquivalent: (S1.Generator.Element, S1.Generator.Element) -> Bool)
-> Bool
{
var g1 = a1.generate()
var g2 = a2.generate()
while true {
var e1 = g1.next()
var e2 = g2.next()
if (e1 != nil) && (e2 != nil) {
if !isEquivalent(e1!, e2!) {
return false
}
}
else {
return (e1 == nil) == (e2 == nil)
}
}
}
/// Return true iff a1 precedes a2 in a lexicographical ("dictionary")
/// ordering, using "<" as the comparison between elements.
public func lexicographicalCompare<
S1 : SequenceType, S2 : SequenceType
where
S1.Generator.Element == S2.Generator.Element,
S1.Generator.Element : Comparable>(
a1: S1, a2: S2) -> Bool {
var g1 = a1.generate()
var g2 = a2.generate()
while true {
var e1_ = g1.next()
var e2_ = g2.next()
if let e1? = e1_ {
if let e2? = e2_ {
if e1 < e2 {
return true
}
if e2 < e1 {
return false
}
continue // equivalent
}
return false
}
return e2_ != nil
}
}
/// Return true iff `a1` precedes `a2` in a lexicographical ("dictionary")
/// ordering, using `isOrderedBefore` as the comparison between elements.
///
/// Requires: isOrderedBefore` is a `strict weak ordering
/// <http://en.wikipedia.org/wiki/Strict_weak_order#Strict_weak_orderings>`__
/// over the elements of `a1` and `a2`.
public func lexicographicalCompare<
S1 : SequenceType, S2 : SequenceType
where
S1.Generator.Element == S2.Generator.Element
>(
a1: S1, a2: S2,
@noescape isOrderedBefore less: (S1.Generator.Element, S1.Generator.Element)
-> Bool
) -> Bool {
var g1 = a1.generate()
var g2 = a2.generate()
while true {
var e1_ = g1.next()
var e2_ = g2.next()
if let e1? = e1_ {
if let e2? = e2_ {
if less(e1, e2) {
return true
}
if less(e2, e1) {
return false
}
continue // equivalent
}
return false
}
return e2_ != nil
}
}
/// Return `true` iff an element in `seq` satisfies `predicate`.
public func contains<
S : SequenceType, L : BooleanType
>(seq: S, @noescape predicate: (S.Generator.Element) -> L) -> Bool {
for a in seq {
if predicate(a) {
return true
}
}
return false
}
/// Return `true` iff `x` is in `seq`.
public func contains<
S : SequenceType where S.Generator.Element : Equatable
>(seq: S, x: S.Generator.Element) -> Bool {
return contains(seq, { $0 == x })
}
/// Return the result of repeatedly calling `combine` with an
/// accumulated value initialized to `initial` and each element of
/// `sequence`, in turn.
public func reduce<S : SequenceType, U>(
sequence: S, initial: U, @noescape combine: (U, S.Generator.Element) -> U
) -> U {
var result = initial
for element in sequence {
result = combine(result, element)
}
return result
}