Is there a way to continue the most outer loop from the most nested one in ABAP?
Example in Java. There is a construct in this language using labels (most people do not know of it anyway) which allows me to continue the most outer loop from the nested one.
public class NestedLoopContinue {
public static void main(String[] args) {
label1: for (int i = 0; i < 5; i++) {
for (int j = 0; j < 2; j++) {
if (i == 3) {
continue label1;
}
}
System.out.println(i + 1);
}
}
}
This outputs
1
2
3
5
Now, how can I do it in ABAP in a smart way? One solution would be to use TRY. ENDTRY. block but it is rather a hacking one. Any other ideas?
DATA: l_outer_counter TYPE i.
DO 5 TIMES.
l_outer_counter = sy-index.
TRY.
DO 2 TIMES.
IF l_outer_counter = 4.
RAISE EXCEPTION TYPE cx_abap_random.
ENDIF.
ENDDO.
WRITE / l_outer_counter.
CATCH cx_abap_random.
CONTINUE.
ENDTRY.
ENDDO.
Or maybe there is a way to tell whether the DO. ENDO. ended with an EXIT statement (without introducing an own variable of course, like SYST global variable)?
DATA: l_outer_counter TYPE i.
DO 5 TIMES.
l_outer_counter = sy-index.
DO 2 TIMES.
IF l_outer_counter = 4.
EXIT.
ENDIF.
ENDDO.
IF sy-last_loop_ended_with_exit = abap_true. "???
CONTINUE.
ENDIF.
WRITE / l_outer_counter.
ENDDO.
I don't know of an ABAP-specific solution, but I've used a general programming solution to handle this before; simply use a boolean and check at the end of the inner loop whether or not to continue.
In Java:
public class NestedLoopContinue
{
public static void main(String[] args)
{
for (int i = 0; i < 5; i++)
{
boolean earlyBreak = false;
for (int j = 0; j < 2; j++)
{
if (i == 3)
{
earlyBreak = true;
break;
}
}
if (earlyBreak)
{
continue;
}
System.out.println(i + 1);
}
}
}
And in ABAP:
DATA: l_outer_counter type i,
early_break type FLAG.
DO 5 TIMES.
l_outer_counter = sy-index.
DO 2 TIMES.
IF l_outer_counter = 4.
early_break = ABAP_TRUE.
EXIT.
ENDIF.
ENDDO.
IF early_break = ABAP_TRUE.
CLEAR early_break.
CONTINUE.
ENDIF.
WRITE / l_outer_counter.
ENDDO.
I've read that the reason label-based breaks exist in Java in the first place is because GOTO statements explicitly do not, and the case covered by label-based break was one of the few "good" uses of GOTO that the team wanted to maintain.
In general, though, this is a very awkward construction. Is there no potential way to refactor your code (perhaps swapping the inner-ness of the loops) to remove the need for this in the first place?
When working with nested loops, I often find the best way to improve readability, and avoid using more unusual approaches (such as breaking to a label, which is not only controversial because of its goto-like nature, but also reduces readability because a lot of people are not familiar with them) is to extract the inner loop into a separate function. I do not know how this is done in ABAP, but the refactored Java equivalent would be:
public class NestedLoopContinue {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
NestedLoopContinue.innerLoop(i)
}
}
static void innerLoop(int i) {
for (int j = 0; j < 2; j++) {
if (i == 3) {
return;
}
}
System.out.println(i + 1);
}
}
I would argue that in this example, this actually becomes less readable because it is harder to follow the logic across the two methods. However, if this was a real-world example (where the methods and variables had some actual meanings and appropriate names to go with them), then the result of extracting the inner loop into a separate method would be more readable than using a label.
Based on the robjohncox answer, the ABAP code might look like this.
CLASS lcl_nested_loop_continue DEFINITION FINAL.
PUBLIC SECTION.
CLASS-METHODS:
main.
PRIVATE SECTION.
CLASS-METHODS:
inner_loop
IMPORTING
i_index TYPE i.
ENDCLASS.
CLASS lcl_nested_loop_continue IMPLEMENTATION.
METHOD main.
DO 5 TIMES.
lcl_nested_loop_continue=>inner_loop( sy-index ).
ENDDO.
ENDMETHOD.
METHOD inner_loop.
DO 2 TIMES.
IF i_index = 4.
RETURN.
ENDIF.
ENDDO.
WRITE / i_index.
ENDMETHOD.
ENDCLASS.
Related
For example, string "AAABBB" will have permutations:
"ABAABB",
"BBAABA",
"ABABAB",
etc
What's a good algorithm for generating the permutations? (And what's its time complexity?)
For a multiset, you can solve recursively by position (JavaScript code):
function f(multiset,counters,result){
if (counters.every(x => x === 0)){
console.log(result);
return;
}
for (var i=0; i<counters.length; i++){
if (counters[i] > 0){
_counters = counters.slice();
_counters[i]--;
f(multiset,_counters,result + multiset[i]);
}
}
}
f(['A','B'],[3,3],'');
This is not full answer, just an idea.
If your strings has fixed number of only two letters I'll go with binary tree and good recursion function.
Each node is object that contains name with prefix of parent name and suffix A or B furthermore it have numbers of A and B letters in the name.
Node constructor gets name of parent and number of A and B from parent so it needs only to add 1 to number of A or B and one letter to name.
It doesn't construct next node if there is more than three A (in case of A node) or B respectively, or their sum is equal to the length of starting string.
Now you can collect leafs of 2 trees (their names) and have all permutations that you need.
Scala or some functional language (with object-like features) would be perfect for implementing this algorithm. Hope this helps or just sparks some ideas.
Since you actually want to generate the permutations instead of just counting them, the best complexity you can hope for is O(size_of_output).
Here's a good solution in java that meets that bound and runs very quickly, while consuming negligible space. It first sorts the letters to find the lexographically smallest permutation, and then generates all permutations in lexographic order.
It's known as the Pandita algorithm: https://en.wikipedia.org/wiki/Permutation#Generation_in_lexicographic_order
import java.util.Arrays;
import java.util.function.Consumer;
public class UniquePermutations
{
static void generateUniquePermutations(String s, Consumer<String> consumer)
{
char[] array = s.toCharArray();
Arrays.sort(array);
for (;;)
{
consumer.accept(String.valueOf(array));
int changePos=array.length-2;
while (changePos>=0 && array[changePos]>=array[changePos+1])
--changePos;
if (changePos<0)
break; //all done
int swapPos=changePos+1;
while(swapPos+1 < array.length && array[swapPos+1]>array[changePos])
++swapPos;
char t = array[changePos];
array[changePos] = array[swapPos];
array[swapPos] = t;
for (int i=changePos+1, j = array.length-1; i < j; ++i,--j)
{
t = array[i];
array[i] = array[j];
array[j] = t;
}
}
}
public static void main (String[] args) throws java.lang.Exception
{
StringBuilder line = new StringBuilder();
generateUniquePermutations("banana", s->{
if (line.length() > 0)
{
if (line.length() + s.length() >= 75)
{
System.out.println(line.toString());
line.setLength(0);
}
else
line.append(" ");
}
line.append(s);
});
System.out.println(line);
}
}
Here is the output:
aaabnn aaanbn aaannb aabann aabnan aabnna aanabn aananb aanban aanbna
aannab aannba abaann abanan abanna abnaan abnana abnnaa anaabn anaanb
anaban anabna ananab ananba anbaan anbana anbnaa annaab annaba annbaa
baaann baanan baanna banaan banana bannaa bnaaan bnaana bnanaa bnnaaa
naaabn naaanb naaban naabna naanab naanba nabaan nabana nabnaa nanaab
nanaba nanbaa nbaaan nbaana nbanaa nbnaaa nnaaab nnaaba nnabaa nnbaaa
I am trying to apply a method to an array of arrays through a for loop.
Unfortunately where I have inserted my array of arrays inside the checkGuess method, I am getting the error 'Local variable x defined in an enclosing scope must be final or effectively final'. I'm a little new to Java so I'm not sure what I'm doing wrong... any help would be greatly appreciated.
for(int x = 0; x < columns.length; x++){
columns[x][y].addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event) {
checkGuess(columns[x][y]);
}
});
if(x>4 && y<5){
y++;
x=-1;
}
}
Additional info:
The array of arrays contains JButtons.
The method checkGuess() takes a JButton as an argument as so:
checkGuess(JButton guess){
}
In Java, variables that are used in an anonymous class must be final (or in Java 8, effectively final - essentially the same thing, just without the explicit final modifier). As for why this is, Jon Skeet has a very nice answer to this here.
One method of correcting your code is simply to assign x to a final reference, as shown below:
for (int x = 0; x < columns.length; x++) {
final int copy = x;
columns[x][y].addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
checkGuess(columns[copy][y]);
}
});
}
ActionListener is an inner anonymous class and it doesn't know what 'x' is when it tries to read it when passing the array of arrays to the function checkGuess().
Untested, but this might work:
for(int x = 0; x < columns.length; x++){
columns[x][y].addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event) {
checkGuess(this);
}
});
if(x>4 && y<5){
y++;
x=-1;
}
}
If not, you would need to find out how to pass x into the class.
I believe "this" will reference the parent of the class, which should be 'columns[x][y]. I could be wrong though.
I am trying new things with arrays and having some difficulty. I am trying to create multiple instances of 1 class and putting them into an array.
I am creating the instances like so:
public function creatingitem(e:TimerEvent)
{
amtcreated = Math.ceil(Math.random() * 4);
while (amtcreated >= 1)
{
amtcreated--;
var i:Number = Math.ceil(Math.random() * 3);
switch (i)
{
case 1 :
//Object1
objectnum = 1;
objectwei = 3;
r = new Board(objectnum,objectwei,stagw,stagh);
addChild(r);
fallingitem.push(r);
break;
case 2 :
//Object2
objectnum = 2;
objectwei = 4;
c = new Board(objectnum,objectwei,stagw,stagh);
addChild(c);
fallingitem.push(c);
break;
case 3 :
//Object3
objectnum = 3;
objectwei = 4;
l = new Board(objectnum,objectwei,stagw,stagh);
addChild(l);
fallingitem.push(l);
break;
default :
break;
}
}
}
Once these are created they check if they collide with the main ball:
public function hitcheck(e:Event)
{
for (var v:int = fallingitem.length - 1; v >= 0; v--)
{
if (ball.hitTestObject(fallingitem[v]))
{
trace(fallingitem[v]);
if (fallingitem[v] == r)
{
bonusscore += 100;
fallingitem[v].removeitem();
}
else if (fallingitem[v] == c)
{
bonusscore += 75;
fallingitem[v].removeitem();
}
else if (fallingitem[v] == l)
{
bonusscore += 75;
fallingitem[v].removeitem();
}
trace(bonusscore);
}
}
}
The issue is I am seeing every item getting hit due to the trace function. Not all instances are meeting the if conditions. As an example I could have 2 "r" instances and when I hit both 1 will go through and add to the score and the other will just continue past. The trace directly following the hitTestObject shows me that both are being hit and registered but I am not sure why it does not add score.
Thank you,
You can't really have 2 r instances. When you're creating the instances, if you happen to create 2 rs, the second r = new Board... statement overwrites the reference, and the variable r is referring to the second one. Both objects still exist, but the variable can only refer to one of them, so when you perform the check, you're ignoring the object that was previously set to r but isn't any more.
To fix this, you could turn r, c and l into Arrays and whenever you create an instance, add it to the appropriate array. Then, you would perform the check using (r.indexOf(fallingitem[v]) != -1), which returns true if the object is in the array.
The other way, based on the provided code, would be to check whatever value objectnum is setting in the constructor, since you're setting that value based on whether it's in the r, c or l category. Though, that won't work if the property is private or might be changed.
I have an array which is filled with platforms that are supposed to move.
var MovingPlatformArray:Array = new Array();
for (var c:int = numChildren - 1; c >= 0; c--){
var child3:DisplayObject = getChildAt(c);
if (child3.name == "movingplatform"){
MovingPlatformArray.push(child3);
}
}
this.addEventListener(Event.ENTER_FRAME,ctrl_birdie);
function ctrl_birdie(e:Event):void{
for(var c in MovingPlatformArray){
MovingPlatform[c].y += speed;
if(MovingPlatformArray[c].hitTestPoint(birdie.x,birdie.y,true)){
birdtelleryvertrager=0;
birdtellery = 0;
birdie.y-=14;
}
if(movingplatform.y <= 25){
speed = 2;
}
if(movingplatform.y >= 350){
speed = -2;
}
}
Right now I have 2 moving platforms in this array. But only one moves up and down. But they both register a touch with the birdie. Am I doing something wrong?
In your listener, you're only setting the position of one platform, which ever one "movingplatform" is a reference to. As all your stage instances of moving platforms are named "movingplatform", one lucky platform is getting referenced by name (the rest ignored), instead of what you intended, which is to use the references in your array and adjust each platform.
You probably meant for movingplatform to be a local variable in your event handler, declared something like this:
var movingplatform:DisplayObject = MovingPlatformArray[c] as DisplayObject;
I'd recommend using a for each loop in place of the for in, because I think it's a little cleaner, but this is a minor style thing:
for each (var platform:DisplayObject in MovingPlatformArray)
{
platform.y += speed;
... rest of your code ...
}
For the sake of clarity, I edited the loop variable to be platform instead of movingplatform, to avoid confusion of having a local variable shadow a stage instance (i.e. this.movingplatform). I wanted it to be clear that the stage instance name is not being used here, because the unintentional instance name reference in your code is the source of your problem in the first place.
As far as i'm concerned, you have two options. use a for each, as adam smith suggested or use a for-loop as it was intended to be used :)
for(var c:uint = 0; c < MovingPlatformArray.length; c++){...
and btw: should "MovingPlatform[c].y += speed;" not be "MovingPlatformArray[c].y += speed;"?
edit: looking at your code, i would also suggest you use MovingPlatformArray[c].hitTestObject(birdie) instead of MovingPlatformArray[c].hitTestPoint(birdie.x,birdie.y,true)
If I were you, I would bring the logic for the platform out, and store it in a class. (Ideally you would do this for the birdie object as well). I have created an example below. The movieclips on the stage should extend Platform rather than MovieClip so they invoke the methods at the bottom.
// Use vectors if you know all the items are going to be the same type
var platforms:Vector.<Platform> = new <Platform>[];
for (var c:int = numChildren - 1; c >= 0; c--){
var child:DisplayObject = getChildAt(c);
// You shouldn't check against names (as per the original post). Because
// names should be unique
if (child is Platform){
platforms.push(child);
// This could be random so each platform has a different range
// This means platform 1 could go from y 30 to y 400, platform 2
// could go from y 60 to y 200, etc
child.setRange(25, 400);
}
}
this.addEventListener(Event.ENTER_FRAME, gameLoop);
// Have an overall game loop
function gameLoop(e:Event):void {
// Loop over the platforms
platforms.forEach(function(item:Platform, i:int, a:Vector.<Platform>):void {
// Hit test function in the class means you only have to pass in one mc
// rather than the points and a boolean
if(item.hitTest(birdie)) {
birdtelleryvertrager=0;
birdtellery = 0;
birdie.y-=14;
}
// Removed the movement logic, this should be kept out of the game loop
// plus how much better does this read?
item.move();
});
}
Then in a class location somewhere, like in a folder game/activeObjects
// A class for the platform stored else where
package game.activeObjects
{
import flash.display.MovieClip;
/**
*
*/
public class Platform extends MovieClip {
private const SPEED:Number = 2;
private var _direction:int = 1;
private var _minimumHeight:Number = 25;
private var _maximumHeight:Number = 350;
public function Platform() {
}
public function setRange(minimumHeight:Number, maximumHeight:Number) {
_minimumHeight = minimumHeight;
_maximumHeight = maximumHeight;
}
public function move():void {
this.y += SPEED * _direction;
if(this.y <= _minimumHeight) {
_direction = 1;
} else if(this.y >= _maximumHeight) {
_direction = -1;
}
}
public function hitTest(mc:MovieClip):Boolean {
return hitTestPoint(mc.x,mc.y,true);
}
}
}
I went through several examples posted online but I cant answer my question.
I have my 'p' variable that is being increased by 1 in the for loop. I want the UI to display the progress of calculation (to show how 'p' is increasing from 0 to 1000000). I do the calculation on the separate thread and the I call dispatcher to update the ResultBox in UI. Example:
int p=0;
...
private void GO(object sender, System.Windows.RoutedEventArgs e)
{
new Thread(delegate()
{
DoWork();
}).Start();
}
void DoWork()
{
for (int i = 0; i < 1000; i++)
{
for (int j = 0; j < 10000; j++)
{
p++;
this.Dispatcher.BeginInvoke(delegate { ResultBox.Text = p.ToString(); });
}
}
}
For some reason this doesn't work. However when I put Thread.Sleep(1) just before this.Dispatcher... it works as intended. Does it mean that the UI update (Dispatcher) is called too frequently therefore it freezes?
Is there any other way to do it?
Thank you
Why not bind a property to your TextBox and the update the property value instead of poking at the textbox directly?
Yes only doing p++ in your loop will not take much of time and inside silverlight, Dispatcher is nothing but a simple queue with delegates, and before silverlight can even update and process its UI, you are pumping too many values on the queue. Imagin what will happen if you keep on adding queue way to faster then the queue is dequeued, then eventually it will hit max limit as well. And eventually it will just stop. If your p++ is replaced with more time consuming task, then you may get good result.
You must know that our eye usually can see only updates of 30 fps, more then 30 updates per second will not be of any use at all, I will suggest your view update should be reduced to max 10 updates per second for best performance.
And for showing progress, I think 1 update per second is also enough. First always display updates very slowly, like
void DoWork()
{
for (int i = 0; i < 1000; i++)
{
for (int j = 0; j < 10000; j++)
{
p++;
if((p % 1000)==0){
this.Dispatcher.BeginInvoke(delegate
{ ResultBox.Text = p.ToString(); });
}
}
}
}
Now you can increaes/decrease 1000 to some suitable multipler of 10 to adjust your visual update.