Let's say I have an array of elements [1, 2, 3] and a delay of 5 seconds.
I want to emit each element of the array with a pause between the current element emited and the next.
Example:
Output:
[00:00] -- 1
[00:05] -- 2
[00:10] -- 3
I've tried to do the following:
import ReactiveSwift
let startTime = DispatchTime.now().uptimeNanoseconds
let arrayProperty = MutableProperty<[Int]>([1, 2, 3])
let arraySignal = arrayProperty.signal
arraySignal
.flatMap { $0 }
.delay(2, on: QueueScheduler.main)
.observeValues { element in
let elapsed = DispatchTime.now().uptimeNanoseconds
print("\((elapsed - startTime) / 1_000_000_000) -- \(element)")
}
But it only delays the emission of the first element and emits the next ones immediately.
I couldn't find the proper operator mix to use, maybe they are missing on ReactiveSwift framework, and I'm a beginner in Reactive programming so implementing my own operator is way too hard yet.
I haven’t had a chance to try this code, but it should be something like this:
SignalProducer([1, 2, 3])
.flatMap(.concat) { n in
return SignalProducer(value: n).delay(2, on: QueueScheduler.main)
}
.startWithValues { element in
let elapsed = DispatchTime.now().uptimeNanoseconds
print("\((elapsed - startTime) / 1_000_000_000) -- \(element)")
}
The key is that you use flatMap to create a new signal producer for each value which you can apply the delay to, and combine them with the .concat flatten strategy.
(Also, note you can use the signal producer initializer that takes a sequence)
Related
Trying to implement an autocorrelation algorithm, meaning, for example:
let exampleData: [Float] = [1, 2, 3, 4, 5]
Trying to find the fastest way to evaluate 1 ^ 2 + 2 ^ 3 + 3 ^ 4 + 4 ^ 5.
Essentially, iterate through the array, and for every element, calculate the result of XOR between it and another element a set distance away.
Trouble is, this also has to be done for many different values of the offset.
Right now I just have a nested for loop, and I don't know how to make it faster...
var data: [Bool]
var result: [Int]
...
for offset in start..<end {
for index in 0..<(end - offset) {
if (data[index] ^ data[index + frequency]) {
result[offset] += 1
}
}
}
Sounds like you might want windows(ofCount:) from swift-algorithms:
https://github.com/apple/swift-algorithms/blob/main/Guides/Windows.md
That will give you a sliding window through any collection, and if your offset is relatively small (or you actually want the whole window, e.g. to do a moving average), that will be great.
The swift-algorithms stuff is nice since it's more optimized than whatever you'll do ad hoc, plus offers lazy eval.
You might also consider aligning and zipping up your sequence and then mapping over that, e.g.:
zip(data, data.dropFirst(offset))
.map { $0 ^ $1 }
...and so on
My actual requirement:
I have a list of custom objects, and I want to iterate it with a delay. I can't use DispatchQueue.main.asyncAfter in my for loop since my iterations create a CoreData object that triggers FetchedResultController and hence updates my TableView. Anyway, so I tried using Rx to iterate my list with a delay of 1 second each. But I am unable to do so.
Question
I want to delay the iteration of each element of the array using RxSwift.
I was able to do it in Java, but couldn't do so in RxSwift.
the .delay() operator didn't help either, it just delayed the whole process.
Any example would help, thus I am not posting any specific code... but this is what I've been trying so far
var array = [1, 2, 3, 4, 5]
Observable.from(array)
.delay(RxTimeInterval(5), scheduler: MainScheduler.instance)
.subscribe { (intValue) in
print("onNext() \(intValue)")
}
Output
onNext() next(1)
onNext() next(2)
onNext() next(3)
onNext() next(4)
onNext() next(5)
onNext() completed
The output gets printed after 5 seconds, not with 5 seconds interval.
I am not getting any integer values, but a next(1).
This is a common confusion. As you have learned delay will be applied to every element equally so all of them are delayed by five seconds, rather than putting a five second delay between each event. (So the first event happens immediately, the second at five seconds, the third at ten, and so on.
Once you realize that what you are really trying to do is put a delay between each event, the solution becomes more clear. Or at least, the reason why just putting a delay on the from operator isn't working should be more clear.
The solution is to use one of the flatMap variants:
let array = [1, 2, 3, 4, 5]
Observable.from(array)
.concatMap { Observable.empty().delay(.seconds(5), scheduler: MainScheduler.instance).startWith($0) }
.subscribe { (intValue) in
print("onNext() \(intValue)")
}
The line Observable.empty().delay(.seconds(5), scheduler: MainScheduler.instance).startWith($0) will immediately emit the value, but then wait five seconds before emitting a completed event.
The concatMap operator calls the closure on each incoming event and concats the resulting Observables together so that following Observables aren't subscribed to, until the previous one completes.
Learn more about this in my article, The Many Faces of FlatMap
An alternative solution based on the comments that Sandeep Bhandari wrote on the question would be to use an interval to constrain when the from operator can emit values. Something like this:
let array = [1, 2, 3, 4, 5]
Observable.zip(Observable.from(array), Observable<Int>.interval(.seconds(5), scheduler: MainScheduler.instance).take(while: { $0 != array.count }))
.map { $0 }
.subscribe { (intValue) in
print("onNext() \(intValue)")
}
I would like to filter a lazy structure and then reduce it using Swift language.
func main() -> () {
let result = (1...)
.lazy
.filter { $0 < 3 }
.reduce(0, {$0 + $1})
return print(
result
)
}
main()
This code compiles; however, the program doesn't execute in a proper way (takes too long). The printed result on the screen should be 3.
Is there a way to accomplish this goal ?
The issue is not that your sequence is lazy, it's that your sequence is infinite. You might be looking for the sequence method. Example:
let s = sequence(first: 0) {
$0 > 3 ? nil : $0 + 1
}
let result = s.reduce(0) { $0 + $1 }
print(result) // 10
s is lazy and is potentially infinite but not actually infinite, because the method that generates the next item in the series does an early exit by returning nil when the sequence goes past 3. This is similar to your filter except that it is not a filter, it's a stopper. You can use any condition you like here to generate the stopper.
To get the program to terminate, you would need to add an upper limit on your original sequence (1...).
As it's written, you have an infinite sequence of numbers, starting at 1. The following operators — the filter, in particular — have no way of "knowing" that they can discard the rest of the sequence once they pass 3. They have to process the entire infinite sequence, filtering out all but the first two elements, before your reduce can produce a final result and you can print it out. (In practice, you'll eventually overflow Int, so the program would terminate then, but that's not really a good thing to rely on.)
If you don't want to change the original (1...), you can approximate the same behavior by swapping out your filter with a prefix. A filter has to look at every element; a prefix can "know" that it stops after a certain number of elements. For example, this runs very quickly and prints out 3:
let result = (1...)
.lazy
.prefix(2)
.reduce(0) {$0 + $1}
I read the famous Why is it faster to process a sorted array than an unsorted array? and I decided to play around and experiment with other languages such as Swift. I was surprised by the run time differences between 2 very similar snippets of code.
In Swift one can access elements in an array either in a direct way or with a subscript while in a for-in loop. For instance this code:
for i in 0..<size {
sum += data[i]
}
Could be written:
for element in data {
sum += element
}
With size the data length and data an array of summable elements.
So, I just implemented in Swift (code bellow) the same algorithm as in the question I mentioned in the first paragraph and what surprised me is that the first method is roughly 5 times faster than the second method.
I don't really know the backstage subscript implementation but I thought that accessing directly the elements in a Swift for-in loop was just syntactic sugar.
Question
My question is what is the difference between the two for-in syntaxes and why it is faster to use subscript?
here is the detail of timers. I'm using Xcode 9.4.1 with Swift 4.1 on an early 2015 MacBook Air with a Commande Line Project.
// Using Direct Element Access
Elapsed Time: 8.506288427
Sum: 1051901000
vs
// Using Subscript
Elapsed Time: 1.483967902
Sum: 1070388000
Bonus question: why the execution is 100 times slower in Swift than in C++ (both executed on the same Mac in a n Xcode project)? For instance 100,000 repetitions in C++ take nearly the same time as 1,000 repetitions in Swift. My first guess is that Swift is a higher level language than C++ and that Swift operates more safety checks for instance.
Here is the Swift code I used, I only modified the second nested loop:
import Foundation
import GameplayKit
let size = 32_768
var data = [Int]()
var sum = 0
var rand = GKRandomDistribution(lowestValue: 0, highestValue: 255)
for _ in 0..<size {
data.append(rand.nextInt())
}
// data.sort()
let start = DispatchTime.now()
for _ in 0..<1_000 {
// Only the following for-in loop changes
for i in 0..<size {
if data[i] <= 128 {
sum += data[i]
}
}
}
let stop = DispatchTime.now()
let nanoTime = stop.uptimeNanoseconds - start.uptimeNanoseconds
let elapsed = Double(nanoTime) / 1_000_000_000
print("Elapsed Time: \(elapsed)")
print("Sum: \(sum)")
The overall performance output highly depends on the optimizations done by the compiler. If you compile your code with optimizations enabled, you will see the difference between both solutions is minimal.
To demonstrate this, I updated your code adding two methods, one with subscripting and the other using for-in.
import Foundation
import GameplayKit
let size = 32_768
var data = [Int]()
var sum = 0
var rand = GKRandomDistribution(lowestValue: 0, highestValue: 255)
for _ in 0..<size {
data.append(rand.nextInt())
}
// data.sort()
func withSubscript() {
let start = DispatchTime.now()
for _ in 0..<1_000 {
for i in 0..<size {
if data[i] <= 128 {
sum += data[i]
}
}
}
let stop = DispatchTime.now()
let elapsed = Double(stop.uptimeNanoseconds - start.uptimeNanoseconds) / 1_000_000_000
print("With subscript:")
print("- Elapsed Time: \(elapsed)")
print("- Sum: \(sum)")
}
func withForIn() {
let start = DispatchTime.now()
for _ in 0..<1_000 {
for element in data {
if element <= 128 {
sum += element
}
}
}
let stop = DispatchTime.now()
let elapsed = Double(stop.uptimeNanoseconds - start.uptimeNanoseconds) / 1_000_000_000
print("With for-in:")
print("- Elapsed Time: \(elapsed)")
print("- Sum: \(sum)")
}
withSubscript()
withForIn()
I saved that code into a file called array-subscripting.swift.
Then, from the command line, we can run it without any optimizations, like this:
$ swift array-subscripting.swift
With subscript:
- Elapsed Time: 0.924554249
- Sum: 1057062000
With for-in:
- Elapsed Time: 5.796038213
- Sum: 2114124000
As you mentioned in the post, there is a big difference in performance.
This difference is pretty negligible when the code is compiled with optimizations:
$ swiftc array-subscripting.swift -O
$ ./array-subscripting
With subscript:
- Elapsed Time: 0.110622556
- Sum: 1054578000
With for-in:
- Elapsed Time: 0.11670454
- Sum: 2109156000
As you can see, both solutions are way faster than before, and very similar on time execution.
Back to your original question, subscripting provides direct access to memory, which is pretty efficient in the case of contiguous arrays, where elements are stored next to each other in memory.
for-in loops, in the other hand, create an immutable copy of each element from the array, which incurs in a performance hit.
What I want to do is populate an Array (sequence) by appending in the elements of another Array (availableExercises), one by one. I want to do it one by one because the sequence has to hold a given number of items. The available exercises list is in nature finite, and I want to use its elements as many times as I want, as opposed to a multiple number of the available list total.
The current code included does exactly that and works. It is possible to just paste that in a Playground to see it at work.
My question is: Is there a better Swift3 way to achieve the same result? Although the code works, I'd like to not need the variable i. Swift3 allows for structured code like closures and I'm failing to see how I could use them better. It seems to me there would be a better structure for this which is just out of reach at the moment.
Here's the code:
import UIKit
let repTime = 20 //seconds
let restTime = 10 //seconds
let woDuration = 3 //minutes
let totalWOTime = woDuration * 60
let sessionTime = repTime + restTime
let totalSessions = totalWOTime / sessionTime
let availableExercises = ["push up","deep squat","burpee","HHSA plank"]
var sequence = [String]()
var i = 0
while sequence.count < totalSessions {
if i < availableExercises.count {
sequence.append(availableExercises[i])
i += 1
}
else { i = 0 }
}
sequence
You can overcome from i using modulo of sequence.count % availableExercises.count like this way.
var sequence = [String]()
while(sequence.count < totalSessions) {
let currentIndex = sequence.count % availableExercises.count
sequence.append(availableExercises[currentIndex])
}
print(sequence)
//["push up", "deep squat", "burpee", "HHSA plank", "push up", "deep squat"]
You can condense your logic by using map(_:) and the remainder operator %:
let sequence = (0..<totalSessions).map {
availableExercises[$0 % availableExercises.count]
}
map(_:) will iterate from 0 up to (but not including) totalSessions, and for each index, the corresponding element in availableExercises will be used in the result, with the remainder operator allowing you to 'wrap around' once you reach the end of availableExercises.
This also has the advantage of preallocating the resultant array (which map(_:) will do for you), preventing it from being needlessly re-allocated upon appending.
Personally, Nirav's solution is probably the best, but I can't help offering this solution, particularly because it demonstrates (pseudo-)infinite lazy sequences in Swift:
Array(
repeatElement(availableExercises, count: .max)
.joined()
.prefix(totalSessions))
If you just want to iterate over this, you of course don't need the Array(), you can leave the whole thing lazy. Wrapping it up in Array() just forces it to evaluate immediately ("strictly") and avoids the crazy BidirectionalSlice<FlattenBidirectionalCollection<Repeated<Array<String>>>> type.