Proposal: Add function SequenceType.find()


(Lily Ballard) #1

I'm proposing a new extension method on SequenceType called find(). It's similar to CollectionType.indexOf() except it returns the element:

extension SequenceType {

/// Returns the first element where `predicate` returns `true`, or `nil`

/// if such value is not found.

public func find(@noescape predicate: (Self.Generator.Element) throws ->
Bool) rethrows -> Self.Generator.Element? {

for elt in self {

if try predicate(elt) {

return elt

}

}

return nil

}

}

-Kevin Ballard


(Keith Smiley) #2

+1. We've added an extension for this and find it very useful.

···

On Tue, Dec 29, 2015 at 18:38 Kevin Ballard via swift-evolution < swift-evolution@swift.org> wrote:

I'm proposing a new extension method on SequenceType called find(). It's
similar to CollectionType.indexOf() except it returns the element:

extension SequenceType {

    /// Returns the first element where `predicate` returns `true`, or
`nil`

    /// if such value is not found.

    public func find(@noescape predicate: (Self.Generator.Element) throws
-> Bool) rethrows -> Self.Generator.Element? {

        for elt in self {

            if try predicate(elt) {

                return elt

            }

        }

        return nil

    }

}

-Kevin Ballard

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Lily Ballard) #3

Proposal PR submitted as https://github.com/apple/swift-evolution/pull/94

-Kevin Ballard

···

On Tue, Dec 29, 2015, at 06:38 PM, Kevin Ballard wrote:

I'm proposing a new extension method on SequenceType called find().
It's similar to CollectionType.indexOf() except it returns the
element:

extensionSequenceType {

/// Returns the first element where `predicate` returns `true`,
or `nil`

/// if such value is not found.

public func find(@noescape predicate: (Self.Generator.Element) throws
-> Bool) rethrows -> Self.Generator.Element? {

for elt in self {

if try predicate(elt) {

return elt

}

}

return nil

}

}

-Kevin Ballard


(Radek Pietruszewski) #4

+1. Like others, this was one of the first stdlib extensions I’ve put into my Swift projects. It’s very common to want to find the first matching element in an array (or, perhaps even more common, find what you know if the only matching element — `filter` without the extra step of unpacking the value from array.)

It’s also common in other languages, and `find` is a good name.

— Radek

···

On 30 Dec 2015, at 03:38, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

I'm proposing a new extension method on SequenceType called find(). It's similar to CollectionType.indexOf() except it returns the element:

extension SequenceType {
    /// Returns the first element where `predicate` returns `true`, or `nil`
    /// if such value is not found.
    public func find(@noescape predicate: (Self.Generator.Element) throws -> Bool) rethrows -> Self.Generator.Element? {
        for elt in self {
            if try predicate(elt) {
                return elt
            }
        }
        return nil
    }
}

-Kevin Ballard

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(James Campbell) #5

We should add the full collection of ruby methods http://matthewcarriere.com/06/23/using-select-reject-collect-inject-and-detect/

···

Sent from my iPhone

On 30 Dec 2015, at 02:40, Keith Smiley via swift-evolution <swift-evolution@swift.org> wrote:

+1. We've added an extension for this and find it very useful.

On Tue, Dec 29, 2015 at 18:38 Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:
I'm proposing a new extension method on SequenceType called find(). It's similar to CollectionType.indexOf() except it returns the element:

extension SequenceType {
    /// Returns the first element where `predicate` returns `true`, or `nil`
    /// if such value is not found.
    public func find(@noescape predicate: (Self.Generator.Element) throws -> Bool) rethrows -> Self.Generator.Element? {
        for elt in self {
            if try predicate(elt) {
                return elt
            }
        }
        return nil
    }
}

-Kevin Ballard

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Andrew Bennett) #6

+1 we also have a very similar extension.

···

On Wed, Dec 30, 2015 at 1:40 PM, Keith Smiley via swift-evolution < swift-evolution@swift.org> wrote:

+1. We've added an extension for this and find it very useful.
On Tue, Dec 29, 2015 at 18:38 Kevin Ballard via swift-evolution < > swift-evolution@swift.org> wrote:

