262: Sliding Windows over Slices
Functional Programming
Tutorial
The Problem
Signal processing, time-series analysis, and pattern recognition algorithms frequently examine overlapping subsequences of fixed length. A moving average over stock prices, an n-gram language model, or detecting whether an array is sorted — all require looking at consecutive groups of elements simultaneously. The windows(n) method provides zero-copy, overlapping sub-slices of a fixed length, making these algorithms expressible as simple iterator pipelines.
🎯 Learning Outcomes
windows(n) yields overlapping sub-slices of length nwindows() from chunks(): overlapping vs non-overlappingwindows()Code Example
#![allow(clippy::all)]
//! 262. Sliding windows over slices
//!
//! `windows(n)` yields overlapping sub-slices of length `n`, zero-copy.
#[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]));
}
}Key Differences
windows() yields borrowed &[T] slices with no allocation; OCaml's equivalent creates new sub-lists.windows(n) is overlapping; chunks(n) is non-overlapping — two complementary methods in Rust.windows() is a stable part of Rust's slice API; OCaml requires user-defined functions.OCaml Approach
OCaml lacks a standard windows function on lists. The idiomatic functional approach uses List.init and List.sub on arrays, or defines a recursive function that takes the head as a sub-list:
let windows n lst =
let arr = Array.of_list lst in
Array.init (Array.length arr - n + 1) (fun i ->
Array.to_list (Array.sub arr i n))
This allocates new sub-arrays; Rust's windows() avoids this via slice references.
Full Source
#![allow(clippy::all)]
//! 262. Sliding windows over slices
//!
//! `windows(n)` yields overlapping sub-slices of length `n`, zero-copy.
#[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]));
}
}
✓ Tests
Rust test suite
#[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]));
}
}
Exercises
windows(3) to compute a 3-element moving average over a Vec<f64> of temperature readings.windows(3).windows() and position().