Swift range bug with switch-statement - swift

Hello guys I'm new here and right now I'm learning Swift by coding some fancy algorithms, which comes to my mind while reading Apples Swift book.
I was trying to compress (automatically downcast) any IntegerType value.
Here is a little snippet of my Code which almost works fine except for one case:
switch signedValue
{
case Int64(Int8.min)...Int64(Int8.max): compressedValue = Int8(signedValue)
case (Int64(Int8.max) + 1)...Int64(UInt8.max): compressedValue = UInt8(signedValue)
case Int64(Int16.min)...Int64(Int16.max): compressedValue = Int16(signedValue)
case (Int64(Int16.max) + 1)...Int64(UInt16.max): compressedValue = UInt16(signedValue)
case Int64(Int32.min)...Int64(Int32.max): compressedValue = Int32(signedValue)
case (Int64(Int32.max) + 1)...Int64(UInt32.max): compressedValue = UInt32(signedValue)
case Int64(Int.min)...Int64(Int.max): compressedValue = Int(signedValue) // range bug #1 - workaround '..<'
default: compressedValue = signedValue
}
Assume signedValue is of Type Int64 and the input value is = 10_000_000_000.
This will lead to a runtime error (in Playground):
Execution was interrupted, reason: EXC_BAD_INSTRUCTION ...
Could anyone help me out to understand what exactly is happening here. There is an workaround for this problem. Instead of '...' I could use '..<' but this not how the range should be.

Unlike intervals (which have two flavours, half-open or closed), Range is only ever half-open. So when you write 1...5, the ...function increments the right-hand argument in order to create a half-open range 1..<6.
The consequence of this is you can’t have a range that spans the entire length of an integer type, since 1...Int.max will result in an attempt to increment Int.max, and an overflow error. You get a similar error with let s = "hello"; let r = s.startIndex...s.endIndex
All is not lost, however, since you shouldn’t be using ranges anyway, this is a job for ClosedInterval. Since the ... operator is used for both, and defaults to ranges (for overloading precedence reasons) you need to explicitly identify the type:
let i64interval: ClosedInterval = Int64(Int64.min)...Int64(Int64.max)
switch signedValue {
// etc
case i64interval: compressedValue = Int(signedValue)
default: compressedValue = signedValue
}

Look at the documentation for the Range Type. It has the following little nugget of information that I never noticed before:
if T has a maximal value, it can serve as an endIndex, but can never be contained in a Range.
T, in that quote, is the type of value in the Range. So if you have a Range of Ints, the max value of the Int cannot be contained in the Range.
That's why it works if you put the ..< in there, instead of ....
Good question, by the way...

Related

Swift Dictionary is slow?

Situation: I was solving LeetCode 3. Longest Substring Without Repeating Characters, when I use the Dictionary using Swift the result is Time Limit Exceeded that failed to last test case, but using the same notion of code with C++ it acctually passed with runtime just fine. I thought in swift Dictionary is same thing as UnorderdMap.
Some research: I found some resources said use NSDictionary over regular one but it requires reference type instead of Int or Character etc.
Expected result: fast performance in accessing Dictionary in Swift
Question: I know there are better answer for the question, but the main goal here is Is there a effiencient to access and write to Dictionary or someting we can use to substitude.
func lengthOfLongestSubstring(_ s: String) -> Int {
var window:[Character:Int] = [:] //swift dictionary is kind of slow?
let array = Array(s)
var res = 0
var left = 0, right = 0
while right < s.count {
let rightChar = array[right]
right += 1
window[rightChar, default: 0] += 1
while window[rightChar]! > 1 {
let leftChar = array[left]
window[leftChar, default: 0] -= 1
left += 1
}
res = max(res, right - left)
}
return res
}
Because complexity of count in String is O(n), so that you should save count in a variable. You can read at chapter
Strings and Characters in Swift Book
Extended grapheme clusters can be composed of multiple Unicode scalars. This means that different characters—and different representations of the same character—can require different amounts of memory to store. Because of this, characters in Swift don’t each take up the same amount of memory within a string’s representation. As a result, the number of characters in a string can’t be calculated without iterating through the string to determine its extended grapheme cluster boundaries. If you are working with particularly long string values, be aware that the count property must iterate over the Unicode scalars in the entire string in order to determine the characters for that string.
The count of the characters returned by the count property isn’t always the same as the length property of an NSString that contains the same characters. The length of an NSString is based on the number of 16-bit code units within the string’s UTF-16 representation and not the number of Unicode extended grapheme clusters within the string.