I'm proposing a new extension method on SequenceType called find(). It's
similar to CollectionType.indexOf() except it returns the element:

extension SequenceType {

    /// Returns the first element where `predicate` returns `true`, or
`nil`

    /// if such value is not found.

    public func find(@noescape predicate: (Self.Generator.Element) throws
-> Bool) rethrows -> Self.Generator.Element? {

        for elt in self {

            if try predicate(elt) {

                return elt

            }

        }

        return nil

    }

}

-Kevin Ballard

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


#7

Is it worth considering a version that returns a tuple of (index, element) ?

Nevin

···

On Tue, Dec 29, 2015 at 9:40 PM, Keith Smiley via swift-evolution < swift-evolution@swift.org> wrote:

+1. We've added an extension for this and find it very useful.
On Tue, Dec 29, 2015 at 18:38 Kevin Ballard via swift-evolution < > swift-evolution@swift.org> wrote:

I'm proposing a new extension method on SequenceType called find(). It's
similar to CollectionType.indexOf() except it returns the element:

extension SequenceType {

    /// Returns the first element where `predicate` returns `true`, or
`nil`

    /// if such value is not found.

    public func find(@noescape predicate: (Self.Generator.Element) throws
-> Bool) rethrows -> Self.Generator.Element? {

        for elt in self {

            if try predicate(elt) {

                return elt

            }

        }

        return nil

    }

}

-Kevin Ballard

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Marco Masser) #8

I just saw this PR and have a quick comment about the implementation: It could be shortened by a few lines using a where clause like this:

func find(@noescape predicate: Self.Generator.Element throws -> Bool) rethrows -> Self.Generator.Element? {
    for element in self where try predicate(element) {
        return element
    }
    return nil
}

Also, I’d like suggest adding a variant that works better with heterogenous sequences, but I’ll raise that when this proposal goes into review.

FYI: That variant would look like this:
func find<T>(_: T.Type, @noescape matching predicate: T throws -> Bool) rethrows -> T?

A question by me on the swift-users mailing list describes its intention, implementation, and usage:
[swift-users] Which is the more idiomatic approach for pinning down the type of a generic parameter? <https://lists.swift.org/pipermail/swift-users/Week-of-Mon-20160118/000893.html>

Cheers,

Marco

···

On 2016-01-10, at 01:58, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

Proposal PR submitted as https://github.com/apple/swift-evolution/pull/94

-Kevin Ballard

On Tue, Dec 29, 2015, at 06:38 PM, Kevin Ballard wrote:

I'm proposing a new extension method on SequenceType called find(). It's similar to CollectionType.indexOf() except it returns the element:

extensionSequenceType {
/// Returns the first element where `predicate` returns `true`, or `nil`
/// if such value is not found.
public func find(@noescape predicate: (Self.Generator.Element) throws -> Bool) rethrows -> Self.Generator.Element? {
for elt in self {
if try predicate(elt) {
return elt
            }
        }
return nil
    }
}

-Kevin Ballard

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Dave Abrahams) #9

+1. Like others, this was one of the first stdlib extensions I’ve put
into my Swift projects. It’s very common to want to find the first
matching element in an array (or, perhaps even more common, find what
you know if the only matching element — `filter` without the extra
step of unpacking the value from array.)

It’s also common in other languages, and `find` is a good name.

My only hesitation is that find has the strong connotation of not
modifying the underlying sequence, but any single-pass sequence will be
consumed. I don't know why that should bother me, though, considering
the many other such methods we have defined on SequenceType.

···

on Wed Feb 17 2016, Radosław Pietruszewski <swift-evolution@swift.org> wrote:

— Radek

On 30 Dec 2015, at 03:38, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

I'm proposing a new extension method on SequenceType called find(). It's similar to CollectionType.indexOf() except it returns the element:

extension SequenceType {
    /// Returns the first element where `predicate` returns `true`, or `nil`
    /// if such value is not found.
    public func find(@noescape predicate: (Self.Generator.Element) throws -> Bool) rethrows -> Self.Generator.Element? {
        for elt in self {
            if try predicate(elt) {
                return elt
            }
        }
        return nil
    }
}

