Why popFirst throws an error, but removeFirst works? - arrays

struct Queue<T>{
private var elements : [T] = []
public mutating func enqueue(_ element: T){
elements.append(element)
}
public mutating func dequeue() -> T?{
return elements.popFirst() // ERROR!
}
public mutating func dequeue2() -> T?{
return elements.removeFirst()
}
}
The error I get for popFirst is:
cannot use mutating member on immutable value: 'self' is
immutable
Both popFirst and removeFirst are marked as mutating and both return and T?. So why isn't it working?
EDIT: As others have commented, it seems to be some sort of bug. It's been discussed in the forums here.
EDIT: Still happens in Xcode 9.4.1 (Swift 4.1.2)

The error is improved in Swift 4.2:
error: ios.playground:4:25: error: '[T]' requires the types '[T]' and 'ArraySlice<T>' be equivalent to use 'popFirst'
return elements.popFirst() // ERROR!
^
You get the error because popFirst is not defined for all Collections. It's only defined if the Collection is its own SubSequence type. Here's the implementation:
extension Collection where SubSequence == Self {
/// Removes and returns the first element of the collection.
///
/// - Returns: The first element of the collection if the collection is
/// not empty; otherwise, `nil`.
///
/// - Complexity: O(1)
#inlinable
public mutating func popFirst() -> Element? {
// TODO: swift-3-indexing-model - review the following
guard !isEmpty else { return nil }
let element = first!
self = self[index(after: startIndex)..<endIndex]
return element
}
}
The extension requires SubSequence == Self. Self (with a capital S) is the type of self (with a lower-case s). It is the type on which you're calling popFirst. In your code, Self is Array<T>, also spelled [T].
The constraint is necessary for this line of popFirst to compile:
self = self[index(after: startIndex)..<endIndex]
^__^ ^_______________________________________^
| |
| This is a SubSequence.
|
This is a Self.
self[index(after: startIndex)..<endIndex] is a SubSequence.
Swift can only assign a SubSequence to Self if it knows that Self == SubSequence.
Array's SubSequence type is ArraySlice. Since ArraySlice is not the same type as Array, this extension doesn't apply to Array.

Here's the extension you're probably looking for:
extension Array {
#inlinable
public mutating func popFirst() -> Element? {
if isEmpty {
return nil
} else {
return removeFirst()
}
}
}

Related

Swift Extension of Array with Equatable Elements Cannot Call Index(of:)

I am attempting to add an extension to the Array type in Swift limited to Arrays whose elements conform to the equatable protocol. I am attempting to define a function in the following manner:
import Foundation
extension Array where Iterator.Element: Equatable {
func deletedIndicies<T: Equatable>(newArray: [T]) -> [Int] {
var indicies = [Int]()
for element in self {
if newArray.index(of: element) == nil {
indicies.append(self.index(of: element)!)
}
}
return indicies
}
}
}
The purpose of the function is to return the indices of any items in the original array that do not appear in the newArray.
The error I receive in Xcode is: Cannot invoke 'index' with an argument list of type '(of: Element)'
Since I am defining the function for only Arrays whose elements are equatable and am requiring that the elements of the newArray are equatable, I am unsure why I cannot invoke the index method.
The problem is you're defining a new generic placeholder T in your method – which is not necessarily the same type as Element. Therefore when you say newArray.index(of: element), you're trying to pass an Element into a argument of type T.
The solution therefore is to simply to type the newArray: parameter as [Element]:
extension Array where Element : Equatable {
func deletedIndicies(byKeeping elementsToKeep: [Element]) -> [Int] {
// ...
}
}
As a side note, this method could also be implemented as:
extension Array where Element : Equatable {
func deletedIndicies(byKeeping elementsToKeep: [Element]) -> [Int] {
// use flatMap(_:) to iterate over a sequence of pairs of elements with indices,
// returning the index of the element, if elementsToKeep doesn't contains it,
// or nil otherwise, in which case flatMap(_:) will filter it out.
return self.enumerated().flatMap {
elementsToKeep.contains($1) ? nil : $0
}
}
}
Also, if you change the constraint on Element to Hashable, this could be also be implemented in O(n), rather than O(n * m) time, which could potentially be desirable:
extension Array where Element : Hashable {
func deletedIndicies(byKeeping elementsToKeep: [Element]) -> [Int] {
// create new set of elements to keep.
let setOfElementsToKeep = Set(elementsToKeep)
return self.enumerated().flatMap {
setOfElementsToKeep.contains($1) ? nil : $0
}
}
}

