-
Notifications
You must be signed in to change notification settings - Fork 211
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improved loop constructs and scoping. #171
Comments
O_o. I didn't even know Dart worked that way. I assumed the scope would cover the condition.
Is that a rule anyone knows? I think we will likely end up relaxing this if we do some kind of lightweight pattern-matching Object o = ...
if (int i = o) {
print(i.isEven);
} So I think it's reasonable to extend the scope for |
One thing I have needed is counting iterations and detecting the first and last iteration. Something like this... for (var e in items) {
if (!for.first) write(', ');
if (for.last) write('and ');
write(e);
} void printList(header, footer, items) {
for (var item in items) {
if (for.first) print(header);
print('${for.count}. $item'); // for.count is 1-based, for.index is 0-based.
if (for.last) print(footer);
}
} We could use a loop label to access iteration properties but I'd hate to have to go back and name the loop unless we are in a situation where we would name it anyway for break/continue (e.g. A very simple improvement would be a simple keyword for The issue I have with labels hiding the do {
doSomethingAlways();
break if test;
doSomethingBetween();
} Now the keyword-colored |
What about for (var e in items; int index) {
if (index == 0) write(', ');
if (index == items.length) write('and ');
if (index % 2 == 0) {
write('even');
} else {
write('odd');
write(e);
} |
In Dart we should be able to loop over maps. for(var key, var value in someMap) {
// Stuff
} Loop over list with index for(var index, var value in someList) {
// Stuff
} As in #68 (comment), if the dart gets support for language level tuples looping over maps will produce tuple of for(var index, value in someList) {
// Stuff
} |
@l7ssha's proposal would be especially useful in combination with collection-for. With that, you could (for example) write this var closure = transitiveClosure({
for (var name, package in packages)
name: package.dependencies.keys
}); Right now, there's not an elegant way of doing that with collection-for. |
@nex3 you can write: var closure = transitiveClosure({
for (var e in packages.entries)
e.key: e.value.dependencies.keys
}); but I agree that naming key and value is better for understanding. |
The Dart loop constructs,
for
/while
/do
-while
are fairly simplistic and low-level. There are a number of improvements that could make them easier to use to write understandable code in less convoluted manners.Else-branches
A fairly often occurring issue is to look for something in a list, and then do something if nothing is found.
Example:
Here we need an extra check (sometimes an extra
found
boolean if we can't usenull
to represent a missing match).I sometimes write that with a break instead:
If a loop construct allowed an
else
branch, then it could be written as:The
else
branch is executed only when the loop condition becomes non-true. It is skipped over when breaking out of the loop.(Example in dart:convert: https://github.com/dart-lang/sdk/blob/master/sdk/lib/convert/json.dart#L343).
The labeled-break based rewrite also suggests a possible desugaring.
Extended Scope of
do
The
do
loop is sometimes exactly what you need when you want to do some computation before doing the first test. However, any variable used in the test must be declared outside the loop.That's annoying when the variable is not needed after the loop.
Instead we should extend the variable scope of the
do
body to also cover the condition, so the above can be written as:This breaks with the rule that dart scopes and blocks are the same thing. It means that the condition is evaluated in the scope of the block (and it's always a block, we add a block in the specification if the body is a non-block statement to avoid degenerate cases like
do var x = foo(); while (x)
), even if it is lexically outside. It makes sense if we see the entiredo
-loop as a single construct.(Example in dart:io: https://github.com/dart-lang/sdk/blob/master/sdk/lib/io/stdio.dart#L66)
It also breaks the rule that a
continue
is like a break of the loop body block. or at least, only variables declared prior to anycontinue
s can be visible in the condition if acontinue
jumps right to the condition.Combined do-while Loops
Another common issue is the fencepost problem. You want to do something in a loop, and then do something between rounds. This is often written as:
If we extend the
do
/while
loop to allow two blocks, combining thedo
and thewhile
loop, we can write this much more readably as:This simply generalizes
do
-while
andwhile
loops into one that has an optionaldo
part and a potentially emptywhile
part.Here we should again let the scope of the first block extend through the condition and into the second part, because it again makes the construct easier to use. It also works well with the obvious desugaring.
The scope of the
do
part may also be able to extend to anelse
part since it's definitely executed (although I'm not sure where acontinue
in the first, "do", part would go, because going to the condition isn't safe then).(A continue in the first part will skip to the test. A continue in the second part will go back to the start of the loop. In both cases they work exactly like they are breaking the current block).
(Example from dart:collection: https://github.com/dart-lang/sdk/blob/master/sdk/lib/_internal/js_runtime/lib/collection_patch.dart#L656)
The text was updated successfully, but these errors were encountered: