/* Java 17 -- Loops and recursion while, do-while, for, for-each, recursion No em dashes anywhere -- use plain hyphens or rewrite the sentence. */ STEPPER_REGISTRY.register("java-17/loops", { label: 'Loops and recursion', panelConfig: { stack: true, heap: false, custom: false, legend: [ { color:'#1d5799', border:'#9dc0e8', label:'Currently running frame' }, { color:'#68655f', border:'#d4d1cc', label:'Paused, waiting to resume' }, { color:'#a0540a', border:'#e8c070', label:'Variable just assigned' }, { color:'#095e40', border:'#90d4b8', label:'Holding a value' }, { color:'#3d3490', border:'#b0a8e0', label:'Reference (array or object)' }, ] }, scenarios: [ /* ------------------------------------------------------------------ */ /* 1. while loop */ /* ------------------------------------------------------------------ */ { label: 'while loop', lines: [ { code: [['tok-kw','static '],['tok-kw','void '],['tok-meth','main'],['tok-op','(String[] args) {']] }, { code: [['',' '],['tok-type','int '],['tok-op','count = '],['tok-num','0'],['tok-op',';']] }, { code: [['',' '],['tok-kw','while '],['tok-op','(count < '],['tok-num','3'],['tok-op',') {']] }, { code: [['',' '],['tok-op','count++;']] }, { code: [['',' '],['tok-op','}']] }, { code: [['',' '],['tok-op','System.out.println('],['tok-str','"Done"'],['tok-op',');']] }, { code: [['tok-op','}']] }, ], steps: [ { line:0, stack:[{name:'main()',vars:[],active:true}], expl:{label:'Program starts', text:'Java enters main() and pushes a new frame onto the call stack.'} }, { line:1, stack:[{name:'main()',vars:[{n:'count',v:'0',fresh:true}],active:true}], expl:{label:'Variable declared', text:'int count = 0 creates a local variable initialized to 0.'} }, { line:2, tokenRange:[2,3], stack:[{name:'main()',vars:[{n:'count',v:'0',fresh:false}],active:true}], expl:{label:'Condition: true', text:'Java evaluates count < 3. Since 0 < 3, the condition is true and the loop body runs.'} }, { line:3, stack:[{name:'main()',vars:[{n:'count',v:'1',fresh:true}],active:true}], expl:{label:'Loop body', text:'count++ increments count from 0 to 1.'} }, { line:2, tokenRange:[2,3], stack:[{name:'main()',vars:[{n:'count',v:'1',fresh:false}],active:true}], expl:{label:'Condition: true', text:'Back at the top. 1 < 3 is still true -- another iteration begins.'} }, { line:3, stack:[{name:'main()',vars:[{n:'count',v:'2',fresh:true}],active:true}], expl:{label:'Loop body', text:'count++ increments count to 2.'} }, { line:2, tokenRange:[2,3], stack:[{name:'main()',vars:[{n:'count',v:'2',fresh:false}],active:true}], expl:{label:'Condition: true', text:'2 < 3 is still true -- one more iteration.'} }, { line:3, stack:[{name:'main()',vars:[{n:'count',v:'3',fresh:true}],active:true}], expl:{label:'Loop body', text:'count++ increments count to 3.'} }, { line:2, tokenRange:[2,3], stack:[{name:'main()',vars:[{n:'count',v:'3',fresh:false}],active:true}], expl:{label:'Condition: false', text:'3 < 3 is false. The loop exits -- execution jumps past the closing brace.'} }, { line:5, stack:[{name:'main()',vars:[{n:'count',v:'3',fresh:false}],active:true}], expl:{label:'After the loop', text:'count retains its value of 3. Its scope continues until main() ends.'} }, { line:6, stack:[{name:'main()',vars:[{n:'count',v:'3',fresh:false}],active:true}], expl:{label:'Program ends', text:'main() reaches its closing brace. Its frame is popped and the program finishes.'} }, ] }, /* ------------------------------------------------------------------ */ /* 2. do-while loop */ /* ------------------------------------------------------------------ */ { label: 'do-while loop', lines: [ { code: [['tok-kw','static '],['tok-kw','void '],['tok-meth','main'],['tok-op','(String[] args) {']] }, { code: [['',' '],['tok-type','int '],['tok-op','n = '],['tok-num','0'],['tok-op',';']] }, { code: [['',' '],['tok-kw','do '],['tok-op','{']] }, { code: [['',' '],['tok-op','n++;']] }, { code: [['',' '],['tok-op','} '],['tok-kw','while '],['tok-op','(n < '],['tok-num','2'],['tok-op',');']] }, { code: [['',' '],['tok-op','System.out.println('],['tok-str','"Done"'],['tok-op',');']] }, { code: [['tok-op','}']] }, ], steps: [ { line:0, stack:[{name:'main()',vars:[],active:true}], expl:{label:'Program starts', text:'main() begins and a new frame is pushed.'} }, { line:1, stack:[{name:'main()',vars:[{n:'n',v:'0',fresh:true}],active:true}], expl:{label:'Variable declared', text:'n is initialized to 0.'} }, { line:2, stack:[{name:'main()',vars:[{n:'n',v:'0',fresh:false}],active:true}], expl:{label:'Enter do block', text:'With do-while, the body always executes at least once -- the condition has not been checked yet.'} }, { line:3, stack:[{name:'main()',vars:[{n:'n',v:'1',fresh:true}],active:true}], expl:{label:'Loop body', text:'n++ increments n from 0 to 1.'} }, { line:4, tokenRange:[3,4], stack:[{name:'main()',vars:[{n:'n',v:'1',fresh:false}],active:true}], expl:{label:'Condition: true', text:'Now the condition is checked: 1 < 2 is true. Execution jumps back to the top of the body.'} }, { line:3, stack:[{name:'main()',vars:[{n:'n',v:'2',fresh:true}],active:true}], expl:{label:'Loop body', text:'n++ increments n to 2.'} }, { line:4, tokenRange:[3,4], stack:[{name:'main()',vars:[{n:'n',v:'2',fresh:false}],active:true}], expl:{label:'Condition: false', text:'2 < 2 is false. The loop exits. The body ran twice -- the condition was never checked before the first run.'} }, { line:5, stack:[{name:'main()',vars:[{n:'n',v:'2',fresh:false}],active:true}], expl:{label:'After the loop', text:'System.out.println("Done") runs.'} }, { line:6, stack:[{name:'main()',vars:[{n:'n',v:'2',fresh:false}],active:true}], expl:{label:'Program ends', text:'main() finishes.'} }, ] }, /* ------------------------------------------------------------------ */ /* 3. for loop */ /* ------------------------------------------------------------------ */ { label: 'for loop', lines: [ { code: [['tok-kw','static '],['tok-kw','void '],['tok-meth','main'],['tok-op','(String[] args) {']] }, { code: [['',' '],['tok-type','int '],['tok-op','sum = '],['tok-num','0'],['tok-op',';']] }, { code: [['',' '],['tok-kw','for '],['tok-op','('],['tok-type','int '],['tok-op','i = '],['tok-num','0'],['tok-op','; '],['tok-op','i < '],['tok-num','3'],['tok-op','; '],['tok-op','i++'],['tok-op',') {']] }, { code: [['',' '],['tok-op','sum += i;']] }, { code: [['',' '],['tok-op','}']] }, { code: [['',' '],['tok-op','System.out.println(sum);']] }, { code: [['tok-op','}']] }, ], steps: [ { line:0, stack:[{name:'main()',vars:[],active:true}], expl:{label:'Program starts', text:'main() begins.'} }, { line:1, stack:[{name:'main()',vars:[{n:'sum',v:'0',fresh:true}],active:true}], expl:{label:'Variable declared', text:'sum starts at 0. It will accumulate the total.'} }, { line:2, tokenRange:[3,5], stack:[{name:'main()',vars:[{n:'sum',v:'0',fresh:false},{n:'i',v:'0',fresh:true}],active:true}], expl:{label:'Init', text:'The initializer int i = 0 runs exactly once. i is created and set to 0.'} }, { line:2, tokenRange:[7,8], stack:[{name:'main()',vars:[{n:'sum',v:'0',fresh:false},{n:'i',v:'0',fresh:false}],active:true}], expl:{label:'Condition: true', text:'Java checks i < 3. 0 < 3 is true -- the loop body runs.'} }, { line:3, stack:[{name:'main()',vars:[{n:'sum',v:'0',fresh:false},{n:'i',v:'0',fresh:false}],active:true}], expl:{label:'Loop body', text:'sum += i adds 0 to sum. sum stays 0 this iteration.'} }, { line:2, tokenRange:[10,10], stack:[{name:'main()',vars:[{n:'sum',v:'0',fresh:false},{n:'i',v:'1',fresh:true}],active:true}], expl:{label:'Increment', text:'The update i++ runs. i goes from 0 to 1.'} }, { line:2, tokenRange:[7,8], stack:[{name:'main()',vars:[{n:'sum',v:'0',fresh:false},{n:'i',v:'1',fresh:false}],active:true}], expl:{label:'Condition: true', text:'Java checks i < 3. 1 < 3 is true -- another iteration.'} }, { line:3, stack:[{name:'main()',vars:[{n:'sum',v:'1',fresh:true},{n:'i',v:'1',fresh:false}],active:true}], expl:{label:'Loop body', text:'sum += i adds 1. sum is now 1.'} }, { line:2, tokenRange:[10,10], stack:[{name:'main()',vars:[{n:'sum',v:'1',fresh:false},{n:'i',v:'2',fresh:true}],active:true}], expl:{label:'Increment', text:'i++ runs. i goes from 1 to 2.'} }, { line:2, tokenRange:[7,8], stack:[{name:'main()',vars:[{n:'sum',v:'1',fresh:false},{n:'i',v:'2',fresh:false}],active:true}], expl:{label:'Condition: true', text:'Java checks i < 3. 2 < 3 is true -- one more iteration.'} }, { line:3, stack:[{name:'main()',vars:[{n:'sum',v:'3',fresh:true},{n:'i',v:'2',fresh:false}],active:true}], expl:{label:'Loop body', text:'sum += i adds 2. sum is now 3.'} }, { line:2, tokenRange:[10,10], stack:[{name:'main()',vars:[{n:'sum',v:'3',fresh:false},{n:'i',v:'3',fresh:true}],active:true}], expl:{label:'Increment', text:'i++ runs. i goes from 2 to 3.'} }, { line:2, tokenRange:[7,8], stack:[{name:'main()',vars:[{n:'sum',v:'3',fresh:false},{n:'i',v:'3',fresh:false}],active:true}], expl:{label:'Condition: false', text:'Java checks i < 3. 3 < 3 is false -- the loop exits. i goes out of scope.'} }, { line:5, stack:[{name:'main()',vars:[{n:'sum',v:'3',fresh:false}],active:true}], expl:{label:'After the loop', text:'i is gone -- it was scoped to the for header. System.out.println(sum) prints 3.'} }, { line:6, stack:[{name:'main()',vars:[{n:'sum',v:'3',fresh:false}],active:true}], expl:{label:'Program ends', text:'main() finishes.'} }, ] }, /* ------------------------------------------------------------------ */ /* 4. for-each loop */ /* ------------------------------------------------------------------ */ { label: 'for-each loop', lines: [ { code: [['tok-kw','static '],['tok-kw','void '],['tok-meth','main'],['tok-op','(String[] args) {']] }, { code: [['',' '],['tok-type','int'],['tok-op','[] nums = {'],['tok-num','10'],['tok-op',', '],['tok-num','20'],['tok-op',', '],['tok-num','30'],['tok-op','};']] }, { code: [['',' '],['tok-type','int '],['tok-op','total = '],['tok-num','0'],['tok-op',';']] }, { code: [['',' '],['tok-kw','for '],['tok-op','('],['tok-type','int '],['tok-op','n : nums) {']] }, { code: [['',' '],['tok-op','total += n;']] }, { code: [['',' '],['tok-op','}']] }, { code: [['',' '],['tok-op','System.out.println(total);']] }, { code: [['tok-op','}']] }, ], steps: [ { line:0, stack:[{name:'main()',vars:[],active:true}], expl:{label:'Program starts', text:'main() begins.'} }, { line:1, stack:[{name:'main()',vars:[{n:'nums',v:'[10, 20, 30]',fresh:true,cls:true}],active:true}], expl:{label:'Array created', text:'An int array {10, 20, 30} is created. nums holds a reference to it -- arrays are objects in Java.'} }, { line:2, stack:[{name:'main()',vars:[{n:'nums',v:'[10, 20, 30]',fresh:false,cls:true},{n:'total',v:'0',fresh:true}],active:true}], expl:{label:'Variable declared', text:'total starts at 0. It will hold the running sum.'} }, { line:3, stack:[{name:'main()',vars:[{n:'nums',v:'[10, 20, 30]',fresh:false,cls:true},{n:'total',v:'0',fresh:false},{n:'n',v:'10',fresh:true}],active:true}], expl:{label:'First element', text:'The for-each loop retrieves the first element and assigns it to n. n = 10.'} }, { line:4, stack:[{name:'main()',vars:[{n:'nums',v:'[10, 20, 30]',fresh:false,cls:true},{n:'total',v:'10',fresh:true},{n:'n',v:'10',fresh:false}],active:true}], expl:{label:'Loop body', text:'total += n adds 10. total is now 10.'} }, { line:3, stack:[{name:'main()',vars:[{n:'nums',v:'[10, 20, 30]',fresh:false,cls:true},{n:'total',v:'10',fresh:false},{n:'n',v:'20',fresh:true}],active:true}], expl:{label:'Next element', text:'The loop advances. n is now 20.'} }, { line:4, stack:[{name:'main()',vars:[{n:'nums',v:'[10, 20, 30]',fresh:false,cls:true},{n:'total',v:'30',fresh:true},{n:'n',v:'20',fresh:false}],active:true}], expl:{label:'Loop body', text:'total += n adds 20. total is now 30.'} }, { line:3, stack:[{name:'main()',vars:[{n:'nums',v:'[10, 20, 30]',fresh:false,cls:true},{n:'total',v:'30',fresh:false},{n:'n',v:'30',fresh:true}],active:true}], expl:{label:'Last element', text:'n is now 30 -- the final element in the array.'} }, { line:4, stack:[{name:'main()',vars:[{n:'nums',v:'[10, 20, 30]',fresh:false,cls:true},{n:'total',v:'60',fresh:true},{n:'n',v:'30',fresh:false}],active:true}], expl:{label:'Loop body', text:'total += n adds 30. total is now 60.'} }, { line:3, stack:[{name:'main()',vars:[{n:'nums',v:'[10, 20, 30]',fresh:false,cls:true},{n:'total',v:'60',fresh:false}],active:true}], expl:{label:'No more elements', text:'All elements have been visited. The loop exits and n goes out of scope.'} }, { line:6, stack:[{name:'main()',vars:[{n:'nums',v:'[10, 20, 30]',fresh:false,cls:true},{n:'total',v:'60',fresh:false}],active:true}], expl:{label:'After the loop', text:'System.out.println(total) prints 60 -- the sum of all elements.'} }, { line:7, stack:[{name:'main()',vars:[{n:'nums',v:'[10, 20, 30]',fresh:false,cls:true},{n:'total',v:'60',fresh:false}],active:true}], expl:{label:'Program ends', text:'main() finishes.'} }, ] }, /* ------------------------------------------------------------------ */ /* 5. Recursion */ /* */ /* Line index (blank lines excluded from count): */ /* 0 static void main(String[] args) { */ /* 1 countdown(3); */ /* 2 } */ /* (blank -- not counted) */ /* 3 static void countdown(int n) { */ /* 4 if (n == 0) { */ /* 5 System.out.println("Go!"); */ /* 6 return; */ /* 7 } */ /* 8 System.out.println(n); */ /* 9 countdown(n - 1); */ /* 10 } */ /* ------------------------------------------------------------------ */ { label: 'Recursion', lines: [ { code: [['tok-kw','static '],['tok-kw','void '],['tok-meth','main'],['tok-op','(String[] args) {']] }, { code: [['',' '],['tok-meth','countdown'],['tok-op','('],['tok-num','3'],['tok-op',');']] }, { code: [['tok-op','}']] }, { blank: true }, { code: [['tok-kw','static '],['tok-kw','void '],['tok-meth','countdown'],['tok-op','('],['tok-type','int '],['tok-op','n) {']] }, { code: [['',' '],['tok-kw','if '],['tok-op','(n == '],['tok-num','0'],['tok-op',') {']] }, { code: [['',' '],['tok-op','System.out.println('],['tok-str','"Go!"'],['tok-op',');']] }, { code: [['',' '],['tok-kw','return'],['tok-op',';']] }, { code: [['',' '],['tok-op','}']] }, { code: [['',' '],['tok-op','System.out.println(n);']] }, { code: [['',' '],['tok-meth','countdown'],['tok-op','(n - '],['tok-num','1'],['tok-op',');']] }, { code: [['tok-op','}']] }, ], steps: [ { line:0, stack:[{name:'main()',vars:[],active:true}], expl:{label:'Program starts', text:'main() begins. Its frame is at the top of the call stack.'} }, { line:1, stack:[{name:'main()',vars:[],active:true}], expl:{label:'Calling countdown(3)', text:'countdown(3) is called. Java will push a new frame for it.'} }, { line:3, stack:[{name:'countdown(n=3)',vars:[{n:'n',v:'3',fresh:true}],active:true},{name:'main()',vars:[],active:false}], expl:{label:'Frame created', text:'A new frame for countdown() is pushed. Parameter n is set to 3. main() is now waiting.'} }, { line:4, stack:[{name:'countdown(n=3)',vars:[{n:'n',v:'3',fresh:false}],active:true},{name:'main()',vars:[],active:false}], expl:{label:'Base case check', text:'n == 0? No -- 3 is not 0. The if block is skipped.'} }, { line:8, stack:[{name:'countdown(n=3)',vars:[{n:'n',v:'3',fresh:false}],active:true},{name:'main()',vars:[],active:false}], expl:{label:'Print n', text:'System.out.println(n) prints 3.'} }, { line:9, stack:[{name:'countdown(n=3)',vars:[{n:'n',v:'3',fresh:false}],active:true},{name:'main()',vars:[],active:false}], expl:{label:'Recursive call', text:'countdown(n - 1) calls itself with 2. A new frame is about to be pushed.'} }, { line:3, stack:[{name:'countdown(n=2)',vars:[{n:'n',v:'2',fresh:true}],active:true},{name:'countdown(n=3)',vars:[{n:'n',v:'3',fresh:false}],active:false},{name:'main()',vars:[],active:false}], expl:{label:'Stack grows', text:'A second countdown() frame is pushed. Each call has its own n. The first call waits below.'} }, { line:4, stack:[{name:'countdown(n=2)',vars:[{n:'n',v:'2',fresh:false}],active:true},{name:'countdown(n=3)',vars:[{n:'n',v:'3',fresh:false}],active:false},{name:'main()',vars:[],active:false}], expl:{label:'Base case check', text:'n == 0? No -- 2 is not 0.'} }, { line:8, stack:[{name:'countdown(n=2)',vars:[{n:'n',v:'2',fresh:false}],active:true},{name:'countdown(n=3)',vars:[{n:'n',v:'3',fresh:false}],active:false},{name:'main()',vars:[],active:false}], expl:{label:'Print n', text:'Prints 2.'} }, { line:9, stack:[{name:'countdown(n=2)',vars:[{n:'n',v:'2',fresh:false}],active:true},{name:'countdown(n=3)',vars:[{n:'n',v:'3',fresh:false}],active:false},{name:'main()',vars:[],active:false}], expl:{label:'Recursive call', text:'countdown(1) is called. Another frame will be pushed.'} }, { line:3, stack:[{name:'countdown(n=1)',vars:[{n:'n',v:'1',fresh:true}],active:true},{name:'countdown(n=2)',vars:[{n:'n',v:'2',fresh:false}],active:false},{name:'countdown(n=3)',vars:[{n:'n',v:'3',fresh:false}],active:false},{name:'main()',vars:[],active:false}], expl:{label:'Stack grows', text:'Three countdown() frames now sit above main(). Each is waiting for the one above it to finish.'} }, { line:4, stack:[{name:'countdown(n=1)',vars:[{n:'n',v:'1',fresh:false}],active:true},{name:'countdown(n=2)',vars:[{n:'n',v:'2',fresh:false}],active:false},{name:'countdown(n=3)',vars:[{n:'n',v:'3',fresh:false}],active:false},{name:'main()',vars:[],active:false}], expl:{label:'Base case check', text:'n == 0? No -- 1 is not 0.'} }, { line:8, stack:[{name:'countdown(n=1)',vars:[{n:'n',v:'1',fresh:false}],active:true},{name:'countdown(n=2)',vars:[{n:'n',v:'2',fresh:false}],active:false},{name:'countdown(n=3)',vars:[{n:'n',v:'3',fresh:false}],active:false},{name:'main()',vars:[],active:false}], expl:{label:'Print n', text:'Prints 1.'} }, { line:9, stack:[{name:'countdown(n=1)',vars:[{n:'n',v:'1',fresh:false}],active:true},{name:'countdown(n=2)',vars:[{n:'n',v:'2',fresh:false}],active:false},{name:'countdown(n=3)',vars:[{n:'n',v:'3',fresh:false}],active:false},{name:'main()',vars:[],active:false}], expl:{label:'Recursive call', text:'countdown(0) is called -- this will trigger the base case.'} }, { line:3, stack:[{name:'countdown(n=0)',vars:[{n:'n',v:'0',fresh:true}],active:true},{name:'countdown(n=1)',vars:[{n:'n',v:'1',fresh:false}],active:false},{name:'countdown(n=2)',vars:[{n:'n',v:'2',fresh:false}],active:false},{name:'countdown(n=3)',vars:[{n:'n',v:'3',fresh:false}],active:false},{name:'main()',vars:[],active:false}], expl:{label:'Deepest call', text:'The stack is now at its deepest: five frames. n = 0 in this frame.'} }, { line:4, stack:[{name:'countdown(n=0)',vars:[{n:'n',v:'0',fresh:false}],active:true},{name:'countdown(n=1)',vars:[{n:'n',v:'1',fresh:false}],active:false},{name:'countdown(n=2)',vars:[{n:'n',v:'2',fresh:false}],active:false},{name:'countdown(n=3)',vars:[{n:'n',v:'3',fresh:false}],active:false},{name:'main()',vars:[],active:false}], expl:{label:'Base case: true', text:'n == 0 is true. The base case is reached -- the if block executes.'} }, { line:5, stack:[{name:'countdown(n=0)',vars:[{n:'n',v:'0',fresh:false}],active:true},{name:'countdown(n=1)',vars:[{n:'n',v:'1',fresh:false}],active:false},{name:'countdown(n=2)',vars:[{n:'n',v:'2',fresh:false}],active:false},{name:'countdown(n=3)',vars:[{n:'n',v:'3',fresh:false}],active:false},{name:'main()',vars:[],active:false}], expl:{label:'Print "Go!"', text:'System.out.println("Go!") runs. The countdown is complete.'} }, { line:6, stack:[{name:'countdown(n=0)',vars:[{n:'n',v:'0',fresh:false}],active:true},{name:'countdown(n=1)',vars:[{n:'n',v:'1',fresh:false}],active:false},{name:'countdown(n=2)',vars:[{n:'n',v:'2',fresh:false}],active:false},{name:'countdown(n=3)',vars:[{n:'n',v:'3',fresh:false}],active:false},{name:'main()',vars:[],active:false}], expl:{label:'return', text:'return exits countdown(n=0). Its frame is popped -- the stack begins to unwind.'} }, { line:10, stack:[{name:'countdown(n=1)',vars:[{n:'n',v:'1',fresh:false}],active:true},{name:'countdown(n=2)',vars:[{n:'n',v:'2',fresh:false}],active:false},{name:'countdown(n=3)',vars:[{n:'n',v:'3',fresh:false}],active:false},{name:'main()',vars:[],active:false}], expl:{label:'Unwinding', text:'Back in countdown(n=1). It has no more work to do -- it reaches its closing brace and returns.'} }, { line:10, stack:[{name:'countdown(n=2)',vars:[{n:'n',v:'2',fresh:false}],active:true},{name:'countdown(n=3)',vars:[{n:'n',v:'3',fresh:false}],active:false},{name:'main()',vars:[],active:false}], expl:{label:'Unwinding', text:'countdown(n=2) also completes and is popped.'} }, { line:10, stack:[{name:'countdown(n=3)',vars:[{n:'n',v:'3',fresh:false}],active:true},{name:'main()',vars:[],active:false}], expl:{label:'Unwinding', text:'countdown(n=3) completes and is popped. Only main() remains.'} }, { line:2, stack:[{name:'main()',vars:[],active:true}], expl:{label:'Back in main', text:'main() resumes after the original countdown(3) call. The program ends.'} }, ] }, ] });