ExamplesBy LevelBy TopicLearning Paths
266 Intermediate

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

  • • Understand that step_by(n) yields the first element then every nth subsequent element
  • • Use step_by() with ranges to generate arithmetic progressions
  • • Combine step_by() with other adapters for strided processing pipelines
  • • Recognize the relationship between step_by() and array stride concepts in numerical computing
  • Code 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

  • Standard library: step_by() is built into Rust's Iterator; OCaml requires filteri or manual index tracking.
  • Infinite sources: Rust's step_by() composes with infinite ranges ((0..).step_by(2)); OCaml needs lazy sequences for this.
  • Numerical computing link: step_by mirrors the "stride" concept in NumPy arrays (a[::step]), BLAS Level 1 routines (DAXPY stride), and C array pointer arithmetic.
  • Efficiency: Rust's 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

  • Use 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.
  • Generate the arithmetic sequence 3, 7, 11, 15, ... (starting at 3 with step 4) using (3i32..).step_by(4).
  • Implement matrix column extraction: given a flat row-major matrix as &[f64] with cols columns, extract column k using step_by(cols) with an appropriate skip.
  • Open Source Repos