This is the P2PU Archive. If you want the current site, go to www.p2pu.org!

Javascript: 101

My recent threads

You haven't posted any discussions yet.

Recently updated threads

Week 3 Track 2 Help

Go back to: General discussion

I was hoping to move on to Week 4, but I can't seem to wrap my head around some of the functions describes in Eloquent Javascript.

Specifically the reduce function:

function reduce(combine, base, array) {
  forEach(array, function (element) {
    base = combine(base, element);
  });
  return base;
}

Without understanding this, I can't seem to move on to the rest of the excercises. I was able to do the first exercise creating a function countZeroes without using the reduce function, but the rest of the chapter uses it constantly.

-Eric

Thomas Armstrong's picture
Thomas Armstrong
Fri, 2011-02-18 02:21

Hey Eric,

I hear you - I'm in a similar boat, and I think a few people might be. Functional programming is quite a different paradigm to the way I'm used to thinking of programs ("computer, do THIS, then THIS, then THIS...")

One thing that has helped me a little is reading further about functional programming, and what makes it different to other programming paradigms. There are some good blog posts out there on the wider Internet that help it make a little more sense.

Here's one:

http://www.joelonsoftware.com/items/2006/08/01.html

I still find it tricky to "read" and conceptualise programs of this style, though. Parag or anyone more experienced have any advice? A useful mental model?

Cheers,
Tom

Parag Shah's picture
Parag Shah
Fri, 2011-02-18 08:00

Hi Tom,

One difference that you will notice in the functional programming paradigm is providing functions as arguments to other functions.

In the non functional paradigm, we normally give values to functions. The functions do some computation or cause some side effect. But in functional programming we do not restrict ourselves to providing only values to functions. We also provide other functions.

As an example, it is very common in software to have an array and to perform some computation on every element of that array.

In a non functional paradigm every time we wanted to do something like this we would create a different function.

If we need to compute the sum of all values in a array:

function sum(arrOfValues) {
var result = 0;
for(var i = 0; i < arrOfValues.length; i++) {
result += arrOfValues[i];
}
return result;
}

Now let's say somewhere else in the program we have a need to compute the product value. Now we will write another function:

function product(arrOfValues) {
var result = 0;
for(var i = 0; i < arrOfValues.length; i++) {
result *= arrOfValues[i];
}
return result;
}

But in functional programming because functions are first class objects and can be passed to other functions, we can abstract this in a more powerful way.

We can say. Let me create a function which will take an array, and another function, which will be called in every element of the array. This is the forEach function. Once we have written the for each function, our code becomes more concise and readable.

var fsum = function(base, element) {
return base + element;
}

var fproduct = function(base, element) {
return base * element;
}

//to get the sum
var finalSum = forEach(fsum, 0, arrayOfValues);
var finalProduc = forEach(fproduct, 1, arrayOfValues);

I am not sure if this is the best explanation, but I am hoping it will help make functional programming a bit clearer.

--
Regards
Parag

Thomas Armstrong's picture
Thomas Armstrong
Fri, 2011-02-18 06:51

I just realised I could have given you an explanation of the "reduce" function, and should have.

Basically, according to my understanding, this function "folds" or "reduces" an array into a single value. That is, it takes a bunch of things and turns them into one thing. To do that, it takes three arguments: the array of things that you want to "reduce" ('array'), the starting value of the "one thing" that you want to collapse them into ('base'), and the tricky part: 'combine', which is a function that tells reduce HOW you want to do it.

It uses the "forEach" function to go over the array, and for every value in the array it calls an anonymous function that sets the variable 'base' to whatever the function provided as the 'combine' argument returns when provided with the current 'base' and that particular value in the array, as arguments.

I guess you could say "reduce" says this: "For Each element in Array, give that element and the current Base to the Combine function. Whatever Combine returns back, make that the new Base, and repeat."

"Combine" could be any number of things: I think the first example in the textbook uses as it's Combine argument a function that just adds two arguments together, but it could just as easily be a function that subtracts one element from another (ie. subtracts 'element' from 'base' for example).

I hope that clarifies stuff a bit. I'm not sure it does :P
Like I said, I'm still struggling with it myself.

Parag Shah's picture
Parag Shah
Fri, 2011-02-18 07:42

Hi Tom,

That was a really nice explanation of the reduce function. Allow me to also present an analogy.

Reduce is actually part of a larger concept called Map-Reduce. It is used when you have to perform some very large computation which can be broken down into smaller computations and then combined to get to the final answer.

As an example, let's say a country had to do a national census. This is a large job. So they would 'map' it into smaller units by appointing representatives in each state and asking them to do the census of their state. Once all the state representatives complete their individual census, we have an array of values (census of each state) which can be combined ('reduce') to get the final answer (census of the nation).

If we want to compute the total:
array - [state_1_total, state_2_total, state_3_total]
base - 0
combine - will add the element it gets to the base value

If we wanted to compute the median or average then we would have a different combine function.

I will annotate the reduce function with comments

