I'm so confused as to what's going on but basically, this is my code. Maybe I just am stupid or do not enough above swift or something but I feel like this should take less than a second, but it takes a very long time to iterate through (i added the CFAbsoluteTimeGetCurrent) because I wanted to see how long each assignment took and they take around 0.0008949041366577148 seconds each, but over the span of 30K that adds up obviously. This is just test code but what I'm really trying to do is implement Agglomerative Clustering using a single linkage in Swift, at first I thought that my algorithm was written poorly but then I just tried to iterate over an array like this and it still took a long time. Does anyone know what's up?
I'm also aware that printing out statements in the console takes time but even after removing these statements the onAppear closure still took a while to finish.
Also sorry this is my first time ever posting on Stack Overflow so if please let me know if I should write my posts a certain way in the future.
#State private var mat: [Double?] = Array(repeating: nil, count: 30000)
var body: some View {
Text("HELLo")
.onAppear() {
for i in 0..<matrix.count {
let start = CFAbsoluteTimeGetCurrent()
mat[i] = 0
let diff = CFAbsoluteTimeGetCurrent() - start
print("HAC_SINGLELINK.init TIME: \(diff), ROW \(i) of \(matrix.count)")
}
}
}
I believe the time is caused by the number of modifications you do to your #State variable, which cause a lot of overhead.
With your initial code, on my machine, it took ~16 seconds. With my modified code, which does all of the modifications on a temporary non-state variable and then assigns to #State once, it takes 0.004 seconds:
.onAppear() {
let start = CFAbsoluteTimeGetCurrent()
var temp = matrix
for i in 0..<temp.count {
temp[i] = 0
}
let diff = CFAbsoluteTimeGetCurrent() - start
matrix = temp
print("HAC_SINGLELINK.init TIME: \(diff) \(matrix.count)")
}
Related
I have Swift code which reads a binary file representing a sequence of UInt32 values like this:
let fileData = binaryFile.readData(ofLength: 44)
guard fileData.count > 0 else { break }
let headerData = fileData.withUnsafeBytes {
Array(UnsafeBufferPointer<UInt32>(start: $0, count: 11))
}
let polyCount = headerData[1].bigEndian
let polyFlags = headerData[2].bigEndian
I'd not used the program containing this code for a while, but when returning to it recently, it still works as expected, but now gives a deprecation warning:
"withUnsafeBytes is deprecated: use withUnsafeBytes<R>(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R instead"
I've searched for quite a long time for an un-deprecated way to do this without success. There are many examples across the web (including in stackoverflow) but almost all of them written before this deprecation into effect. Frankly, I've fried my brain hunting and trying suggestions! I am prepared to accept that I'm missing something totally obvious ("If it's getting complicated, you're doing it wrong."), but I work in an environment where I have no colleagues to ask .. except here.
Any guidance would be much appreciated.
RamsayCons's own solution is nice, but what about the performance? I think, it could be better if we reduce all unnecessary operation.
extension Data {
func toArray<T>(type: T.Type) -> [T] where T: ExpressibleByIntegerLiteral {
Array(unsafeUninitializedCapacity: self.count/MemoryLayout<T>.stride) { (buffer, i) in
i = copyBytes(to: buffer) / MemoryLayout<T>.stride
}
}
}
measured performance gain depends, but number of execution per second is at least doubled. Bigger the data, bigger advantage!
self.dataArray = data.withUnsafeBytes{ Array($0.bindMemory(to: UInt32.self))}
Well, letting some time pass restored brain function! I found an answer (in stackoverflow, of course):
round trip Swift number types to/from Data
some way into that question/answer is:
extension Data {
func toArray<T>(type: T.Type) -> [T] where T: ExpressibleByIntegerLiteral {
var array = Array<T>(repeating: 0, count: self.count/MemoryLayout<T>.stride)
_ = array.withUnsafeMutableBytes { copyBytes(to: $0) }
return array
}
}
The trick is that Arrays are not necessarily stored in contiguous memory so simply copying enough bytes in order to the destination doesn't do it. I hope this helps the next person hunting with a fried brain!!
var truthArray = [Bool]()
func isStrictlyIncreasing(sameSequence: [Int]) {
var truthArray = [Bool]()
for i in 0 ... sameSequence.count-1{
if sameSequence[i]<sameSequence[i+1] {
truthArray.append(true)
}
else {
truthArray.append(false)
}
}
}
func almostIncreasingSequence(sequence: [Int]) -> Bool {
for i in 0 ... sequence.count-1 {
var sameSequence = sequence
let number = sequence[i]
sameSequence.remove(at: i)
isStrictlyIncreasing(sameSequence: sameSequence)
sameSequence.insert(number, at: i)
}
if truthArray.contains(true) {
return true
}
else {
return false
}
}
In this certain code challenge on CodeFights, you are asked to determine if removing one of the numbers in an array can be removed to leave a strictly increasing sequence.
The things I have tried to clear this error are switching the index beginning at 1 and readjusting the code that way, reducing and increasing the index bounds by 1, and this is probably my third time rewriting this code. But since my knowledge is strictly limited, I can't get very creative with my solution.
The problem I have right now is an index out of range. I know exactly what this means, except I don't know which line of code is causing the problem and why. I would very much appreciate hints more than a direct solution, as I am a beginner and this is a good learning experience.
Any and all help is appreciated! If I can add to this question with more details don't hesitate to tell me! :)
in if sameSequence[i]<sameSequence[i+1] you will get out of range always
If sameSequence size is 10, the for will be invoked from i=0 to i=9 (10 times). The last iteration will try to access sameSequence[10] and you will have an out of bounds exception.
You can fix it by changing the loop to for i in 0..<sameSequence.count-1 { } but consider that this could have an impact on your algorithm and your problem solution
I'm collecting rows of answers from a database which are made in to arrays. Something like:
for (var i:int = 0; i < event.result.length; i++) {
var data = event.result[i];
var answer:Array = new Array(data["question_id"], data["focus_id"], data["attempts"], data["category"], data["answer"], data["correct"], data["score"]);
trace("answer: " + answer);
restoreAnswer(answer, i);
}
Now, if I trace answer, I typically get something like:
answer: 5,0,2,IK,1.a,3.1,0
answer: 5,0,1,IK,2.a,3.1,0
answer: 4,1,1,AK,3,3,2
From this we see that focus_id 0 (second array item) in question_id 5 (first array item) has been attempted twice (third array item), and I only want to use the last attempt in my restoreAnswer function.
My problem is that first attempt answers override the second attempts since the first are parsed last it seems. How do I go about only calling my restoreAnswer only when appropriate?
The options are:
1 attempts, correct score (2 points)
2 attempts, correct score (1 points)
1 attempt, wrong score (0 points)
2 attemps, wrong score (0 points)
There can be multiple focus_id in each question_id.
Thank you very much! :)
I would consider having the DB query return only the most recent attempt, or if that doesn't work efficiently, return the data in attempt order. You may score question 5 twice, but at least it'll score correctly on the last pass.
You can also filter or sort the data you get back from the server.
Michael Brewer-Davis suggested using the database query to resolve this; normally speaking, this would be the right solution.
If you wait until you get it back from the web method call or whatever in AS3, then consider creating an additional Vector variable:
var vAttempts:Vector.<Vector.<int>> = new Vector.<Vector.<int>>(this.m_iNumQuestions);
You mentioned that it seems that everything is sorted so that earlier attempts come last. First you want to make sure that's true. If so, then before you do any call to restoreAnswer(), you'll want to check vAttempts to make sure that you have not already called restoreAnswer() for that question_id and focus_id:
if (!vAttempts[data["question_id"]])
{
vAttempts[data["question_id"]] = new Vector.<int>(); // ensuring a second dimension
}
if (vAttempts[data["question_id"]].indexOf(data["focus_id"]) == -1)
{
restoreAnswer(answer, i);
vAttempts[data["question_id"]].push(data["focus_id"]);
}
So optimizing this just a little bit, what you'll have is as follows:
private final function resultHandler(event:ResultEvent):void {
var vAttempts:Vector.<Vector.<int>> = new Vector.<Vector.<int>>(this.m_iNumQuestions);
var result:Object = event.result;
var iLength:int = result.length;
for (var i:int = 0; i < iLength; i++) {
var data = result[i];
var iQuestionID:int = data["question_id"];
var iFocusID:int = data["focus_id"];
var answer:Array = [iQuestionID, iFocusID, data["attempts"],
data["category"], data["answer"], data["correct"], data["score"]];
trace("answer: " + answer);
var vFocusIDs:Vector.<int> = vAttempts[iQuestionID];
if (!vFocusIDs) {
vAttempts[iQuestionID] = new <int>[iFocusID];
restoreAnswer(answer, i);
} else if (vFocusIDs.indexOf(iFocusID) == -1) {
restoreAnswer(answer, i);
vFocusIDs.push(iFocusID);
}
}
}
Note: In AS3, Arrays can skip over certain indexes, but Vectors can't. So if your program doesn't already have some sort of foreknowledge as to the number of questions, you'll need to change vAttempts from a Vector to an Array. Also account for whether question IDs are 0-indexed (as this question assumes) or 1-indexed.
I have an movieClip Container and I want to move all its children into an Array.
i think about the method I used to delete all children of a container by using while and removechild at 0, but I think it wont work in this situation. Anyone have a solution?
thanks for your help.
var parObj:DisplayObjectContainer = Container; /* Is that the name of your MC? */
var kids:Array = []
var kidCount:int = parObj.numChildren;
// please see note on push at bottom
for( var i:int = 0; i < kidCount; i++ ) kids.push( parObj.getChildAt( i ) );
TA-DA! kids is now an array of all children of parObj
as a function:
function getChildren( parObj:DisplayObjectContainer ):Array
{
var kids:Array = []
var kidCount:int = parObj.numChildren;
for( var i:int = 0; i < kidCount; i++ )
kids.push( parObj.getChildAt( i ) );
return kids
}
Or, if you're courageous enough for vectors (the result of this function can only hold DisplayObjects and it is slightly faster than a normal array):
function getChildren( parObj:DisplayObjectContainer ):Vector.<DisplayObject>
{
var Vector.<DisplayObject> = new Vector.<DisplayObject>();
var kidCount:int = parObj.numChildren;
for( var i:int = 0; i < kidCount; i++ )
kids.push( parObj.getChildAt( i ) );
return kids
}
EDIT
It was pointed out that this loop was not optimizing numChildren. I will agree (within reason -- chances are this will not be a bottleneck), so it is now an int. A good point to that SOer.
But, there have been two comments made vis-a-vis push vs. kids[ kids.length ] = parObj.getChildAt( i );
When it comes to assigning array indexes, my experience is that situations where push vs. arr[ i ] = val truly make that much of a difference, it is likely that something else is going on which should be optimized first -- are you really getting an array of the children of a MovieClip hundreds (thousands?) of times? Then maybe you don't really need an array of DisplayObject, maybe you need to remove the extra loop and handle all of this (more or less) inline. Do you really have a thousand length array? Then your bottleneck is probably going to be related to the fact that you're dealing with a large, unwieldy data structure and not the loop in itself.
I find push to be clear, explicit, and most important, obvious. arr.push takes no time to understand, it is a self-contained statement, whereas array index assignment requires me, at least, to actually look. Further, because push is a method call and arr.length is a property of a dynamic object, push also defends against the possibility of typos: arr[ arr.lengt ] causes no errors! It is valid code! arr.pus(value), on the other hand, causes a TypeError. Don't know about the rest of the world, but I don't have time to deal with obscured typos.
Assigning array indexes directly, to me, is premature optimization. If you feel otherwise, that's fine (and by all means, vote me down, that is the point of the site -- hopefully we'll all agree on an acceptable practice), but push will be my standard.
You can use a combination of numChildren and getChildAt() to loop through your elements within a container. The following code assumes that you're running the code from within the container. If this isn't the case, just prefix those two properties with the name of your container:
var ar:Array = [];
var i:int = 0;
for(i; i<numChildren; i++)
{
var mc:DisplayObject = getChildAt(i);
ar[ar.length] = mc;
}
trace(ar.length);
I had a hard time trying to word my question properly, so i'm sorry if it seems confusing. Also i'm using the flixel library in flash builder. It may not be that important butcause probably anyone that knows a little more than me or even a little AS3 could probably see what i'm doing wrong.
Anyway, what i'm trying to do is basically create 10 instances of this square object I made. all I have to do is pass it an x an y coordinate to place it and it works. so ive tested if i just do:
var testsquare:Bgsq;
testsquare = new Bgsq(0,0);
add(testsquare);
it works fine and adds a square at 0,0 just like i told it to, but i want to add 10 of them then move the next one that's created 25 px to the right (because each square is 25px)
my problem is that I only ever see 1 square, like it's only making 1 instance of it still.
anyone possibly have an idea what I could be doing wrong?
var counter:int = 0;
var bgsqa:Array = new Array;
for (var ibgs:int = 0; ibgs < 10; ibgs++)
{
bgsqa[counter] = new Bgsq(0,0);
bgsqa[counter].x += 25;
add(bgsqa[counter]);
counter++;
}
There's a lot you're doing wrong here.
First off, you're using a pseudo-iterator (counter) to access array elements through a loop instead of, well, using the iterator (ibgs).
Second, I don't see anything in the array (bgsqa) you're iterating through. It's no wonder you're having problems. Here's what you should do.
var bgsqa:Array = [];
for(var i:int=0;i<10;i++)
{
var bgsq:Bgsq = new Bgsq(i * 25, 0);
add(bgsq);
bgsqa.push(bgsq);
}
That should probably do it if your post is accurate.
for (var ibgs:int = 0; ibgs < 10; ibgs++)
{
bgsqa[counter] = new Bgsq(0,0);
bgsqa[counter].x = counter * 25;
add(bgsqa[counter]);
counter++;
}
They start at 0, so applying += is simply adding 25 to 0. This should do the trick.