Control Flow: loops & conditionals
The bread and butter of every imperative language: a for loop and an if. The task is identical in all seven - sum the integers 1..5, but use an if to skip the even numbers, then print the total of the odds (1 + 3 + 5 = 9). Because the accumulator is a single integer living in a register or on the stack, this topic touches no heap at all - no malloc, nothing to free - so it's a clean look at each language's loop and branch syntax before the memory-heavy topics. Note how the C-family share for (init; cond; step) while Zig, Hare, Odin, and Forth each spell iteration their own way.
#include <stdio.h>
int main(void) {
int total = 0; /* accumulator: just a stack int */
for (int i = 1; i <= 5; i++) {
if (i % 2 == 0)
continue; /* skip even numbers */
total += i; /* add 1, 3, 5 */
}
printf("%d\n", total); /* 9 */
return 0;
}Classic C for (init; cond; step) with if/continue to skip the evens. total is an automatic (stack) variable, so there is no heap and nothing to free - control flow here costs zero allocations.
#include <iostream>
int main() {
int total = 0;
// Range-based for over a brace-init list: clean and idiomatic.
for (int i : {1, 2, 3, 4, 5}) {
if (i % 2 == 0)
continue; // skip even numbers
total += i; // 1 + 3 + 5
}
std::cout << total << '\n'; // 9
}Modern C++ favors the range-based for (for (int i : {...})) over an index loop; if/continue filters the evens. The accumulator stays on the stack - no new/delete, no smart pointer needed - so RAII has nothing to clean up.
// Top-level code runs in TempleOS - no main() needed.
I64 total = 0;
I64 i;
for (i = 1; i <= 5; i++) {
if (i & 1) // odd: low bit set
total += i; // add 1, 3, 5
}
Print("%d\n", total); // 9
HolyC's for and if are C-like (here i & 1 tests oddness via the low bit). Top-level code runs with no main, and total is a plain I64 on the task stack - the per-task heap is never touched, so there's no MAlloc/Free.
const std = @import("std");
pub fn main() void {
var total: i32 = 0;
// No C-style for here: use a while with a continue-expression.
var i: i32 = 1;
while (i <= 5) : (i += 1) {
if (@rem(i, 2) == 0) continue; // skip even numbers
total += i; // 1 + 3 + 5
}
std.debug.print("{d}\n", .{total}); // 9
}Zig has no C-style for; the idiom is while (cond) : (step), with the continue-expression after the :. if/continue skips the evens, and total is a stack var - Zig never hides allocations, so no allocator is involved here.
use fmt;
export fn main() void = {
let total = 0;
// Hare's for loop: for (init; cond; step) { ... }.
for (let i = 1; i <= 5; i += 1) {
if (i % 2 == 0)
continue; // skip even numbers
total += i; // 1 + 3 + 5
};
fmt::println(total)!; // 9
};Hare keeps a C-style for (init; cond; step) plus if/continue. The accumulator is a stack let with no alloc/free in sight; the trailing ! on fmt::println propagates any I/O error rather than ignoring it.
package main
import "core:fmt"
main :: proc() {
total := 0
// Odin's for has no parens; this is the C-style three-part form.
for i := 1; i <= 5; i += 1 {
if i % 2 == 0 {
continue // skip even numbers
}
total += i // 1 + 3 + 5
}
fmt.println(total) // 9
}Odin spells every loop with the keyword for (no parentheses) - here the C-style init; cond; step form, with if/continue. total lives on the stack, so the implicit context.allocator is untouched: no new/delete.
\ DO ... LOOP runs the index from a START up to (LIMIT-1).
\ 6 1 DO ... LOOP gives I = 1,2,3,4,5. We keep the running
\ total on the data stack instead of in a variable.
: ODD-SUM ( -- n )
0 ( total=0 on the stack )
6 1 DO ( loop I = 1..5 )
I 2 MOD IF ( odd? low result nonzero )
I + ( add I to the running total )
THEN
LOOP ; ( -- total )
ODD-SUM . \ prints 9
CRForth's counted loop is LIMIT START DO ... LOOP, where I pushes the current index; IF ... THEN is the conditional (RPN: the flag is already on the stack). There are no named locals here - the running total is carried on the data stack, so nothing is allocated or freed.