-Kevin Ballard

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
-Dave


(Lily Ballard) #10

We should add the full collection of ruby methods http://matthewcarriere.com/06/23/using-select-reject-collect-inject-and-detect/

We already have them. Well, almost:

select - this is filter reject - this is just filter with a negated
predicate collect - this is map inject - this is reduce detect - this is
the find() method that I'm proposing here.

-Kevin

+1. We've added an extension for this and find it very useful. On
Tue, Dec 29, 2015 at 18:38 Kevin Ballard via swift-evolution <swift-

__
I'm proposing a new extension method on SequenceType called find().
It's similar to CollectionType.indexOf() except it returns the
element:

extensionSequenceType {

/// Returns the first element where `predicate` returns `true`,
or `nil`

/// if such value is not found.

public func find(@noescape predicate: (Self.Generator.Element)
throws -> Bool) rethrows -> Self.Generator.Element? {

for elt in self {

if try predicate(elt) {

return elt

}

}

return nil

}

}

-Kevin Ballard

_______________________________________________

swift-evolution mailing list

···

On Wed, Dec 30, 2015, at 02:13 AM, James Campbell wrote:

On 30 Dec 2015, at 02:40, Keith Smiley via swift-evolution <swift- > evolution@swift.org> wrote:

evolution@swift.org> wrote:

swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Donnacha Oisín Kidney) #11

+1 on this.

I see a lot of code like this:

sequence.filter(predicate).first

Which is of course is inefficient. However, the go-to optimisation:

sequence.lazy.filter(predicate).first

Is not necessarily better, and has some strange behaviour:

let array = [1, 2, 3, 4, 5, 6]
func noisyPredicate(n: Int) -> Bool {
  print(n, terminator: " ")
  return n > 2
}

array.lazy.filter(noisyPredicate).first
// 1 2 3 1 2 3

AnySequence(array).lazy.filter(noisyPredicate).first
// 1 2 3 4 5 6

If it’s called on a collection, the collection is only evaluated up until the element being looked for, but it’s done twice. If it’s called on a sequence, the whole sequence is evaluated, regardless of where the element is found.

I think that find is maybe not the best name, though. It’s not immediately clear that it doesn’t return an index. I’d prefer to call it first, as in:

extension SequenceType {
  /// Returns the first element where `predicate` returns `true`, or `nil`
  /// if such value is not found.
  public func first(@noescape thatSatisfies: (Self.Generator.Element) throws -> Bool) rethrows -> Self.Generator.Element? {
    for elt in self {
      if try thatSatisfies(elt) {
        return elt
      }
    }
    return nil
  }
}

[1, 2, 3, 4, 5].first(thatSatisfies: (Int) throws -> Bool)

[1, 2, 3, 4, 5].first { $0 > 3 }

···

On 30 Dec 2015, at 10:13, James Campbell via swift-evolution <swift-evolution@swift.org> wrote:

We should add the full collection of ruby methods http://matthewcarriere.com/06/23/using-select-reject-collect-inject-and-detect/

Sent from my iPhone

On 30 Dec 2015, at 02:40, Keith Smiley via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

+1. We've added an extension for this and find it very useful.
On Tue, Dec 29, 2015 at 18:38 Kevin Ballard via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
I'm proposing a new extension method on SequenceType called find(). It's similar to CollectionType.indexOf() except it returns the element:

extension SequenceType {
    /// Returns the first element where `predicate` returns `true`, or `nil`
    /// if such value is not found.
    public func find(@noescape predicate: (Self.Generator.Element) throws -> Bool) rethrows -> Self.Generator.Element? {
        for elt in self {
            if try predicate(elt) {
                return elt
            }
        }
        return nil
    }
}

-Kevin Ballard

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Lily Ballard) #12

Good thought, but I doubt such a method would pull its own weight.

-Kevin Ballard

Is it worth considering a version that returns a tuple of (index,
element) ?

Nevin

+1. We've added an extension for this and find it very useful. On
Tue, Dec 29, 2015 at 18:38 Kevin Ballard via swift-evolution <swift-