Swift3: type inference inside generic extension

I need to do something like this:
extension Array {
func flat() -> Element { return self.flatMap { $0 } }
}
But there is problem with type inference:
'flatMap' produces '[SegmentOfResult.Iterator.Element]', not the
expected contextual result type 'Element'
Edit: Usage example:
[[1,2],[3,4,5],[6]].flat()
should produce [1,2,3,4,5,6] which is the same as:
[[1,2],[3,4,5],[6]].flatMap { $0 }
If you take a look at the flatMap(_:) signature,
extension Sequence {
// ...
public func flatMap<SegmentOfResult : Sequence>(_ transform: (Self.Iterator.Element) throws -> SegmentOfResult) rethrows -> [SegmentOfResult.Iterator.Element]
// ...
}
you'll see that it returns a [SegmentOfResult.Iterator.Element], where SegmentOfResult is the type that is returned from the function you pass it. This isn't necessarily the same type as Element (as your extension is for all arrays), which is why your code fails to compile.
In order to be working with arrays where the elements are sequences, you'll want to constrain your extension so that Element : Sequence.
Also, as the function you're passing to flatMap(_:) is an identity transform (it just returns the element it receives as input), you'll want to change the return type to [Element.Iterator.Element] (an array of the inner element).
extension Array where Element : Sequence {
func flat() -> [Element.Iterator.Element] {
return self.flatMap { $0 }
}
}
Although that being said, I see no reason why this shouldn't be an extension of Sequence:
// An extension for a sequence of sequences
extension Sequence where Iterator.Element : Sequence {
// returns an array of the inner element type (an array of the element of the element)
func flat() -> [Iterator.Element.Iterator.Element] {
return self.flatMap { $0 }
}
}
(However, I don't really see the need to create an extension for this in the first place – array.flatMap{$0} isn't exactly lengthy!)

How do I implement the filter function in Swift

To gain a better understanding of Swift, I've extended SequenceType to add my own versions of map, reduce and forEach functions. However, the filter function is different in that it returns an array of Self.Generator.Element. Here's what I have so far:
extension SequenceType {
public func myFilter(#noescape includeElement: (Self.Generator.Element) throws -> Bool) rethrows -> [Self.Generator.Element] {
var array = [Self.Generator.Element]()
for element in self {
do {
if try includeElement (element) {
array.append(element)
}
}
}
return array
}
}
The statement var array = [Self.Generator.Element]() produces an "Invalid use of () to call a value of non-function type [Self.Generator.Element.Type]" error. My question is how do I create/add to/return an array of Self.Generator.Element?
Not sure why, but Xcode is fine with following syntax:
extension SequenceType {
public func myFilter(#noescape includeElement: (Self.Generator.Element) throws -> Bool) rethrows -> [Self.Generator.Element] {
var array : [Self.Generator.Element] = []
for element in self {
do {
if try includeElement (element) {
array.append(element)
}
}
}
return array
}
}
The only change is the array declaration (var array : [Self.Generator.Element] = []).
Swift's type inference is getting confused about what you mean. This could mean two things:
var array = [Self.Generator.Element]()
You might mean (and do mean) that you want to call the constructor for the type Array<Self.Generator.Element>.
But you might mean (and the compiler thinks you mean) that you want to create an array of Self.Generator.Element.Type and then you want to call it as a function.
I don't remember this being a compiler confusion in the past, and it may be a regression. That said, it is ambiguous (though only one really makes sense in the end, and I would think you'd need to have said Generator.Element.self there to refer to the type, so maybe it's a real compiler bug). You can remove the ambiguity by using the preferred way to initialize arrays, by using their type:
var array: [Self.Generator.Element] = []

Swift Cannot invoke 'find' with an argument list of type '([Score], Score)' where Score is a struct

