๐Ÿฆ€ Functional Rust
๐ŸŽฌ How Rust Iterators Work Lazy evaluation, chaining, collect(), and zero-cost abstractions.
๐Ÿ“ Text version (for readers / accessibility)

โ€ข Iterators are lazy โ€” .map(), .filter(), .take() build a chain but do no work until consumed

โ€ข .collect() triggers evaluation, transforming the chain into a Vec, HashMap, or other collection

โ€ข Zero-cost abstraction: iterator chains compile to the same machine code as hand-written loops

โ€ข .iter() borrows, .into_iter() consumes, .iter_mut() borrows mutably

โ€ข Chaining replaces nested loops with a readable, composable pipeline

262: Sliding Windows over Slices

Difficulty: 2 Level: Intermediate Iterate over every overlapping N-element sub-slice โ€” zero-copy, no index arithmetic, no bounds checking.

The Problem This Solves

Many algorithms need to look at a group of consecutive elements together: compute a moving average over the last 3 data points, check if a sequence is strictly sorted, detect local maxima by comparing each point to its neighbors, extract bigrams or trigrams from a word list. Without `windows()`, you'd use index arithmetic โ€” `data[i-1]`, `data[i]`, `data[i+1]` โ€” which requires careful bounds checking and obscures the intent. The sliding window is a fundamental pattern in signal processing, text analysis, and sequence algorithms. Each window overlaps the previous by `n-1` elements, stepping one position forward each time. For a slice of length `L` and window size `n`, you get `L - n + 1` windows. OCaml has no built-in sliding window โ€” you'd write a recursive function or use `List.init` with sublist extraction, allocating new lists at each step. Rust's `windows(n)` is a zero-copy method on slices that returns slice references into the original data.

The Intuition

`windows(n)` produces sub-slices of exactly `n` consecutive elements, advancing one step at a time. Every element except the first and last `n-1` appears in multiple windows.
data = [1, 2, 3, 4, 5]    windows(3):
      [1, 2, 3]
         [2, 3, 4]
            [3, 4, 5]
Each window is a `&[T]` โ€” a borrowed view into the original slice. No allocation, no copying. Passing a window to `.iter().sum()` or indexing `w[0]`, `w[1]` works as you'd expect.

How It Works in Rust

let data = [1i32, 2, 3, 4, 5];

// Moving average (window size 3)
let moving_avg: Vec<f64> = data.windows(3)
 .map(|w| w.iter().sum::<i32>() as f64 / 3.0)
 .collect();
// โ†’ [2.0, 3.0, 4.0]

// Check if strictly increasing โ€” compare each adjacent pair
let is_increasing = data.windows(2).all(|w| w[0] < w[1]);
// โ†’ true

// Find local maxima: center element greater than both neighbors
let signal = [1i32, 3, 2, 5, 4, 6, 2];
let local_max: Vec<usize> = signal.windows(3)
 .enumerate()
 .filter(|(_, w)| w[1] > w[0] && w[1] > w[2])  // middle > both sides
 .map(|(i, _)| i + 1)                            // i is index of w[0]; center is i+1
 .collect();
// โ†’ [1, 3, 5]

// Bigrams from a word list
let words = ["the", "quick", "brown", "fox"];
let bigrams: Vec<_> = words.windows(2).collect();
// โ†’ [["the","quick"], ["quick","brown"], ["brown","fox"]]
`windows()` is a slice method โ€” call it on `&[T]` or arrays. Window size must be > 0 (panics otherwise). For non-overlapping chunks, use `chunks(n)` instead.

What This Unlocks

Key Differences

ConceptOCamlRust
Sliding windowManual recursion or `List.init``slice.windows(n)` โ€” built in
MemoryAllocates new lists per windowZero-copy โ€” references into original slice
OverlappingMust implement manuallyBuilt-in behavior
Non-overlappingMust implement manually`chunks(n)` instead
Works on iteratorsN/ASlice method only โ€” collect to `Vec` first if needed
//! 262. Sliding windows over slices
//!
//! `windows(n)` yields overlapping sub-slices of length `n`, zero-copy.

fn main() {
    let data = [1i32, 2, 3, 4, 5];

    println!("Windows of 3:");
    for w in data.windows(3) {
        println!("  {:?}", w);
    }

    let window_size = 3;
    let moving_avg: Vec<f64> = data.windows(window_size)
        .map(|w| w.iter().sum::<i32>() as f64 / window_size as f64)
        .collect();
    println!("Moving averages (k=3): {:?}", moving_avg);

    let is_increasing = data.windows(2).all(|w| w[0] < w[1]);
    println!("Strictly increasing: {}", is_increasing);

    let signal = [1i32, 3, 2, 5, 4, 6, 2];
    let local_max: Vec<usize> = signal.windows(3)
        .enumerate()
        .filter(|(_, w)| w[1] > w[0] && w[1] > w[2])
        .map(|(i, _)| i + 1)
        .collect();
    println!("Local maxima at indices: {:?}", local_max);

    let words = ["the", "quick", "brown", "fox"];
    let bigrams: Vec<_> = words.windows(2).collect();
    println!("Bigrams: {:?}", bigrams);
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_windows_count() {
        let data = [1i32, 2, 3, 4, 5];
        assert_eq!(data.windows(3).count(), 3);
    }

    #[test]
    fn test_windows_moving_avg() {
        let data = [1i32, 2, 3, 4, 5];
        let avgs: Vec<f64> = data.windows(2)
            .map(|w| w.iter().sum::<i32>() as f64 / 2.0)
            .collect();
        assert_eq!(avgs, vec![1.5, 2.5, 3.5, 4.5]);
    }

    #[test]
    fn test_windows_sorted_check() {
        let sorted = [1i32, 2, 3, 4];
        assert!(sorted.windows(2).all(|w| w[0] <= w[1]));
        let unsorted = [1i32, 3, 2, 4];
        assert!(!unsorted.windows(2).all(|w| w[0] <= w[1]));
    }
}
(* 262. Sliding windows over slices - OCaml *)

let windows n arr =
  let len = Array.length arr in
  if n > len then [||]
  else Array.init (len - n + 1) (fun i -> Array.sub arr i n)

let () =
  let arr = [|1; 2; 3; 4; 5|] in
  let ws = windows 3 arr in
  Printf.printf "Windows of 3:\n";
  Array.iter (fun w ->
    Printf.printf "[%s]\n"
      (String.concat "; " (Array.to_list (Array.map string_of_int w)))
  ) ws;

  let k = 3 in
  let avgs = Array.map (fun window ->
    let sum = Array.fold_left (+) 0 window in
    float_of_int sum /. float_of_int k
  ) (windows k arr) in
  Printf.printf "Moving averages: %s\n"
    (String.concat ", "
      (Array.to_list (Array.map (Printf.sprintf "%.1f") avgs)));

  let pairs = windows 2 arr in
  let increasing = Array.for_all (fun w -> w.(0) < w.(1)) pairs in
  Printf.printf "Monotonically increasing: %b\n" increasing