2.7. Functional Programming - Procedural Abstraction: The Filtering and Folding (Reduce) Patterns¶
2.7.1. The Filtering Pattern¶
Consider the following two functions with some samples of their usage. First a function called keepPositive.
// Given a list, return a list with the positive numbers
var keepPositive = function (ns) {
    if (fp.isNull(ns))
        return [ ];
    else if (fp.isGT(fp.hd(ns),0))
        return fp.cons(fp.hd(ns),
                       keepPositive(fp.tl(ns)));
    else
        return keepPositive(fp.tl(ns));
}
> keepPositive([-1,1,0,-10,-20,2,3,-30])
[ 1, 2, 3 ]
> censor(["Functions","loops","assignments",
          "and","recursion","are","essential"])
['Functions','and','recursion','are','essential']
Next a function called censor, with its helper isAcceptable.
// Censor out all unacceptable words in a list of strings
var censor = function (sentence) {
    if (fp.isNull(sentence))
        return [ ];
    else if (isAcceptable(fp.hd(sentence)))
        return fp.cons(fp.hd(sentence),
                       censor(fp.tl(sentence)));
    else
        return censor(fp.tl(sentence));
}
var isAcceptable = function (word) {
    if (fp.isMember(word, [ "loops",
                            "assignments",
                            "side effects" ]))
        return false
    else
        return true;
}
> censor(["Functional", "programming", "includes", "functions", "mapping", "loops", "assignments", "side effects", "currying"]);
[ 'Functional', 'programming', 'includes', 'functions', 'mapping', 'currying' ]
In your own words, try to fill in the following sets of ?????. Both the keepPositive and censor functions take in a ????? and return a ????? that is obtained by ?????. Your answers to these questions will have described a pattern called filtering. The following filter function abstracts that pattern.
var filter = function (pred,ns) {
    if (fp.isNull(ns))
        return [ ];
    else if (pred(fp.hd(ns)))
        return fp.cons(fp.hd(ns),
                       filter(pred,fp.tl(ns)));
    else
        return filter(pred,fp.tl(ns));
}
Here are some examples of using the filter function to achieve the same results we obtained previously for keepPositive and censor.
> var keepPositive2 = function (ns)  {
                        return filter(function (n) { return fp.isGT(n,0); }, ns); }
> var keepPositive3 = fp.curry(filter)(function (n) { return fp.isGT(n,0); });
> keepPositive2([-1,1,0,-10,-20,2,3,-30])      // same output as for keepPositive
> keepPositive3([-1,1,0,-10,-20,2,3,-30])      // same output as for keepPositive
> var censor2 = function (words) { return filter(isAcceptable, words); }
> var censor3 = fp.curry(filter)(isAcceptable)
> censor2(["Functions","loops","assignments",    // same output as for censor
          "and","recursion","are","essential"])
> censor3(["Functions","loops","assignments",    // same output as for censor
          "and","recursion","are","essential"])
This first problem deals with the filtering pattern.
2.7.2. Folding/Reducing¶
To discover our next pattern, try to discern what the following sum and count functions have in common.
// Return the sum of all the numbers in a flat list
var sum = function (ns) {
    var helper = function (ns,a) {
        if (fp.isNull(ns))
            return a;
        else
            return helper(fp.tl(ns),
                          fp.add(a,fp.hd(ns)));
    }
    return helper(ns,0);
}
> sum([1,2,3])
6
// Return a count of how many times n occurs in the list ns
var count = function (n,ns) {
  var helper = function (ns,a) {
      if (fp.isNull(ns))
          return a;
      else
          return helper(fp.tl(ns),
                        fp.add(a,
                               fp.isEq(fp.hd(ns),n)
                                    ? 1
                                    : 0));
    }
    return helper(ns,0);
}
> count(-1,[-1,0,1,2,-1,-1])
3
Next, in your own words, try to fill in the following sets of ?????. Both helper functions in sum and count start out with a ????? and a ????? and return a ????? that is obtained by ?????. Your answers to these questions will have described a pattern called folding or reducing*. The following reduce function abstracts that pattern.
var reduce = function (f,ns,acc) {
    if (fp.isNull(ns))
        return acc;
    else
        return reduce(f,fp.tl(ns),f(acc,fp.hd(ns)));
}
Next we show how to use reduce to create the sum and count functions that we previously had to write ourselves.
> var sum2 = function (ns) { return reduce(fp.add,ns,0); }
> var count2 = function (n,ns) {
                     return reduce(function(a,x) {
                                        return fp.add(a,fp.isEq(x,n) ? 1 : 0); },
                                   ns, 0); }
> sum2([1,2,3])                    // same output as with sum
> count2(-1,[-1,0,1,2,-1,-1])      // same output as with count
Following are four additional examples of how the reduce function can be used.
- Here is a non-recursive definition of the factorial function that uses reduce and the fillIn function defined in Section 2.6.
 
var factorial = function (n) { return reduce(fp.mul,fillIn(1,n),1); }
- How would you rewrite the function that takes in a list and returns its length?
 
var length = function (ns) { return reduce( ???, ???, ???); }
- How would you rewrite the function that takes in a list and reverses it?
 
var reverse = function (ns) { return reduce( ???, ???, ???); }
- What does this function do?
 
var figureItOut = function (l1,l2) {
       return reduce( function(a,n) { return fp.cons(n,a); }, l1, l2);
};
Reducing from the right: Whereas the reduce function we have defined applies its helper function f to produce the accumulated value acc in left-to-right order as it works through the list, we could also define a similar function that applied the helper function in right-to-left fashion as it worked through the list.
var reduceRight = function (f,ns,acc) {
    if (fp.isNull(ns))
        return acc;
    else
        return f( fp.hd(ns), reduceRight(f,fp.tl(ns),acc) );
}
So, the expression 1−(2−(3−(4−5))) can be computed as follows:
subtractFromRight([1,2,3,4,5])
if the function substractFromRight is defined as follows (what should the ??? be):
var subtractFromRight = function (ns) { return reduceRight( ??? ,ns,0); }
Simlarly, reduceRight is exactly what we need to build the append function:
var append = function (l1,l2) { return reduceRight(fp.cons, l1, l2); };
This problem deals with the folding pattern.
2.7.3. Folding/Reducing (2)¶
This problem uses both the mapping and the folding patterns.
2.7.4. Folding/Reducing (3)¶
This problem will give you intensive practice with the folding pattern. This problem is randomized and must be solved three times in a row.