__
I'm proposing a new extension method on SequenceType called find().
It's similar to CollectionType.indexOf() except it returns the
element:

extensionSequenceType {

/// Returns the first element where `predicate` returns `true`,
or `nil`

/// if such value is not found.

public func find(@noescape predicate: (Self.Generator.Element)
throws -> Bool) rethrows -> Self.Generator.Element? {

for elt in self {

if try predicate(elt) {

return elt

}

}

return nil

}

}

-Kevin Ballard

_______________________________________________

swift-evolution mailing list

swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________

swift-evolution mailing list

···

On Wed, Dec 30, 2015, at 04:24 PM, Nevin Brackett-Rozinsky wrote:

On Tue, Dec 29, 2015 at 9:40 PM, Keith Smiley via swift-evolution <swift- > evolution@swift.org> wrote:

evolution@swift.org> wrote:
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


#13

I think

        subviews.find{ ($0 as? NSButton)?.state == NSOnState } as? NSButton

is sufficient and makes it clear that you are searching for a specific type. Even though you have to cast twice I don't think an additional method would be worth its use since it isn't used as often.

- Maximilian

···

Am 17.02.2016 um 10:32 schrieb Marco Masser via swift-evolution <swift-evolution@swift.org>:

I just saw this PR and have a quick comment about the implementation: It could be shortened by a few lines using a where clause like this:

func find(@noescape predicate: Self.Generator.Element throws -> Bool) rethrows -> Self.Generator.Element? {
    for element in self where try predicate(element) {
        return element
    }
    return nil
}

Also, I’d like suggest adding a variant that works better with heterogenous sequences, but I’ll raise that when this proposal goes into review.

FYI: That variant would look like this:
func find<T>(_: T.Type, @noescape matching predicate: T throws -> Bool) rethrows -> T?

A question by me on the swift-users mailing list describes its intention, implementation, and usage:
[swift-users] Which is the more idiomatic approach for pinning down the type of a generic parameter?

Cheers,

Marco

On 2016-01-10, at 01:58, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

Proposal PR submitted as https://github.com/apple/swift-evolution/pull/94

-Kevin Ballard

On Tue, Dec 29, 2015, at 06:38 PM, Kevin Ballard wrote:
I'm proposing a new extension method on SequenceType called find(). It's similar to CollectionType.indexOf() except it returns the element:

extensionSequenceType {
/// Returns the first element where `predicate` returns `true`, or `nil`
/// if such value is not found.
public func find(@noescape predicate: (Self.Generator.Element) throws -> Bool) rethrows -> Self.Generator.Element? {
for elt in self {
if try predicate(elt) {
return elt
            }
        }
return nil
    }
}

-Kevin Ballard

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Lily Ballard) #14

+1 on this.

I see a lot of code like this:

sequence.filter(predicate).first

Which is of course is inefficient. However, the go-to optimisation:

sequence.lazy.filter(predicate).first

Is not necessarily better, and has some strange behaviour:

let array = [1, 2, 3, 4, 5, 6] func noisyPredicate(n: Int) -> Bool {
print(n, terminator: " ") return n > 2 }

array.lazy.filter(noisyPredicate).first // 1 2 3 1 2 3

AnySequence(array).lazy.filter(noisyPredicate).first // 1 2 3 4 5 6

If it’s called on a collection, the collection is only evaluated up
until the element being looked for, but it’s done twice. If it’s
called on a sequence, the *whole* sequence is evaluated, regardless of
where the element is found.

Actually, you're calling it on a collection in both cases. SequenceType
doesn't have a `first` property (honestly, I think it should). Saying
`lazy.filter(noisyPredicate)` is actually resolving to the version of
filter() from SequenceType, the one that returns an Array, because the
one from LazySequenceType that returns a LazyFilterSequence doesn't have
a `first` property.

As for it calling it twice on a collection, this is because it's
implemented as `isEmpty ? nil : self[startIndex]`, which is honestly
pretty silly.

