The project is in a healthy, maintained state
A tiny Ruby gem to provide the 'scan_left' operation on any Ruby Enumerable.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies
 Project Readme

scan_left

Tests

Yard Docs Docs Coverage

Gem Version Gem Downloads

A tiny Ruby gem to provide the #scan_left operation on any Ruby Enumerable.

What does it do?

Imagine a series of numbers which you want to sum. You accomplish this by processing all elements, adding each to the previous sum, returning the final result.

Now imagine that, rather than just the final sum at the end of the series, you want a series of the partial sums after processing each element. This is called a "Prefix Sum".

In functional programming (FP), the sum is generalized as the fold operation, in which an initial state and a binary operation are combined to "fold" a series of values into a single result. The closely related prefix sum, which produces a series of intermediate results, is generalized as the scan operation. Adding "left" to these operation names indicates that calculation is to proceed left-to-right.

Compare / Contrast with #inject

Ruby's standard library operation Enumerable#inject implements the FP fold operation. It also implements the simpler reduce operation, which is a fold whose initial state is the first element of the series.

The key differences between #inject and #scan_left are:

  1. Incremental results: #scan_left returns a series of results after processing each element of the input series. #inject returns a single value, which equals the final result in the series returned by #scan_left.

  2. Laziness: #scan_left can preserve the laziness of the input series. As each incremental result is read from the output series, the actual calculation is lazily performed against the input. #inject cannot be a lazy operation in general, as its single result reflects a calculation across every element of the input series.

Examples

require "scan_left"

# For comparison, results from #inject are shown as well:

ScanLeft.new([]).scan_left(0) { |s, x| s + x } == [0]
[].inject(0) { |s, x| s + x }                  == 0

ScanLeft.new([1]).scan_left(0) { |s, x| s + x } == [0, 1]
[1].inject(0) { |s, x| s + x }                  == 1

ScanLeft.new([1, 2, 3]).scan_left(0) { |s, x| s + x } == [0, 1, 3, 6]
[1, 2, 3].inject(0) { |s, x| s + x }                  == 6

# OPTIONAL: To avoid explicitly using the `ScanLeft` class, you may
# choose to use the provided refinement on Enumerable. 
#
# This refinement adds a `#scan_left` method directly to Enumerable 
# for a more concise syntax.

using EnumerableWithScanleft

[].scan_left(0) { |s, x| s + x }        => [0]
[1].scan_left(0) { |s, x| s + x }       => [0, 1]
[1, 2, 3].scan_left(0) { |s, x| s + x } => [0, 1, 3, 6]

Further Reading