/**
* @param combine A function which accepts an element and a base value. It will perform some computation with these
* values and return a new value
* @param array An array of values
* @base The starting value to be used by each invocation of the combine function. Note that this value will change for
each invocation of combine
* This function takes an array, a base value, and a function. It iterates through the array and invokes the function
* 'combine' for every value of the array. Every time this function is invoked, it is given an element from the array
* and a base value. It performs some computation and returns a value, which will be used as the base value for the
* next invocation of the 'combine' function.
*/
function reduce(combine, base, array) {
//for each element in the array, we invoke an anonymous function which in turn invokes combine and holds the return
//value of combine for the next invocation
forEach(array, function (element) {
base = combine(base, element);
});
//since bae holds the cumulative value of all invocations of combine, we return base which will contain the final
//result
return base;
}

----------------------------------------------------------------------------------------

Here is a more code for our census example

var stateTotals = getStateCensusTotals();
var nationalTotal = reduce(combine, 0, stateTotals);
alert("national census total " + nationalTotal);

var combine = function(base element) {
return base + element;
}

function getStateCensusTotals() {
//compute census for each state and return the result in an array
}

Nick's picture
Nick
Fri, 2011-02-18 14:56

Thanks both for the helpful explanation.

I too am wrapped around the axle when trying to make sense of functions as arguments to functions. Actually, that part makes sense to me. It's the scoping that really has me still scratching my head. i.e. that variables continue to exist even after the function has returned and those variables can be used by other functions.

I'm struggling to understand the rules for when and how variables can be used/passed, etc. In one sense it almost seems like they can be passed anywhere and everywhere, which starts to make Crockford's comment about "more expressive power" make sense, but it also seems like it's both dangerous (your variables could step on each other if you're not careful with naming) and also makes it difficult to follow the flow of a program.

Oh, and one more thing. I can't get many of the examples in Chapter 6 to run in the console on the Eloquent Javascript site. e.g. the block of code right before Exercise 6.1. I've refreshed the page and still no luck.

Anyone else having this problem?

n

Parag Shah's picture
Parag Shah
Sat, 2011-02-19 08:16

Hi Nick,

You say "variables continue to exist even after the function has returned and those variables can be used by other functions."

These variables can only be used by nested functions.

function someOuterFunction() {
  var a = 10;
  var func = function() {
    alert(a++);
  }
  return func;
}

In the above example, the variable 'a' continues to exist even after the function 'someOuterFunction' has exited. However, it can only be used/accessed by the nested function 'func', and not by any other function declared outside of 'someOtherFunction'.

Nick's picture
Nick
Sat, 2011-02-19 13:39

Hi Parag,
Thanks for the clarification, that helps.

Are you saying that even though someOuterFunction() has exited the nested function could still be doing some work? It would seem so, since if 'a' continues to exist AND it could only be used by the nested function it implies the nested function is still operative and doing something with 'a'.

That's where I'm struggling - the notion that the outer function could have exited but the inner one is still working. In my mind, it seems like the outer function is always dependent on the inner function to complete its work before it can exit. Or am I missing something completely?

n

Parag Shah's picture
Parag Shah
Sat, 2011-02-19 14:17

Intuitively it is very natural to assume that once the outer function has exited, everything within it will also be cleaned up, and that all inner functions would be done with by the time the outer function exits.

However, closures are different. They are an exception to this intuitive understanding. Actually the intuitive understanding stems from the fact that we are used to thinking in terms of imperative languages.

first do step 1
then do step 2
do nested step 2.a
do nested step 2.b

and so on.

Here we assume that nested 2.a and 2.b must complete for this bundle of steps to complete.

However a closure is not an imperative construct in relation to it's containing function. It is purely a scoping construct. What this means is that there is no linear relationship between a function and it's nested functions in Javascript. In fact you must have noticed that the nested function does not automatically execute when the outer function is invoked. It is simply declared within the scope of the outer function.

The implication of all this is that th scope of the nested function contains all the variables that were declared within the outer function. This scope will continue to exist even after the outer function has exited. For this to happen, the nested function does not need to be operative, but some code somewhere must hold a reference to the nested function. Otherwise it will be deemed as not being used and may be garbage collected.

Does that help make things a bit clearer? The relationship between an outer and inner function is that of scope and not of linear action (as in imperative, happens after or happens before). The outer function creates a scope which the inner function can use at any point of time. At what point of time really depends, on when the code which is holding a reference to the inner function actually executes the inner function.

Nick's picture
Nick
Sat, 2011-02-19 23:03

Thanks for that clarification. It does help. I'm beginning to wrap my brain around it. I think just working with it some more and seeing more code will help me grok it.

Thanks again!
n

Parag Shah's picture
Parag Shah
Fri, 2011-02-18 07:43

oops the formatting got all messed up. Please copy and paste the code in a notepad, to make it more readable.

Eric Anderson's picture
Eric Anderson
Thu, 2011-02-24 18:51

A late reply, but thanks to all for the helpful comments. I finally understand the reduce function, though I still have a hard time understanding all of the uses of it. In time I hope, in time.