A Note on Looping

 

Although traditional looping constructs are fully supported, SPL is a series based language. As already indicated, simpler and much faster routines usually result by relying on this very powerful capability. Consider a routine to remove the average value from a series. A traditional approach might resemble the following:

 

/* slow way to remove the mean */

demean1(s)

{

    local outser[], n, j;

 

    n = length(s);

 

    for (j = 1; j <= n; j++)

    {

        outser[j] = s[j] mean(s);

    }

 

    return(outser);

}

 

We can improve on this by pre-allocating outser, saving the mean value so it is only calculated once and using the faster loop iterator:

 

/* a slightly faster way to remove the mean */

demean2(s)

{

    local outser[length(s)], n, mn, j;

 

    n  = length(s);

    mn = mean(s);

 

    loop (j = 1..n)

    {

        outser[j] = s[j] - mn;

    }

 

    return(outser);

}

 

However, here’s a much better method using the power of SPL:

 

/* much faster and simpler de-mean algorithm */

demean3(s)

{

    return(s - mean(s));

}

 

Because SPL is series based, the expression s - mean(s) is automatically applied to each element of series s. The above function is much easier to understand if we think of the input as an entire series rather then an array consisting of individual numeric elements.

 

To create a sinwave, the loop oriented approach would resemble:

/* loop to create a 10 Hz sinewave */

y = {};

t = 0..0.01..1;

 

loop (n = 1..101)

{

    y[n] = sin(2*pi*10*t[n]);

}

 

This can be performed much faster, more intuitively and more concise with:

/* faster sine */

t = 0..0.01..1;

y = sin(2*pi*10*t);

 

And, even faster with:

/* fastest sine */

y = gsin(101, 0.01, 10);

 

Sometimes applying the series processing capability of SPL may not be entirely obvious, particularly to traditional, loop oriented programmers. A loop approach to replacing the values of a series that exceed a certain threshold could be written as:

 

/* replace elements > thresh with val */

replacewith1(s, thresh, val)

{

    local n, outser, j;

 

    n = length(s);

    outser = s;

 

    for (j = 1; j <= n; j++)

    {

        if (outser[j] > thresh)

        {

            outser[j] = val;

        }

    }

 

    return(outser);

}

 

A much faster routine using binary series can be derived from the replace function discussed earlier:

 

/* fast replace using a binary series */

replacewith2(s, thresh, val)

{

    local cond, result;

 

    cond = s > thresh;

 

    result = s * not(cond) + val * cond;

 

    return(result);

}

 

As a final example, consider a function that replaces outlier values (i.e. values that do not meet a specified condition) with a straight line interpolation of the previous non-outlier value and the next non-outlier value. The traditional approach would require several nested loops and rather complicated logical checks and calculations. By clever use of built-in functions, the SPL solution is fast and elegant.

 

/* outlier linear interpolation replacement */

outlier(s, cond)

{

    return(xyinterp(delete(xvals(s), cond), delete(s, cond)))

}

 

Now, outlier(w1, w1 > .1) replaces all the elements of W1 that exceed 0.1 by the linear interpolation of the previous and next non-outlier values. An XY series is formed by removing the Y value and corresponding X value from the series. The linear interpolation is accomplished with the built-in function xyinterp. Any number of outliers can occur in succession and the condition that specifies an outlier value can be quite general. The function is fast, general purpose and does not require looping.

 

The message here is clear; for fast and clean SPL functions, use the automatic series processing capabilities and avoid looping through each element of an input series.