Forum Discussion
Halloween 2026 Scheduling Challenge 🎃
For this Round Robin challenge, I decided to iterate through process (treats), full slice counts 'k', and partial slices in 'mod'. MAP felt like the best tool for the task because the 1D shapes are the same.
Many Round Robin schedulers tend to build a queue. I chose to obtain the counts, expand the processes (treats), and assign to a queue by using Streakλ to obtain the appearance index. The only regret is not creating the entire schedule in one MAP. The next best thing was setting up SCAN for some easy accumulation.
/* [TBD] -------------------------------------------------------------------------
Function: RoundRobinλ
Category: Algorithms (Scheduling)
Author: Patrick H.
Version: 1.1
Dependencies: Resizeλ, Streakλ
Description:
Produces a standard Round Robin schedule from process, arrival, burst, and quantum
inputs. Each process receives equal-sized time slices (quantum) in cyclic order.
If MOD(burst, quantum) <> 0, the remaining work is scheduled in a final tail slice.
The ready-queue behavior is simulated structurally: processes are expanded into
repeated quantum slices, assigned appearance indices via Streakλ, and sorted by
appearance to emulate circular FIFO ordering. SCAN is used to accumulate slice
boundaries, ensuring a non-overlapping, strictly increasing timeline.
Parameters:
process - Process identifier (text)
arrival - Ready time of each process (integer >= 0)
burst - CPU time required for each process (integer > 0)
quantum - Duration of each time slice (integer > 0)
------------------------------------------------------------------------------*/
RoundRobinλ=
LAMBDA(
process,
arrival,
burst,
quantum,
LET(
// Flatten, stack and sort inputs
input, SORT(HSTACK(TOCOL(process),TOCOL(arrival),TOCOL(burst)),2),
arrival, CHOOSECOLS(input,2),
process, TAKE(input,,1),
burst, TAKE(input,,-1),
// Halting scenarios
InValidInput?, OR(ISERROR(input)),
InvalidBurst?, OR(OR(ISTEXT(burst)),OR(burst <= 0)),
InvalidQuantum?, OR(ISTEXT(quantum),quantum <= 0),
IF(InValidInput?,#VALUE!,
IF(InvalidBurst?,"#BURST!",
IF(InvalidQuantum?,"#QUANTUM!",
LET(
// Full slice counts
k, QUOTIENT(+burst,quantum),
// Partial slices
mod, MOD(burst,quantum),
// Unpack, nest processes, flatten
Unpackλ, LAMBDA(p_,k_,m_,
LET(
low_stock, k_ = 0,
each_process, EXPAND(p_,k_,,p_),
time_slice, Resizeλ(HSTACK(0,quantum),k_),
tail, HSTACK(p_,0,m_),
sched, HSTACK(each_process,time_slice),
final, IF(low_stock,tail,
IF(m_,VSTACK(sched,tail),sched)),
final)),
flat, FLATTEN( MAP(process,k,mod,Unpackλ) ,,0),
// Appearance index
idx, Streakλ(TAKE(flat,,1)),
// Sort by appearance
sorted, SORTBY(flat,idx,1),
// Burst accumulation
acc, SCAN(0,TAKE(sorted,,-2),SUM),
// Present
header, {"Process","Start","Stop"},
final, VSTACK(header,HSTACK(TAKE(sorted,,1),acc)),
final
))))));
If you don't have access to version 3 compatibility, this is what the nested arrays look like before flattening:
Post FLATTEN:
SCAN can then process columns 2-3 from the above array to arrive at the start/stop for each round.
I really enjoy writing functions with nested array support. It feels like a weight had been lifted off my back! I re-wrote an amortization lambda the other day and couldn't believe how much easier it was to put together without those old limitations. My old recursive approach is obsolete!