FWIW, there's other sequence methods that actually evaluate lazy
collections twice, because they detect that the receiver is a
collection and run a preprocessing pass first. For example, the
version of joinWithSeparator() that returns a String does this (to
size all of the elements so it can preallocate a buffer), which causes
duplicate evaluation of lazy filter/map collections. I filed this a
while ago as SR-68[1].

I think that find is maybe not the best name, though. It’s not
immediately clear that it doesn’t return an index.

indexOf() used to be called find(), but it was renamed because find()
didn't make it obvious that it returned an index. I know I personally
got confused all the time about what find() returned.

Also, this is a sequence method. Sequences don't have indexes.

-Kevin Ballard

I’d prefer to call it first, as in:

extensionSequenceType { /// Returns the first element where
`predicate` returns `true`, or `nil` /// if such value is not found.
public func first(@noescape thatSatisfies: (Self.Generator.Element)
throws -> Bool) rethrows -> Self.Generator.Element? { for elt in self
{ if try thatSatisfies(elt) { return elt } } returnnil } }

[1, 2, 3, 4, 5].first(thatSatisfies: (Int) throws -> Bool)

[1, 2, 3, 4, 5].first { $0 > 3 }

We should add the full collection of ruby methods
http://matthewcarriere.com/06/23/using-select-reject-collect-inject-and-detect/

Sent from my iPhone

+1. We've added an extension for this and find it very useful. On
Tue, Dec 29, 2015 at 18:38 Kevin Ballard via swift-evolution <swift-

__
I'm proposing a new extension method on SequenceType called find().
It's similar to CollectionType.indexOf() except it returns the
element:

extensionSequenceType{ /// Returns the first element where
`predicate` returns `true`, or `nil` /// if such value is not
found. publicfuncfind(@noescapepredicate: (Self.Generator.Element)throws->Bool)rethrows-
>Self.Generator.Element? { foreltinself{ iftrypredicate(elt) {
returnelt } } returnnil } }

-Kevin Ballard

_______________________________________________
swift-evolution mailing list swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_________________________________________________
swift-evolution mailing list swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Links:

  1. https://bugs.swift.org/browse/SR-68

···

On Wed, Dec 30, 2015, at 07:33 AM, Donnacha Oisín Kidney via swift-evolution wrote:

On 30 Dec 2015, at 10:13, James Campbell via swift-evolution <swift- >> evolution@swift.org> wrote:
On 30 Dec 2015, at 02:40, Keith Smiley via swift-evolution <swift- >> evolution@swift.org> wrote:

evolution@swift.org> wrote:


(James Campbell) #15

Ruby calls that method detect. As in detect and return the item that matches this condition

···

Sent from my iPhone

On 30 Dec 2015, at 15:33, Donnacha Oisín Kidney <oisin.kidney@gmail.com> wrote:

+1 on this.

I see a lot of code like this:

sequence.filter(predicate).first

Which is of course is inefficient. However, the go-to optimisation:

sequence.lazy.filter(predicate).first

Is not necessarily better, and has some strange behaviour:

let array = [1, 2, 3, 4, 5, 6]
func noisyPredicate(n: Int) -> Bool {
  print(n, terminator: " ")
  return n > 2
}

array.lazy.filter(noisyPredicate).first
// 1 2 3 1 2 3

AnySequence(array).lazy.filter(noisyPredicate).first
// 1 2 3 4 5 6

If it’s called on a collection, the collection is only evaluated up until the element being looked for, but it’s done twice. If it’s called on a sequence, the whole sequence is evaluated, regardless of where the element is found.

I think that find is maybe not the best name, though. It’s not immediately clear that it doesn’t return an index. I’d prefer to call it first, as in:

extension SequenceType {
  /// Returns the first element where `predicate` returns `true`, or `nil`
  /// if such value is not found.
  public func first(@noescape thatSatisfies: (Self.Generator.Element) throws -> Bool) rethrows -> Self.Generator.Element? {
    for elt in self {
      if try thatSatisfies(elt) {
        return elt
      }
    }
    return nil
  }
}

[1, 2, 3, 4, 5].first(thatSatisfies: (Int) throws -> Bool)

[1, 2, 3, 4, 5].first { $0 > 3 }

