.. raw:: html .. _FP7: .. raw:: html .. |--| unicode:: U+2013 .. en dash .. |---| unicode:: U+2014 .. em dash, trimming surrounding whitespace :trim: .. odsalink:: AV/PL/FP/FP7CON.css .. This file is part of the OpenDSA eTextbook project. See .. http://opendsa.org for more details. .. Copyright (c) 2012-2020 by the OpenDSA Project Contributors, and .. distributed under an MIT open source license. .. avmetadata:: :author: David Furcy and Tom Naps ====================================================================== Procedural Abstraction: The Filtering and Folding (or Reduce) Patterns ====================================================================== The Filtering Pattern --------------------- The filtering (or simply, filter) pattern is exemplified below. In the example on the right, JavaScript's mod operator **%** is used to test whether or not an integer is even. .. inlineav:: FP7Code1CON ss :points: 0.0 :required: False :threshold: 1.0 :long_name: Illustrate Filtering Pattern :output: show Note that, with **curry**, we could use the **filter** function to actually create the functions **keepPositive** and **keepEven**. :: > var keepPositive = fp.curry(filter)(function (n) { return fp.isGT(n,0); }); > var keepEven = fp.curry(filter)(function (n) { return fp.isZero(n % 2); }); This illustrates the function-creating powers of currying. It allows us to not only replicate the behavior of individual functions such as **keepPositive** and **keepEven**, but also to replicate the functions themselves without ever having to write their code. The first problem below deals with the filtering pattern. .. avembed:: Exercises/PL/Filter.html ka :module: FP7 :points: 1.0 :required: True :threshold: 1 :exer_opts: JXOP-debug=true&JOP-lang=en&JXOP-code=pseudo :long_name: Filtering Pattern The Folding/Reduce Pattern -------------------------- To discover our next pattern, recall that, in the section :ref:`reverse`, we used a helper function with an accumulator to perform a **cons** operation prior to making a recursive call instead of after returning from a recursive call. Keep that in mind as you look for the commonality in the following two examples. .. inlineav:: FP7Code2CON ss :points: 0.0 :required: False :threshold: 1.0 :long_name: Illustrate Reduce/Folding Pattern :output: show The *reduce* function we have defined in the previous examples applies its helper function *f* to produce the accumulated value *acc* in left-to-right order as it works through the list. Moreover, it does this in what is known as *tail-recursive* fashion because the helper function *f* is applied within the arguments to the recursive call, not to the result being returned from the recursive call. This left-to-right order was essential for the definition of *reverse* to work correctly. For *sum*, it was inconsequential because of the commutative property of addition. We will return to a more extensive discussion of tail-recursive functions in the `section on continuation passing style`_. .. _section on continuation passing style: FP9.html We could also define a similar function that applies the helper function in right-to-left order as it works through the list. This is illustrated in the next set of examples. .. inlineav:: FP7Code3CON ss :points: 0.0 :required: False :threshold: 1.0 :long_name: Illustrate ReduceRight Pattern :output: show Note that the *reduceRight* function expects the function we pass in for the operation that "accumulates" values to have its first parameter represent the head of the list, that is, the "next" value to be accumulated. Its second parameter is consequently the accumulator. This is the opposite of *reduce*, which expected a function that had the first parameter playing the role of the accumulator, and its second being the "next value to be accumulated". This emphasizes the right versus left associativity of the two patterns. Also note that, because *reduce* captures the accumulation pattern as we described it in `the section on helper functions`_, the accumulator has undergone all the computations necessary to achieve the final answer by the time the end of the list is reached. This is not true of *reduceRight* since it applies its function parameter *f* to the result of making the recursive call instead of applying recursion to the result of applying *f*. The *acc* parameter that *reduce* takes is a true accumulator, which builds up its values on the descent into recursion, so that all the necessary computation is completed by the time the base case is reached. The *acc* parameter that *reduceRight* takes is merely a starting point for the accumulation of values that must be computed as we recursively ascend from reaching the base level. .. _the section on helper functions: FP4.html The following problem deals with the reduce patterns described above. .. avembed:: Exercises/PL/Reduce1.html ka :module: FP7 :points: 1.0 :required: True :threshold: 1 :exer_opts: JXOP-debug=true&JOP-lang=en&JXOP-code=pseudo :long_name: Reduce 1 Practice with the Mapping and Reduce Patterns --------------------------------------------- The following problem uses both the mapping and the reduce patterns. .. avembed:: Exercises/PL/Reduce2.html ka :module: FP7 :points: 1.0 :required: True :threshold: 1 :exer_opts: JXOP-debug=true&JOP-lang=en&JXOP-code=pseudo :long_name: Reduce and Map More Practice with the Reduce Pattern ------------------------------------- The following problem will give you intensive practice with the reduce pattern. This problem is randomized and must be solved three times in a row. .. avembed:: Exercises/PL/Reduce3.html ka :module: FP7 :points: 1.0 :required: True :threshold: 3 :exer_opts: JXOP-debug=true&JOP-lang=en&JXOP-code=pseudo :long_name: Reduce 3 .. odsascript:: AV/PL/FP/FP7Code1CON.js .. odsascript:: AV/PL/FP/FP7Code2CON.js .. odsascript:: AV/PL/FP/FP7Code3CON.js