Stridable Protocol

How can I conform to the Strideable protocol in Swift?

Here's a definition of the protocol: Stridable

You can implement it like this:

final class Foo: Strideable {
var value: Int = 0
init(_ newValue: Int) { value = newValue }
func distanceTo(other: Foo) -> Int { return other.value - value }
func advancedBy(n: Int) -> Self { return self.dynamicType(value + n) }
}

func ==(x: Foo, y: Foo) -> Bool { return x.value == y.value }
func <(x: Foo, y: Foo) -> Bool { return x.value < y.value }

let a = Foo(10)
let b = Foo(20)

for c in stride(from: a, to: b, by: 1) {
println(c.value)
}

You need to provide the functions distanceTo, advancedBy and the operators == and <. There is more information about these functions in the documentation I linked.

Why does conforming to Strideable change how multiplication is evaluated?

It's from apple documents:

Important
The Strideable protocol provides default implementations for the equal-to (==) and less-than (<) operators that depend on the Stride type’s implementations. If a type conforming to Strideable is its own Stride type, it must provide concrete implementations of the two operators to avoid infinite recursion.

So either you provide the "==" and "<" implementation like this :

 struct Temp: Strideable {
var error = 1
func advanced(by n: Int) -> Temp { return Temp() }
func distance(to other: Temp) -> Int {
print("hello")
return self == other ? 0 : 1
}
static func == (left : T, right: T){
print("great")
}
}

or use some value to manager the stride property. like var location : Int = 0

Stridable Protocol

In Swift 3, "Collections move their index", compare
A New Model for Collections and Indices on Swift evolution. In particular, you don't call advancedBy() on an index,
but an index() method on the collection to advance indices.

So your method would be implemented in Swift 3 as

extension RandomAccessCollection {

func binarySearch(predicate: (Iterator.Element) -> Bool) -> Index {
var low = startIndex
var high = endIndex
while low != high {
let mid = index(low, offsetBy: distance(from: low, to: high)/2)
if predicate(self[mid]) {
low = index(after: mid)
} else {
high = mid
}
}
return low
}
}

The same method would also compile (and work) as an extension of the
more general Collection type, but – as Vadim Yelagin correctly remarked,
is very inefficient if the index/offset calculations cannot be done in
constant time.

How to fix the Strideable protocol error?

I suppose you want to get the substring from the end of the found range to the end of the string.

In Swift 4 you can use

let t = myHTMLString
if let roz = t.range(of: "[\"fmt_stream_map\"", options: .backwards) {
first = String(t[roz.upperBound...])
}

After updating my XCode, I get a Strideable error

Ok, I am not sure why, but apparently at some point there was an update for Strideable, so the only thing I had to do was delete my Strideable extension.

Once I did that, the app worked without any issues.

protocol witness for Strideable.distance(to : A) - A.Stride in conformance Int64 + 124 - what does this error message mean?

The mixed-type comparison can be
the cause for the problem. The subtraction operator is inferred from
the types of its operands as

@available(swift, deprecated: 3.0, obsoleted: 4.0, message: "Mixed-type subtraction is deprecated. Please use explicit type conversion.")
public func -<T>(lhs: T, rhs: T) -> T.Stride where T : SignedInteger

with T == Int64 and T.Stride == Int. Your code would cause a warning message with Xcode 8.3.2:

let THRESHOLD = 900 * 1000

let var1: Int64 = 0x100000000
let var2: Int64 = 0

// warning: '-' is deprecated: Mixed-type subtraction is deprecated. Please use explicit type conversion.
if var1 - var2 > THRESHOLD {
print("foo")
} else {
print("bar")
}

On a 32-bit device the difference can be too large for an Int
and the above example would abort with a runtime error


* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
frame #0: 0x0223d428 libswiftCore.dylib`protocol witness for Swift.Strideable.distance (to : A) -> A.Stride in conformance Swift.Int64 : Swift.Strideable in Swift + 72

The solution is to explicitly convert the right-hand side:

if var1 - var2 > Int64(THRESHOLD) { ... }

Conforming String.CharacterView.Index to Strideable: fatal error when using stride(to:by:): cannot increment endIndex

Simply declaring the protocol conformance

extension String.CharacterView.Index : Strideable { }

compiles because String.CharacterView.Index conforms to
BidirectionalIndexType , and ForwardIndexType/BidirectionalIndexType have default method implementations for advancedBy() and distanceTo()
as required by Strideable.

Strideable has the default protocol method implementation
for stride():

extension Strideable {
// ...
public func stride(to end: Self, by stride: Self.Stride) -> StrideTo<Self>
}

So the only methods which are "directly" implemented for
String.CharacterView.Index are – as far as I can see - the successor() and predecessor() methods from BidirectionalIndexType.

As you already figured out, the default method implementation of
stride() does not work well with String.CharacterView.Index.

But is is always possible to define dedicated methods for a concrete type. For the problems of making String.CharacterView.Index conform to Strideable see
Vatsal Manot's answer below and the discussion in the comments – it took me a while to get what he meant :)

Here is a possible implementation of a stride(to:by:) method for String.CharacterView.Index:

extension String.CharacterView.Index {
typealias Index = String.CharacterView.Index

func stride(to end: Index, by stride: Int) -> AnySequence<Index> {

precondition(stride != 0, "stride size must not be zero")

return AnySequence { () -> AnyGenerator<Index> in
var current = self
return AnyGenerator {
if stride > 0 ? current >= end : current <= end {
return nil
}
defer {
current = current.advancedBy(stride, limit: end)
}
return current
}
}
}
}

