Consider the following dart code:
void main(List<String> args) {
firstLoop:
for (var i = 0; i < 2; i++) {
print('${i}::I am in the first loop');
secondLoop:
for (var j = 0; j < 2; j++) {
print('${j}::I am in the second loop.');
thirdLoop:
for (var k = 0; k < 2; k++) {
print('${k}::I am in the third loop');
fourthLoop:
for (var l = 0; l < 2; l++) {
print('${l}::I am in the fourth loop');
break secondLoop;
}
}
}
}
}
The output was:
0::I am in the first loop
0::I am in the second loop.
0::I am in the third loop***
0::I am in the fourth loop
1::I am in the first loop
0::I am in the second loop.
0::I am in the third loop***
0::I am in the fourth loop
And for the following code:
void main(List<String> args) {
firstLoop:
for (var i = 0; i < 2; i++) {
print('${i}::I am in the first loop');
secondLoop:
for (var j = 0; j < 2; j++) {
print('${j}::I am in the second loop.');
thirdLoop:
for (var k = 0; k < 2; k++) {
print('${k}::I am in the third loop***');
fourthLoop:
for (var l = 0; l < 2; l++) {
print('${l}::I am in the fourth loop');
break thirdLoop;
}
}
}
}
}
The output was:
0::I am in the first loop
0::I am in the second loop.
0::I am in the third loop***
0::I am in the fourth loop
1::I am in the second loop.
0::I am in the third loop***
0::I am in the fourth loop
1::I am in the first loop
0::I am in the second loop.
0::I am in the third loop***
0::I am in the fourth loop
1::I am in the second loop.
0::I am in the third loop***
0::I am in the fourth loop
I can't really understand this flow.
The behaviour is like:
when I jump to a certain label, the statement or block above that label is executed?
It doesn't look like my understanding is clear. Please give me more insight/clearance to this.
When you break a labeled statement, whether it's a loop or not, it behaves as if that statement had just completed normally (not throwing, not returning, not any other control flow).
A labeled statement is something of the form label: statement. The statement doesn't have to be a loop, but it usually is.
So, if you write:
print("outside before");
label: {
print("inside before");
break label; // Breaks to *end of labeled statement*.
print("inside after");
} // End of labeled statement is here.
print("outside after");
it will print:
outside before
inside before
outside after
You can break any statement, as long as you can refer to it.
A break with no label automatically refers to the closest enclosing loop or switch statement. If you want to break to break any other statement, you need to give it a name with a label first, so you can do break label; to designate the statement to break.
(You can also have labels on the cases of a switch. You cannot break those because they are not statements, you have to break the switch instead.)
In your example, you are breaking a loop which is also the last statement of an outer loop. When you break the inner loop, you reach the end of the body of the outer loop, and it goes on to the next iteration (if it has one). That's why it looks like it is going backwards, it's hitting the end of the loop and looping backwards, just as loops normally do.
So, break does not jump to a label, it exits the labeled statement.
If you want to continue with that label, you may want to use continue label; instead. Continuing a loop means going back to the loop header and (maybe) take another iteration of the loop. It works precisely as breaking out of the body of the loop. You end the body, then the normal loop behavior takes over.
Example:
for (var i = 0; i < 5; i++) {
print("before: $i");
if (i.isEven) continue;
print("after: $i");
}
This prints:
before: 0
before: 1
after: 1
before: 2
before: 3
after: 3
before: 4
It works with all kinds of loops, even do-while:
var i = 0;
do {
print("before: $i");
if (i.isEven) continue;
print("after: $i");
} while (++i < 5);
prints the same thing.
Related
I'm currently using this formula to highlight duplicates in my spreadsheet.
=ARRAYFORMULA(COUNTIF(A$2:$A2,$A2)>1)
Quite simple, it allows me to skip the first occurrence and only highlight 2nd, 3rd, ... occurrences.
I would like the formula to go a bit further and highlight near duplicates as well.
Meaning if there is only one character difference between 2 cells, then it should be considered as a duplicate.
For instance: "Marketing", "Marketng", "Marketingg" and "Market ing" would all be considered the same.
I've made a sample sheet in case my requirement is not straightforward to understand.
Thanks in advance.
Answer
Unfortunately, it is not possible to do this only through Formulas. Apps Scripts are need as well. The process for achieving your desired results is described below.
In Google Sheets, go to Extensions > Apps Script, paste the following code1 and save.
function TypoFinder(range, word) { // created by https://stackoverflow.com/users/19361936
if (!Array.isArray(range) || word == "") {
return false;
}
distances = range.map(row => row.map(cell => Levenshtein(cell, word))) // Iterate over range and check Levenshtein distance.
var accumulator = 0;
for (var i = 0; i < distances.length; i++) {
if (distances[i] < 2) {
accumulator++
} // Keep track of how many times there's a Levenshtein distance of 0 or 1.
}
return accumulator > 1;
}
function Levenshtein(a, b) { // created by https://stackoverflow.com/users/4269081
if (a.length == 0) return b.length;
if (b.length == 0) return a.length;
// swap to save some memory O(min(a,b)) instead of O(a)
if (a.length > b.length) {
var tmp = a;
a = b;
b = tmp;
}
var row = [];
// init the row
for (var i = 0; i <= a.length; i++) {
row[i] = i;
}
// fill in the rest
for (var i = 0; i < b.length; i++) {
var prev = i;
for (var j = 0; j < a.length; j++) {
var val;
if (b.charAt(i) == a.charAt(j)) {
val = row[j]; // match
} else {
val = Math.min(row[j] + 1, // substitution
prev + 1, // insertion
row[j + 1] + 1); // deletion
}
row[j] = prev;
prev = val;
}
row[a.length] = prev;
}
return row[a.length];
}
In cell B1, enter =TypoFinder($A$2:$A2,$A2). Autofill that formula down the column by draggin.
Create a conditional formatting rule for column A. Using Format Rules > Custom Formula, enter =B2:B.
At this point, you might wish to hide column B. To do so, right click on the column and press Hide Column.
The above explanation assumes the column you wish to highlight is Column A and the helper column is column B. Adjust appropriately.
Note that I have assumed you do not wish to highlight repeated blank columns as duplicate. If I am incorrect, remove || word == "" from line 2 of the provided snippet.
Explanation
The concept you have described is called Levenshtein Distance, which is a measure of how close together two strings are. There is no built-in way for Google Sheets to process this, so the Levenshtein() portion of the snippet above implements a custom function to do so instead. Then the TypoFinder() function is built on top of it, providing a method for evaluating a range of data against a specified "correct" word (looking for typos anywhere in the range).
Next, a helper column is used because Sheets has difficulties parsing custom formulas as part of a conditional formatting rule. Finally, the rule itself is implemented to check the helper column's determination of whether the row should be highlighted or not. Altogether, this highlights near-duplicate results in a specified column.
1 Adapted from duality's answer to a related question.
How I can remove previously printed line in console with print() in Dart?
I am willing to display a progress indicator such as:
int n = 0;
for (var item in list) {
n++;
print('${(n / list.length * 100).toStringAsFixed(0)}%');
// do something
}
Currently it prints:
1%
2%
3%
...
But I would like to delete the previous line and replace it with the newly calculated percent.
print always adds a newline, but you can write directly to stdout to avoid that.
To overwrite the previous line, you can place a carriage return ('\r') character at the start of your string, which tells the cursor to return to the beginning of the line. You may also want to pad the end of your string with some spaces to overwrite any text which might still be remaining from the previous write, although that's not necessary in this case.
It may look something like this in the end:
// At top of file
import 'dart:io';
int n = 0;
for (var item in list) {
n++;
stdout.write('\r${(n / list.length * 100).toStringAsFixed(0)}%');
// do something
}
You can use dart:io and a carriage return (\r):
import "dart:io";
void main() {
for (int i = 0; i < 5; i++) {
stdout.write("\r $i");
}
}
I have a List of the type Model. when I loop all its elements and loop the next one except for the last one, then change the last one manually, the one before changes.
here is a little code to reproduce the problem (also you can run it directly in dartpad from here)
void main() {
List<Model> s = [];
for (int i = 0; i < 5; i++) {
s.add(Model(i));
}
for (int i = 0; i < s.length - 1; i++) {
s[i] = s[i + 1];
}
print(s);
s[s.length-1].x = 100;
print(s);
}
class Model {
int x;
Model(this.x);
#override
String toString() => 'x: ' + this.x.toString();
}
notice that this problem does not happen when you comment out the for loop or the manual change, or instead of changing the last one's property, you reassign a new value to it, like s[s.length - 1] = Model(100);. seems like dart for some reason is re-running the loop.
When you run the second for loop, you assign the i + 1th Model to the ith position in the list:
// initialise list
for (int i = 0; i < s.length; i++) {
s[i] = s[i + 1]
}
If you unwrap the loop, it looks roughly like this:
s[0] = s[1];
s[1] = s[2];
s[2] = s[3];
s[3] = s[4];
Notice that this leaves s[4] unchanged, but also assigns it to s[3].
In Dart, variables contain references to objects. This means that when your list runs s[3] = s[4];, both s[3] and s[4] point to the same object.
Now, if you modify s[4] you, are actually modifying the objects that s[4] refers to, which happens to also be the object that s[3] refers to, so they both appear to change in your list.
I have a small dxl script and I need to return the number of assigned positions from an array of, let's say, size 20 in which only 10 positions are assigned.
I tried to use noError() and lastError() functions, but after lastError() is called, the script is halted and I can't continue the execution.
Here's my code:
int returnArrayLength(string array[]){
int lengthOfArray = 0,i = 0;
for (i=0; i < sizeof array ; i++){
noError()
if (!null array[i]){
lengthOfArray++
print lengthOfArray
}
if (!null lastError()){
print "Exception caught!" // not printed
break
}
}
return lengthOfArray
}
string labels[6]
labels[0] = "label0"
labels[1] = "label1"
labels[2] = "label2"
labels[3] = "label3"
print returnArrayLength(labels) // not printed
The above code prints the following:
1
2
3
4
How can I resume the execution after the lastError() function is called ?
This was tougher than I thought it would be!
So, as it turns out, an unassigned element error halts the DXL program entirely. So what do we need to do?
Well, we need to run a snippet of code, in it's own environment, and let it crash if it needs to!
To do so, we need to create an eval_, pass it our array, and then have it return_ (which won't be executed if the eval_ fails)
Take a look:
int returnArrayLength(string array[]){
int lengthOfArray = 0,i = 0;
for (i=0; i < sizeof array ; i++){
string scode = "noError()
string ( &passedAr)[] = (addr_ "( ( addr_ array ) int ) ")
string s = passedAr["i"]
lastError()
return_ \"Y\""
if ( ( eval_ scode ) == "Y" ){
lengthOfArray++
print lengthOfArray
} else {
print "Exception caught!" "\n"
break
}
}
return lengthOfArray
}
string labels[6]
labels[0] = "label0"
labels[1] = "label1"
labels[2] = "label2"
labels[3] = "label3"
print returnArrayLength(labels)
What a fantastic little problem.
Resources I used to help solve this:
How to pass an array into an eval_
eval_ , addr_ , and memory leaks
Testing for unassigned variables - This one doesn't quite work because of the nature of arrays, at least as far as I could tell!
In any case, thanks for the challenge!
I'm using a turn-based match for a board game, and when a turn is complete I call GKTurnBasedMatch.EndTurn and pass the match participants and the new match data as the arguments. I need the game to advance to the unmatched players, but it only does so after some indeterminate time related to the timeout value. Setting the timeout value 0 only prevents the game from ever progressing past player 1. The match data is being updated, so the app is definitely communicating with Game Center servers. What am I missing here?
private void endTurn(double timeout)
{
// Copies list of participants to a mutable array
GKTurnBasedParticipant[] Participants = new GKTurnBasedParticipant[match.Participants.Length];
match.Participants.CopyTo(Participants, 0);
// Advances to the next player
match.EndTurn(Participants, timeout, matchData, (e) =>
{
// If there is an error message, print it to the console
if (e != null)
{
Console.WriteLine(e.LocalizedDescription);
Console.WriteLine(e.LocalizedFailureReason);
}
// Otherwise proceed normally
else
turnOverUpdate();
});
}
Apple's documentation is quite poor for the EndTurn method, but I figured it out. The NextParticipants field should be treated like EndTurnWithNextParticipant, so you have to copy GKTurnBasedMatch.Participants and reorder it so the next player is first and so fourth. The match only gives you the participants in order of joining, not relative to the local player, so you have to sort it. Below is the code I used to accomplish this.
List<GKTurnBasedParticipant> participants = new List<GKTurnBasedParticipant>();
// Gets the index of the local player
int index = 0;
for (int i = 0; i < match.Participants.Length; i++)
{
if (match.Participants[i].Player != null)
{
if (match.Participants[i].Player.PlayerID == GKLocalPlayer.LocalPlayer.PlayerID)
{
index = i;
break;
}
}
}
int offset = match.Participants.Length - index;
for (int i = 1; i < offset; i++)
participants.Add(match.Participants[i + index]);
for (int i = 0; i <= index; i++)
participants.Add(match.Participants[i]);
GKTurnBasedParticipant[] nextParticipants = participants.ToArray();