266: Striding with step_by()
Functional Programming
Tutorial
The Problem
Subsampling — taking every nth element from a sequence — is needed in signal processing (downsampling), matrix operations (accessing column strides), image processing (pixel subsampling), and data analysis (sampling large datasets). Without a dedicated combinator, this requires index-based loops or manual counter tracking. The step_by(n) adapter yields the first element, then skips n-1 elements, repeatedly — turning strided access into a composable iterator operation.
🎯 Learning Outcomes
step_by(n) yields the first element then every nth subsequent elementstep_by() with ranges to generate arithmetic progressionsstep_by() with other adapters for strided processing pipelinesstep_by() and array stride concepts in numerical computingCode Example
#![allow(clippy::all)]
//! 266. Striding with step_by()
//!
//! `step_by(n)` yields every nth element — the first, then skips n-1, and so on.
#[cfg(test)]
mod tests {
#[test]
fn test_step_by_3() {
let result: Vec<usize> = (0..10).step_by(3).collect();
assert_eq!(result, vec![0, 3, 6, 9]);
}
#[test]
fn test_step_by_2() {
let result: Vec<i32> = [1, 2, 3, 4, 5].iter().copied().step_by(2).collect();
assert_eq!(result, vec![1, 3, 5]);
}
#[test]
fn test_step_by_1_identity() {
let result: Vec<i32> = (1..=4).step_by(1).collect();
assert_eq!(result, vec![1, 2, 3, 4]);
}
#[test]
fn test_step_by_multiples() {
let result: Vec<i32> = (0..=20).step_by(5).collect();
assert_eq!(result, vec![0, 5, 10, 15, 20]);
}
}Key Differences
step_by() is built into Rust's Iterator; OCaml requires filteri or manual index tracking.step_by() composes with infinite ranges ((0..).step_by(2)); OCaml needs lazy sequences for this.step_by mirrors the "stride" concept in NumPy arrays (a[::step]), BLAS Level 1 routines (DAXPY stride), and C array pointer arithmetic.step_by skips elements without evaluating them on most iterator types; OCaml's filteri always calls the predicate on every element.OCaml Approach
OCaml lacks a built-in step_by for lists. It is implemented with List.filteri modulo arithmetic or via Array index stepping:
let step_by n lst =
List.filteri (fun i _ -> i mod n = 0) lst
(* Or with sequences *)
let step_by_seq n seq =
Seq.filter_mapi (fun i x -> if i mod n = 0 then Some x else None) seq
Full Source
#![allow(clippy::all)]
//! 266. Striding with step_by()
//!
//! `step_by(n)` yields every nth element — the first, then skips n-1, and so on.
#[cfg(test)]
mod tests {
#[test]
fn test_step_by_3() {
let result: Vec<usize> = (0..10).step_by(3).collect();
assert_eq!(result, vec![0, 3, 6, 9]);
}
#[test]
fn test_step_by_2() {
let result: Vec<i32> = [1, 2, 3, 4, 5].iter().copied().step_by(2).collect();
assert_eq!(result, vec![1, 3, 5]);
}
#[test]
fn test_step_by_1_identity() {
let result: Vec<i32> = (1..=4).step_by(1).collect();
assert_eq!(result, vec![1, 2, 3, 4]);
}
#[test]
fn test_step_by_multiples() {
let result: Vec<i32> = (0..=20).step_by(5).collect();
assert_eq!(result, vec![0, 5, 10, 15, 20]);
}
}
✓ Tests
Rust test suite
#[cfg(test)]
mod tests {
#[test]
fn test_step_by_3() {
let result: Vec<usize> = (0..10).step_by(3).collect();
assert_eq!(result, vec![0, 3, 6, 9]);
}
#[test]
fn test_step_by_2() {
let result: Vec<i32> = [1, 2, 3, 4, 5].iter().copied().step_by(2).collect();
assert_eq!(result, vec![1, 3, 5]);
}
#[test]
fn test_step_by_1_identity() {
let result: Vec<i32> = (1..=4).step_by(1).collect();
assert_eq!(result, vec![1, 2, 3, 4]);
}
#[test]
fn test_step_by_multiples() {
let result: Vec<i32> = (0..=20).step_by(5).collect();
assert_eq!(result, vec![0, 5, 10, 15, 20]);
}
}
Exercises
step_by(2) to extract all elements at odd indices from a slice, and skip(1).step_by(2) to extract elements at even indices.(3i32..).step_by(4).&[f64] with cols columns, extract column k using step_by(cols) with an appropriate skip.