How would I break my infinite loop in the code below? When I try to simply use "break", I get the warning "break or continue jumps across a function or a class boundary".
while (true)
runCatching {
// do some stuff
}.getOrElse {
when (it) {
is InterruptedException -> {
// do some stuff
TODO: FIGURE OUT HOW TO BREAK OUT OF WHILE LOOP
}
else -> {
// do some other stuff
}
}.also {
// do some stuff
}
You can label loops by naming them followed by an # symbol nameOfLoop# while(true) and then specify the label when you break by adding # and the label break#nameOfLoop;
Related
I have this simple loop and condition, but you see below I can't jump out of loop :
rwloop# for (z in rendered_words.size-1 downTo 0 )
{
var css_=rendered_words[z].node.attr("class")
css_?.let {
if (css_=="classzero") {
break#rwloop
}
}
}
But I receive this error in break#rwloop :
break' or 'continue' jumps across a function or a
class boundary
Drop the let lambda since the #rwloop label is not visible inside it and use this:
rwloop# for (z in rendered_words.size-1 downTo 0 )
{
var css_=rendered_words[z].node.attr("class")
if (css_ != null) {
if (css_=="classzero") {
break#rwloop
}
}
}
https://kotlinlang.org/docs/reference/inline-functions.html#non-local-returns
It states that
break and continue are not yet available in inlined lambdas, but we are planning to support them too.
So, you should
Wait until its support comes
Or use local return statement instead,
How?
The lambda is a function itself, so you can return from it, this (if it is the last thing in the for loop like your case) will make the same effect of continue
rwloop# for(z in rendered_words.size-1 downTo 0 ) {
var css_=rendered_words[z].node.attr("class")
css_?.let {
if (css_=="classzero") {
return#let
}
}
}
Kotlin considers the lambda as a boundary (it is not an inner class because it is inlined), so you can't cross it by break nor continue till now.
Perhaps I have an underlying issue understanding the overall premise, but I am trying to figure out the best approach to do something with an array of items, and end the test early once a certain criteria is found.
For example, I have an array of names;
var names = ["Bob", "Billy", "Sarah", "Brandon", "Brian", "Rick"]
I would like to test each name in the array and see if it exists in a database. To do this, I'm calling another function with a completion handler;
for name in names {
TestName(name) { response in
if response {
// END THE LOOP
} else {
// KEEP GOING
}
}
I've not been able to figure out the // END THE LOOP. For the purposes of this example, I'm only concerned when the response is true the first time (if Billy exists in the array, I have no further interest in testing Sarah, Brandon, Brian, or Rick).
Thank you!
Before you start the loop, set a flag:
var exitEarly = false
for name in names {
Test the flag each time thru the loop:
for name in names {
if exitEarly {
break
}
In the TestName response block, set the flag:
TestName(name) { response in
if response {
exitEarly = true
} else {
// KEEP GOING
}
}
Note, however, that if TestName's block is executed asynchronously, that won't work, because the whole loop precedes the calling of any of the asynchronous blocks (that is the nature of asynchronous-ness).
Your case isn't really what a loop is designed for. It's possible that the loop may finish before the closures within the loop are executed.
Instead, try a recursive function with a completion block:
func databaseHasAName(names: [String], index: Int, completion: (String?) -> ()) {
guard index < names.count else{
completion(nil)
return
}
let name = names[index]
TestName(name) { response in
if response {
completion(name)
} else {
databaseHasName(names, index: index + 1, completion: completion)
}
}
}
This insures that only one call is happening at a time, regardless of the synchronicity of the response block
Add the #noescape attribute to TestName's closure parameter to indicate that the closure does not escape the call.
Use a variable outside the TestName call, set to false, and set it to true inside the loop if you want to stop.
After the TestName call, check the variable to see if you need to break.
or
Change TestName to return a value indicating if it should proceed or not
I had to do the same thing as PEEJWEEJ. My async tasks are web queries and parsing and quite intensive and the for loop always finished before those tasks did so there was no way to stop them.
So I converted it to recursive and set a flag when I wanted it to stop and it works fine now.
// ************************************************************************************************************
// MARK: Recursive function
// I changed the loop to be recursive so I could stop when I need to - i.e. when selecting another race
// I set the stopFetching flag in the seque when changing races and the current set of BG queries stops
// ************************************************************************************************************
func getRaceResultsRecursive(race: Race, bibs: [Bib], index: Int, completion: #escaping (Bool?) -> ())
{
guard index < bibs.count else{
completion(nil)
return
}
var url: URL
self.stopFetching = false
let bibID = bibs[index]
url = self.athleteResultAPI.formatURL(baseURL: (race.baseURL)!,
rd: (race.rdQueryItem)!,
race: (race.raceQueryItem)!,
bibID: "\(bibID.bib!)",
detail: (race.detailQueryItem)!,
fragment: (race.detailQueryItem)!,
webQueryType: (race.webQueryType!))
self.athleteResultAPI.fetchAthleteResults(url: url, webQueryType: race.webQueryType!)
{
(athleteReturnCode) in
switch athleteReturnCode
{
case let .success(athleteResult):
self.athleteResults.append(athleteResult) // Add to collection
self.delegate?.athleteResultsUpdated() // update Delegate to notify of change
case let .failure(error):
print(error)
break
}
if self.stopFetching {
completion(true)
}
else {
self.getRaceResultsRecursive(race: race, bibs: bibs, index: index + 1, completion: completion)
}
}
}
// ************************************************************************************************************
// getRaceResults
// Get all the bibs to track for this race from the iCloudKit database
// ************************************************************************************************************
func getRaceResults(race: Race)
{
// get all the races and bibs an put in the Races Store
raceStore.fetchBibsForRace(race: race, foreignKey: race.recordID)
{
(results, error) in
if let error = error {
print("Error in fetchBibsForRace(): \(error)")
return
}
// clear the store since we are on a new race
self.athleteResults.removeAll()
self.getRaceResultsRecursive(race: race, bibs: race.bibs, index: 0)
{
(stopFetching) in
return
}
}
}
How to I break an outer loop from within an nested structure that responds to the break statement in Swift?
For example:
while someCondition {
if someOtherCondition {
switch (someValue) {
case 0: // do something
case 1: // exit loop
case 2...5: // do something else
default: break
}
} else {
someCondition = false
}
}
The break will only get me out of the switch, and in Swift, it has to be used as empty cases are not allowed. How can I entirely exit the loop from within the switch?
Swift allows for labeled statements. Using a labeled statement, you can specify which which control structure you want to break from no matter how deeply you nest your loops (although, generally, less nesting is better from a readability standpoint). This also works for continue.
Example:
outerLoop: while someCondition {
if someOtherCondition {
switch (someValue) {
case 0: // do something
case 1: break outerLoop // exit loop
case 2...5: // do something else
default: break
}
} else {
someCondition = false
}
}
Label the loop as outerLoop and whenever needed user break Label: i.e. break outerLoop in our case.
outerLoop: for indexValue in 0..<arr.count-1 {
if arr[indexValue] > arr[indexValue+1] {
break outerLoop
}
}
I run all my scripts through a .suite-file roughly in the following form:
_include("variables.sah");
_include("functions.sah");
$a = 0;
while($a<3) {
try {
_navigateTo($loginpage);
login($user, $password);
myFunction();
$a = 3
}
catch (e){
_navigateTo($loginpage);
login($user, $password);
//undo changes made by myFunction()
...
$a++;
if($a<3) {
_log("Try again");
}
else {
_log("Skip to next script");
}
}
}
function myFunction() {
//do this
...
}
Now all this runs perfectly fine, except for one thing: it doesn't repeat when it encounters a missing element which under normal circumstances would abort all scripts. It simply ignores the error and moves on to the next line of the suite. How do I make my script retry up to 2 times before moving on, if I don't know which part (if any) is going to fail and when?
Your code looks fine I guess.
One thing I can think of is that the exception is thrown in the catch block.
I made a simple script which works as intended:
var $errors = 0;
function trySet() {
try {
_setValue(_textbox("does not exist"), "");
} catch ($e) {
$errors++
_alert($errors);
}
}
for (var $i = 0; $i < 3; $i++) {
trySet();
}
Better figure out where exactly your script runs into problems and handle them with separate try-catch blocks accordingly. How you handle the exceptions is up to you but I guess it would be something like:
try {
login()
} catch ($e) {
// login failed, try again
}
try {
myfunction()
catch($e) {
revertMyFunction()
//try again
}
Maybe define your own exceptions to differently react to errors, have a look at this for more info on custom exceptions: Custom Exceptions in JavaScript
Regards
Wormi
How can i give the continue statement to force the next iteration of the outer loop to take place?
for(i=0;i<strlen(name1);i++) // Outer Loop
{
for(j=0;j<strlen(name2);j++) //inner Loop
{
if(name1[i]==name2[j])
{
name1[i]='*';
continue; //If i continue here the inner loops's newxt iteration takes place
}
}
}
for(i=0;i<strlen(name1);i++) // Outer Loop
{
for(j=0;j<strlen(name2);j++) //inner Loop
{
if(name1[i]==name2[j])
{
name1[i]='*';
break; // <-- break out of loop
}
}
}
The answer by acme is perfectly correct for your code. & that should be accepted answer. I just want to address a case, when there is some more code after inner loop.
Original code:
for(i=0;i<strlen(name1);i++) // Outer Loop
{
for(j=0;j<strlen(name2);j++) //inner Loop
{
if(name1[i]==name2[j])
{
name1[i]='*';
continue; //If i continue here the inner loops's newxt iteration takes place
}
}
// <some more code here....>
}
Change it to:
for(i=0;i<strlen(name1);i++) // Outer Loop
{
for(j=0;j<strlen(name2);j++) //inner Loop
{
if(name1[i]==name2[j])
{
name1[i]='*';
break; // come out of inner loop.
}
}
if(j<strlen(name2)) // check if there was a conditional break.
continue; // skip remaining code, after inner for loop.
// <some more code here....>
}
NOTE1: Try to avoid checking of strlen() in for loop, if possible. It gets evaluated every iteration. If the string is not going to change, you can evaluate it once, store in a variable (say len_name1) & use that variable as i<len_name1, instead of function call in loop.
NOTE2: If there are multiple break statements in the inner for loop & you want to differentiate between the 2 break statement, you can set a flag just before breaking & based on its value, instead of using if(j<strlen(name2)).