Why does assignment return the old value instead of the new one?

Why does assignment return the previous value instead of the new value, re example: assigns 0 to y instead of 2? This is dangerously unusual as it violates the principle of least surprise.
var x: I32 = 0
let y = (x = 2)
I am pretty sure this was done to get consistent results for iso variables. Assignment to an iso variable would not be able to return the new value because that created an alias. But it's true that a less surprising design would involve an assignment operator returning None and some other operation (swap?) for the recovery of the original value that is being overwritten.

If given a Substring, is it possible to access the underlying complete String on which it is based?

Say I have the following code...
let x = "ABCDE"
// 'x' is a String
var y = x[1...3]
// 'y' is a Substring that equals "BCD"
If you only have access to y, is it possible to access x, or specifically parts of x which are outside the range of y? (i.e. can you access 'A' or 'E', or grow the range of y?)
So here's what Apple says:
Important
Don’t store substrings longer than you need them to perform a specific
operation. A substring holds a reference to the entire storage of the
string it comes from, not just to the portion it presents, even when
there is no other reference to the original string. Storing substrings
may, therefore, prolong the lifetime of string data that is no longer
otherwise accessible, which can appear to be memory leakage.
Now I find their use of the word "otherwise" in the last sentence rather interesting. It seems to me to keep the door open on this question - could a substring be manipulated to be expanded to include memory on either side that we know still exists as part of the original string?
So here's what I'd think is a fair test:
let x = "ABCDEFGH"
let substr = x.prefix(3)
var substrIndex = substr.startIndex
substr.formIndex(&substrIndex, offsetBy: 4) // offset beyond the substring
let prefix = substr.prefix(through:substrIndex)
print(prefix)
So what'cha think that would print?
Actually we never get to the print. We get a runtime fatal error instead.
Thread 1: Fatal error: Operation results in an invalid index
BTW, even trying the following results in an EXC_BAD_ACCESS crash:
let x = "ABCDEFGH"
var substr = x.prefix(3)
withUnsafePointer(to: &substr)
{ substrPointer in
let z = substrPointer.advanced(by: 3)
print(z.pointee)
}
So I don't think there's a way to get to the rest of the string if you just have a substring... from within Substring or String classes anyhow, or even dealing with unsafe pointers. I'm sure there's a way using direct memory access, for Apple claims the rest of the String's memory is there... but you'd probably have to fall back to C or C++.

Swift 3 subscript range works for first cluster but not for middle

I'm trying to figure out why the following works on the first string cluster (character) but not on a second one. Perhaps the endIndex cannot be applied on another String?
let part = "A"
let full = "ABC"
print(full[part.startIndex ... part.startIndex]) // "A"
print(full[part.endIndex ... part.endIndex]) // "" <- ???
print(full[part.endIndex ... full.index(after: part.endIndex)]) // "B"
bSecond should hold "B", but instead is empty. But the proof that one string index works on another is that the last statement works.
EDIT:
Assuming full.hasPrefix(part) is true.
Swift puzzles.
You cannot use the indices of one string to subscript a different
string. That may work by chance (in your first example) or not
(in your second example), or crash at runtime.
In this particular case, part.endIndex (which is the "one past the end position" for the part string) returns
String.UnicodeScalarView.Index(_position: 1), _countUTF16: 0)
with _countUTF16: (which is the "count of this extended grapheme cluster in UTF-16 code units") being zero, i.e. it describes
a position (in the unicode scalar view) with no extent. Then
full[part.endIndex ... part.endIndex]
returns an empty string. But that is an implementation detail
(compare StringCharacterView.swift). The real answer is just "you can't do that".
A safe way to obtain the intended (?) result is
let part = "A"
let full = "ABC"
if let range = full.range(of: part) {
print(full[range]) // A
if range.upperBound != full.endIndex {
print(full[range.upperBound...range.upperBound]) // B
}
}

How to compare Range<String.Index> and DefaultBidirectionalIndices<String.CharacterView>?

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