This seems to work as expected:

let str = "01234"
str.startIndex.stride(to: str.endIndex, by: 2).forEach {
print($0,str.characters[$0])
}

Output

0 0
2 2
4 4

swift breaks into infinite loop after conforming to Stridable

From the documentation of the Strideable protocol:

Important

The Strideable protocol provides default implementations for the equal-to (==) and less-than (<) operators that depend on the Stride type’s implementations. If a type conforming to Strideable is its own Stride type, it must provide concrete implementations of the two operators to avoid infinite recursion.

In your case Rank is not it's own Stride type, but the distance(to:) method calls firstIndex(of:), which calls == on the elements. Now == has a special implementation for Strideable types which – as we can see from the implementation in Stride.swift– calls distance(to:). That leads to an “infinite” recursion, and ultimately to an stack overflow. You can also see that from the stack backtrace:

(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0x7ffeef3fffb8)
* frame #0: 0x0000000100001b4b test`Rank.distance(other=ace, self=king) at main.swift:11
frame #1: 0x0000000100001fd8 test`protocol witness for Strideable.distance(to:) in conformance Rank at <compiler-generated>:0
frame #2: 0x00007fff7ac4927b libswiftCore.dylib`static (extension in Swift):Swift.Strideable.== infix(A, A) -> Swift.Bool + 219
frame #3: 0x0000000100001f56 test`protocol witness for static Equatable.== infix(_:_:) in conformance Rank at <compiler-generated>:0
frame #4: 0x00007fff7ab25ea9 libswiftCore.dylib`(extension in Swift):Swift.Collection< where A.Element: Swift.Equatable>.firstIndex(of: A.Element) -> Swift.Optional<A.Index> + 905
frame #5: 0x0000000100001c31 test`Rank.distance(other=ace, self=ace) at main.swift:12:49
...
frame #42256: 0x0000000100001fd8 test`protocol witness for Strideable.distance(to:) in conformance Rank at <compiler-generated>:0
frame #42257: 0x00007fff7ac4927b libswiftCore.dylib`static (extension in Swift):Swift.Strideable.== infix(A, A) -> Swift.Bool + 219
frame #42258: 0x0000000100001f56 test`protocol witness for static Equatable.== infix(_:_:) in conformance Rank at <compiler-generated>:0
frame #42259: 0x00007fff7ab25ea9 libswiftCore.dylib`(extension in Swift):Swift.Collection< where A.Element: Swift.Equatable>.firstIndex(of: A.Element) -> Swift.Optional<A.Index> + 905
frame #42260: 0x0000000100001c31 test`Rank.distance(other=ace, self=ace) at main.swift:12:49
frame #42261: 0x0000000100001fd8 test`protocol witness for Strideable.distance(to:) in conformance Rank at <compiler-generated>:0
frame #42262: 0x00007fff7ac4927b libswiftCore.dylib`static (extension in Swift):Swift.Strideable.== infix(A, A) -> Swift.Bool + 219
frame #42263: 0x0000000100001f56 test`protocol witness for static Equatable.== infix(_:_:) in conformance Rank at <compiler-generated>:0
frame #42264: 0x00007fff7ab25ea9 libswiftCore.dylib`(extension in Swift):Swift.Collection< where A.Element: Swift.Equatable>.firstIndex(of: A.Element) -> Swift.Optional<A.Index> + 905
frame #42265: 0x0000000100001c31 test`Rank.distance(other=queen, self=ace) at main.swift:12:49
frame #42266: 0x0000000100001830 test`main at main.swift:23:18
frame #42267: 0x00007fff7b3843d5 libdyld.dylib`start + 1
(lldb)

As Joakim said, the easiest solution is to implement those methods based on the enums's raw values, this avoids the (recursive) use of ==:

enum Rank: Int, Strideable {
case ace, king, queen, jack

func advanced(by n: Int) -> Rank {
return Self(rawValue: self.rawValue + n)!
}

func distance(to other: Rank) -> Int {
return other.rawValue - self.rawValue
}
}

This is also more efficient than looking up the values in the allCases collection again and again.

How can I iterate day by day from an StartDate to an EndDate using strideable on Swift?

Your code doesn't compile because you're striding by integers, but you've defined your Stride to be TimeInterval. It would do most of what you expect if you fix the type of dayDurationInSeconds:

let dayDurationInSeconds: TimeInterval = 60*60*24

But this will give you completely incorrect results, because not every day is 86,400 seconds long. DST alters the day length twice a year in location-dependent ways.

The correct way to perform date math is with Calendar. If you're not using Calendar, you can't use anything that isn't explicitly in seconds.

What you may want is something more like this, which enumerates all the midnights (UTC) between the dates in question. Remember, a Date in Foundation is not a "date." It's a precise instant in time. If you really do want to work with "dates", what you want are DateComponents.

let formatter = DateFormatter()
formatter.dateFormat = "yyyy/MM/dd"
let startDate = formatter.date(from: "2014/01/01")!
let endDate = formatter.date(from: "2019/03/31")!

var calendar = Calendar(identifier: .gregorian)
calendar.timeZone = TimeZone(identifier: "UTC")!

calendar.enumerateDates(startingAfter: startDate,
matching: DateComponents(hour: 0, minute: 0, second:0),
matchingPolicy: .nextTime) { (date, _, stop) in
guard let date = date, date < endDate else {
stop = true
return
}

print(date)
}


Related Topics



Leave a reply



Submit