On 30 Dec 2015, at 10:13, James Campbell via swift-evolution <swift-evolution@swift.org> wrote:

We should add the full collection of ruby methods http://matthewcarriere.com/06/23/using-select-reject-collect-inject-and-detect/

Sent from my iPhone

On 30 Dec 2015, at 02:40, Keith Smiley via swift-evolution <swift-evolution@swift.org> wrote:

+1. We've added an extension for this and find it very useful.

On Tue, Dec 29, 2015 at 18:38 Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:
I'm proposing a new extension method on SequenceType called find(). It's similar to CollectionType.indexOf() except it returns the element:

extension SequenceType {
    /// Returns the first element where `predicate` returns `true`, or `nil`
    /// if such value is not found.
    public func find(@noescape predicate: (Self.Generator.Element) throws -> Bool) rethrows -> Self.Generator.Element? {
        for elt in self {
            if try predicate(elt) {
                return elt
            }
        }
        return nil
    }
}

-Kevin Ballard

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(James Campbell) #16

But overloading the first method with a closure is a clever ide s

···

Sent from my iPhone

On 30 Dec 2015, at 15:33, Donnacha Oisín Kidney <oisin.kidney@gmail.com> wrote:

+1 on this.

I see a lot of code like this:

sequence.filter(predicate).first

Which is of course is inefficient. However, the go-to optimisation:

sequence.lazy.filter(predicate).first

Is not necessarily better, and has some strange behaviour:

let array = [1, 2, 3, 4, 5, 6]
func noisyPredicate(n: Int) -> Bool {
  print(n, terminator: " ")
  return n > 2
}

array.lazy.filter(noisyPredicate).first
// 1 2 3 1 2 3

AnySequence(array).lazy.filter(noisyPredicate).first
// 1 2 3 4 5 6

If it’s called on a collection, the collection is only evaluated up until the element being looked for, but it’s done twice. If it’s called on a sequence, the whole sequence is evaluated, regardless of where the element is found.

I think that find is maybe not the best name, though. It’s not immediately clear that it doesn’t return an index. I’d prefer to call it first, as in:

extension SequenceType {
  /// Returns the first element where `predicate` returns `true`, or `nil`
  /// if such value is not found.
  public func first(@noescape thatSatisfies: (Self.Generator.Element) throws -> Bool) rethrows -> Self.Generator.Element? {
    for elt in self {
      if try thatSatisfies(elt) {
        return elt
      }
    }
    return nil
  }
}

[1, 2, 3, 4, 5].first(thatSatisfies: (Int) throws -> Bool)

[1, 2, 3, 4, 5].first { $0 > 3 }

On 30 Dec 2015, at 10:13, James Campbell via swift-evolution <swift-evolution@swift.org> wrote:

We should add the full collection of ruby methods http://matthewcarriere.com/06/23/using-select-reject-collect-inject-and-detect/

Sent from my iPhone

On 30 Dec 2015, at 02:40, Keith Smiley via swift-evolution <swift-evolution@swift.org> wrote:

+1. We've added an extension for this and find it very useful.

On Tue, Dec 29, 2015 at 18:38 Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:
I'm proposing a new extension method on SequenceType called find(). It's similar to CollectionType.indexOf() except it returns the element:

extension SequenceType {
    /// Returns the first element where `predicate` returns `true`, or `nil`
    /// if such value is not found.
    public func find(@noescape predicate: (Self.Generator.Element) throws -> Bool) rethrows -> Self.Generator.Element? {
        for elt in self {
            if try predicate(elt) {
                return elt
            }
        }
        return nil
    }
}

-Kevin Ballard

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Lily Ballard) #17

Alternatively you can write

subviews.lazy.flatMap({ $0 as? NSButton }).find({ $0.state ==
NSOnState })

-Kevin Ballard

···

On Wed, Feb 17, 2016, at 07:12 AM, Maximilian Hünenberger via swift-evolution wrote:

I think

subviews.find{ ($0 as? NSButton)?.state == NSOnState } as? NSButton

is sufficient and makes it clear that you are searching for a specific
type. Even though you have to cast twice I don't think an additional
method would be worth its use since it isn't used as often.

- Maximilian