While find(["a", "b"], "c") works with no problems, I get an error when trying to find the index of a structure inside an array of structures:
struct Score
{
//...
}
var scores: [Score] = //...
var score: Score = //...
find(self.scores, score) // Error: Cannot invoke 'find' with an argument list of type '([Score], Score)'
I though it could be a problem with structures that can't be compared to each other by default. But changing Scores definition to class
gives me the same error.
EDIT: as of Swift 2.0, there is now a built-in version of find that takes a closure so you don’t have to write your own – but also, find has been renamed indexOf, and is now a protocol extension of CollectionType, so you call it like a method:
// if you make `Score` conform to `Equatable:
if let idx = self.scores.indexOf(score) {
}
// or if you don't make it Equatable, you can just use a closure:
// (see original answer below for why you might prefer to do this)
if let idx = scores.indexOf({$0.scoreval == 3}) {
}
Original pre-2.0 answer below
While the answers suggesting making your class Equatable may work nicely, I'd recommend a bit of caution before choosing to do this. The reason being that, as the docs state, equatability implies substitutability, and your == operator must be reflexive, symmetric and transitive. If you don't adhere to this, you might get some very weird behavior when using algorithms like equals, sort etc. Be especially cautious if implementing Equatable on non-final classes. If you're sure you can meet requirements, go for it, and find will work.
If not, an alternative you could consider is writing a function that ought to be in the standard library but isn't, which is a find that takes a closure:
func find<C: CollectionType>(source: C, match: C.Generator.Element -> Bool) -> C.Index {
for idx in indices(source) {
if match(source[idx]) { return idx }
}
return nil
}
Once you have this, you can supply any matching criteria you prefer. For example, if your objects are classes you could use reference equality:
let idx = find(scores) { $0 === $1 }
The interface for the function find is/was:
func find<C : CollectionType where C.Generator.Element : Equatable>(domain: C,
value: C.Generator.Element) -> C.Index?
This says that the CollectionType of C must have elements that are Equatable and, furthermore, that the value must also be Equatable.
[Note Swift 3.0: As of Swift 3.0, you'll need to use the index function which comes in two variations. In the first, you'll supply your own predicate:
func index(where: (Self.Generator.Element) -> Bool) -> Self.Index?
In the second, your elements need to be equatable:
// Where Generator.Element : Equatable
func index(of: Self.Generator.Element) -> Self.Index?
If you decide to go the equatable route, then the following applies.
Note End]
Your Score struct is not Equatable and hence the error. You'll need to figure out what it means for scores to be equal to one another. Maybe it is some numerical 'score'; maybe it is a 'score' and a 'user id'. It depends on your Score abstraction. Once you know, you implement == using:
func == (lhs:Score, rhs:Score) -> Bool {
return // condition for what it means to be equal
}
Note: if you use class and thus scores have 'identity' then you can implement this as:
func == (lhs:Score, rhs:Score) -> Bool { return lhs === rhs }
Your example with strings works because String is Equatable. If you look in the Swift library code you'll see:
extension String : Equatable {}
func ==(lhs: String, rhs: String) -> Bool
As the others have said, the objects you search for must conform to the Equatable protocol.
So you need to add an extension to your Score struct that tells the compiler that it conforms to that protocol:
extension Score: Equatable {}
And then you need to implement the == function for that class:
public func ==(lhs: Score, rhs: Score) -> Bool
{
return lhs.whatever == rhs.whatever //replace with code for your struct.
}

Using find() on an array of protocol types

Whenever I create an array of Protocol Types, how would I obtain the index of an object in that array? I've tried the below:
protocol aProtocol {
func doSomething()
}
class aClass: aProtocol, Equatable {
var aProperty = "test"
func doSomething() {
}
}
func == (lhs: aClass, rhs: aClass) -> Bool {
return lhs.aProperty == rhs.aProperty
}
var testArray = aProtocol[]()
let testObject = aClass()
testArray += testObject
find(testArray, testObject)
In this case I get a "Cannot convert the expression's type '$T4?' to type 'aClass'" error.
Looking at find()'s method signature we find that the element should implement Equatable (which is why I overload the == operator above):
func find<C : Collection where C.GeneratorType.Element : Equatable>(domain: C, value: C.GeneratorType.Element) -> C.IndexType?
Does anyone have any insight as to what I'm doing wrong or if this scenario is even possible? Thanks.
If find() expects its first argument to have elements that are Equatable and its second argument to be Equatable too, then invoking find() on an array of non-Equatable aProtocol elements won't compile.
To fix, change to
protocol aProtocol : Equatable { ... }
and then implement the Equatable protocol for your aClass definition.
[I'm not in front of a Mac at the moment, so I can't verify this suggestion.]
You overload the == function, but you don't mark aClass as conforming to the Equatable protocol.
https://developer.apple.com/library/prerelease/ios/documentation/General/Reference/SwiftStandardLibraryReference/Equatable.html

Resources