This comparison worked in Swift 2 but doesn't anymore in Swift 3:
let myStringContainsOnlyOneCharacter = mySting.rangeOfComposedCharacterSequence(at: myString.startIndex) == mySting.characters.indices
How do I compare Range and DefaultBidirectionalIndices?
From SE-0065 – A New Model for Collections and Indices
In Swift 2, collection.indices returned a Range<Index>, but because a range is a simple pair of indices and indices can no longer be advanced on their own, Range<Index> is no longer iterable.
In order to keep code like the above working, Collection has acquired an associated Indices type that is always iterable, ...
Since rangeOfComposedCharacterSequence returns a range of
character indices, the solution is not to use indices, but
startIndex..<endIndex:
myString.rangeOfComposedCharacterSequence(at: myString.startIndex)
== myString.startIndex..<myString.endIndex
As far as I know, String nor String.CharacterView does not have a concise method returning Range<String.Index> or something comparable to it.
You may need to create a Range explicitly with range operator:
let myStringContainsOnlyOneCharacter = myString.rangeOfComposedCharacterSequence(at: myString.startIndex)
== myString.startIndex..<myString.endIndex
Or compare only upper bound, in your case:
let onlyOne = myString.rangeOfComposedCharacterSequence(at: myString.startIndex).upperBound
== myString.endIndex
Related
I was trying to implement a small iteration which returns the square of some ranges.
Which should be the equivalence of this Python script
for i in range(n):
print(i*i)
In Swift I tried
first attempt
let numbers = [1..<10]
for i in numbers{
print(i*i)
}
and
second attmpt
let numbers = [1..<10]
for i in numbers{
var j: Int = i
print(j*j)
}
but then the compiler says
Cannot convert value of type 'Range<Int>' to specified type 'Int'
I understand from my python experience this is due to different types in Swift. Thus my questions are
How can I fix this? (i.e. implement the same thing i did in python)
What are the problems with my first and second attempts?
Why are there so many types of <Int> in Swift?
Thanks in advance!
Your code doesn't compile because you have used [] around the range, which creates an array. [1..<10] is an array of ranges. The for loop is then iterating over that array, which has only one element - the range 1..<10.
This is why i is of type Range<Int>. It is the range, not the numbers in the range.
Just remove the [] and both of your code would work. You can iterate over ranges directly (in fact, anything that conforms to the Sequence protocol), not just arrays. You can even write the range inline with the loop:
for i in 0..<10 {
print(i * i)
}
Why are there so many types of <Int> in Swift?
You are looking at this the wrong way, the word Range and ClosedRange in the types Range<Int> and ClosedRange<Int> are not words that modify Int, as if they are different "flavours" of Int. It's the opposite - Range<Bound> and ClosedRange<Bound> are generic types, and Range<Int> can be a considered the specific "type" of Range that has Int as its bounds. You can also have Range<Float> or Range<UInt8> for example.
I like to know if I use Set instead of Array can my method of first(where:) became Complexity:O(1)?
Apple says that the first(where:) Method is O(n), is it in general so or it depends on how we use it?
for example look at these two ways of coding:
var numbers: [Int] = [Int]()
numbers = [3, 7, 4, -2, 9, -6, 10, 1]
if let searchResult = numbers.first(where: { value in value == -2 })
{
print("The number \(searchResult) Exist!")
}
else
{
print("The number does not Exist!")
}
and this:
var numbers: Set<Int> = Set<Int>()
numbers = [3, 7, 4, -2, 9, -6, 10, 1]
if let searchResult = numbers.first(where: { value in value == -2 })
{
print("The number \(searchResult) Exist!")
}
else
{
print("The number does not Exist!")
}
can we say that in second way Complexity is O(1)?
It's still O(n) even when you use a Set. .first(where:) is defined on a sequence, and it is necessary to check the items in the sequence one at a time to find the first one that makes the predicate true.
Your example is simply checking if the item exists in the Set, but since you are using .first(where:) and a predicate { value in value == -2} Swift will run that predicate for each element in the sequence in turn until it finds one that returns true. Swift doesn't know that you are really just checking to see if the item is in the set.
If you want O(1), then use .contains(-2) on the Set.
I recommend to learn more about Big-O notation. O(1) is a strict subset of O(n). Thus every function that is O(1) is also in O(n).
That said, Apple’s documentation is actually misleading as it does not take the complexity of the predicate function into account. The following is clearly O(n^2):
numbers.first(where: { value in numbers.contains(value + 42) })
Both Set and Dictionary conform to the Sequence protocol, which is the one that exposes the first(where:) function. And this function has the following requirement, taken from the documentation:
Complexity: O(n), where n is the length of the sequence.
Now, this is the upper limit of the function complexity, it might well be that some sequences optimize the search based on their data type and the storage details.
Bottom line: you need to reach the documentation for a particular type if you want to know more about the performance of some feature, however if you're only circulating some protocol references, then you should assume the "worst" - aka what's in the protocol documentation.
This is the implementation of the first(where:) function in the sequence:
/// - Complexity: O(*n*), where *n* is the length of the sequence.
#inlinable
public func first(
where predicate: (Element) throws -> Bool
) rethrows -> Element? {
for element in self {
if try predicate(element) {
return element
}
}
return nil
}
From the Swift Source Code on the Github
As you can see, It's a simple for loop and the complexity is O(n) (assuming the predicate complexity is 1 🤷🏻♂️).
The predicate executes n times. So the worst case is O(n)
The Set has not an overload for this function (since it is nonsense and there will be nothing more than the first one in a Set). If you know about the sequence and you are just looking for a value (not a predicate), just use contains or firstIndex(of:). These two have overloads with the complexity of O(1)
From the Swift Source Code on the Github
I ran across some funky Range behavior and now I'm questioning everything I thought I knew about Range in Swift.
let range = Range<Int>(start: 0, end: 2)
print(range.count) // Prints 2
Since Range uses a start & end instead of a location & length that NSRange uses I would expect the range above to have a count of 3. It almost seems like it is being treated like an NSRange since a count of 2 makes sense if your location = 0 and length = 2.
let array = ["A", "B", "C", "D"]
let slice = array[range]
I would expect slice to contain ABC since range's end index is 2, but slice actually contains AB, which does correspond to the range.count == 2, but doesn't add up since the range's endIndex == 2 which should include C.
What am I missing here?
I'm using Xcode 7.2's version of Swift, not any of the open source versions.
Range objects Range<T> in Swift are, by default, presented as a half-open interval [start,end), i.e. start..<end (see HalfOpenInterval IntervalType).
You can see this if your print your range variable
let range = Range<Int>(start: 0, end: 2)
print(range) // 0..<2
Also, as Leo Dabus pointed out below (thanks!), you can simplify the declaration of Range<Int> by using the half-open range operator ..< directly:
let range = 0..<2
print(range) // 0..<2 (naturally)
Likewise, you can declare Range<Int> ranges using the closed range operator ...
let range = 0...1
print(range) // 0..<2
And we see, interestingly (in context of the question), that this again verifies that the default representation of Ranges are by means of the half-open operator.
That the half-open interval is default for Range is written somewhat implicitly, in text, in the language reference for range:
Like other collections, a range containing one element has an endIndex
that is the successor of its startIndex; and an empty range has
startIndex == endIndex.
Range conforms, however, to CollectionType protocol. In the language reference to the latter, it's stated clearly that the startIndex and endIndex defines a half-open interval:
The sequence view of the elements is identical to the collection view.
In other words, the following code binds the same series of values to
x as does for x in self {}:
for i in startIndex..<endIndex {
let x = self[i]
}
To wrap it up; Range is defined as half-open interval (startIndex ..< endIndex), even if it's somewhat obscure to find in the docs.
See also
Swift Language Guide - Basic Operators - Range Operators
Swift includes two range operators, which are shortcuts for expressing
a range of values.
...
The closed range operator (a...b) defines a range that runs from a to
b, and includes the values a and b. The value of a must not be greater
than b.
...
The half-open range operator (a..< b) defines a range that runs from a
to b, but does not include b. It is said to be half-open because it
contains its first value, but not its final value.
I'm having an issue trying to access the nth element of a Range using subscripts. The code is super simple:
var range = 0..<9
var itemInRange = range[n] // n is some Int where 0 <= n < 9
The second line complains with the error Ambiguous use of "subscript", which I took to mean that Xcode wasn't clear what the type of the variable range is, and so it's unable to know which implementation of subscript to use. I tried to fix this by explicitly defining the type of range with
var range: Range<Int> = 0..<9
and
var firstInRange = (range as Range<Int>)[0]
but neither of these solved the problem. Is there a way to get Xcode to disambiguate the call to subscript?
You can create an array with the range and then pick an element from the array.
var range = [Int](0..<9)
var itemInRange = range[1]
From apple docs
A collection of consecutive discrete index values.
Like other collections, a range containing one element has an endIndex
that is the successor of its startIndex; and an empty range has
startIndex == endIndex.
Axiom: for any Range r, r[i] == i.
Therefore, if Element has a maximal value, it can serve as an
endIndex, but can never be contained in a Range.
It also follows from the axiom above that (-99..<100)[0] == 0. To
prevent confusion (because some expect the result to be -99), in a
context where Element is known to be an integer type, subscripting
with Element is a compile-time error:
// error: could not find an overload for 'subscript'...
print(Range(start: -99, end: 100)[0])
https://developer.apple.com/library/prerelease/mac/documentation/Swift/Reference/Swift_Range_Structure/index.html
So I have an instance of Range<String.Index> obtained from a search method. And also a standalone String.Index by other means how can I tell wether this index is within the aforementioned range or not?
Example code:
let str = "Hello!"
let range = Range(start: str.startIndex, end: str.endIndex)
let anIndex = advance(str.startIndex, 3)
// How to tell if `anIndex` is within `range` ?
Since comparison operators do not work on String.Index instances, the only way seems to be to perform a loop through the string using advance but this seems overkill for such a simple operation.
The beta 5 release notes mention:
The idea of a Range has been split into three separate concepts:
Ranges, which are Collections of consecutive discrete ForwardIndexType values. Ranges are used for slicing and iteration.
Intervals over Comparable values, which can efficiently check for containment. Intervals are used for pattern matching in switch
statements and by the ~= operator.
Striding over Strideable values, which are Comparable and can be advanced an arbitrary distance in O(1).
Efficient containment checking is what you want, and this is possible since String.Index is Comparable:
let range = str.startIndex..<str.endIndex as HalfOpenInterval
// or this:
let range = HalfOpenInterval(str.startIndex, str.endIndex)
let anIndex = advance(str.startIndex, 3)
range.contains(anIndex) // true
// or this:
range ~= anIndex // true
(For now, it seems that explicitly naming HalfOpenInterval is necessary, otherwise the ..< operator creates a Range by default, and Range doesn't support contains and ~= because it uses only ForwardIndexType.)