Am 17.02.2016 um 10:32 schrieb Marco Masser via swift-evolution <swift- > evolution@swift.org>:

I just saw this PR and have a quick comment about the implementation:
It could be shortened by a few lines using a where clause like this:

func find(@noescape predicate: Self.Generator.Elementthrows -> Bool)
rethrows -> Self.Generator.Element? { for element inselfwheretry
predicate(element) { return element } returnnil }

Also, I’d like suggest adding a variant that works better with
heterogenous sequences, but I’ll raise that when this proposal goes
into review.

FYI: That variant would look like this: func find<T>(_: T.Type,
@noescape matching predicate: Tthrows -> Bool) rethrows -> T?

A question by me on the swift-users mailing list describes its
intention, implementation, and usage: [swift-users] Which is the more
idiomatic approach for pinning down the type of a generic
parameter?[1]

Cheers,

Marco

On 2016-01-10, at 01:58, Kevin Ballard via swift-evolution <swift- >>> evolution@swift.org> wrote:

Proposal PR submitted as
https://github.com/apple/swift-evolution/pull/94

-Kevin Ballard

On Tue, Dec 29, 2015, at 06:38 PM, Kevin Ballard wrote:

I'm proposing a new extension method on SequenceType called find().
It's similar to CollectionType.indexOf() except it returns the
element:

extensionSequenceType { /// Returns the first element where
`predicate` returns `true`, or `nil` /// if such value is not
found. public func find(@noescape predicate:
(Self.Generator.Element) throws -> Bool) rethrows ->
Self.Generator.Element? { for elt in self { if try predicate(elt) {
return elt } } return nil } }

-Kevin Ballard

_______________________________________________
swift-evolution mailing list swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_________________________________________________
swift-evolution mailing list swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Links:

  1. https://lists.swift.org/pipermail/swift-users/Week-of-Mon-20160118/000893.html


(Marco Masser) #18

While I very much prefer the version without any casts, I suspect that you’re right in that the consensus will probably be that adding an additional method for heterogenous sequences will be considered not to be worthwhile. But maybe I’m wrong and if not, I can still keep my own implementation.

Cheers,

Marco

···

On 2016-02-17, at 16:12, Maximilian Hünenberger <m.huenenberger@me.com> wrote:

I think

        subviews.find{ ($0 as? NSButton)?.state == NSOnState } as? NSButton

is sufficient and makes it clear that you are searching for a specific type. Even though you have to cast twice I don't think an additional method would be worth its use since it isn't used as often.


(Marco Masser) #19

Just to reiterate my point: I very much prefer the version without any casts. I just think this is much clearer and doesn’t put the burden of thinking about accidental memory and speed overhead (when forgetting the “lazy”) on the caller every time:

subviews.find(NSButton.self, matching: { $0.state == NSOnState })

Also, there’s the goal of Swift being easy to learn. Which line do you think is easier to explain to newcomers?

···

On 2016-02-18, at 00:46, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

Alternatively you can write

    subviews.lazy.flatMap({ $0 as? NSButton }).find({ $0.state == NSOnState })


(Trent Nadeau) #20

I would prefer a label on the first parameter, since you're not finding the
NSButton type but instead elements having the type of NSButton.

subviews.find(havingType: NSButton.self, matching: { $0.state == NSOnState
})

···

On Thu, Feb 18, 2016 at 4:37 AM, Marco Masser via swift-evolution < swift-evolution@swift.org> wrote:

On 2016-02-18, at 00:46, Kevin Ballard via swift-evolution < > swift-evolution@swift.org> wrote:

Alternatively you can write

    subviews.lazy.flatMap({ $0 as? NSButton }).find({ $0.state ==
NSOnState })

Just to reiterate my point: I very much prefer the version without any
casts. I just think this is much clearer and doesn’t put the burden of
thinking about accidental memory and speed overhead (when forgetting the
“lazy”) on the caller every time:

subviews.find(NSButton.self, matching: { $0.state == NSOnState })

Also, there’s the goal of Swift being easy to learn. Which line do you
think is easier to explain to newcomers?

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
Trent Nadeau