Higher Order Functions

CSCI-UA.0480-008

Abstractions

Abstraction is:

  • the process of hiding away necessary, but immaterial details
  • … to allow for a programmer to work more closely with the problem that is actually being solved


For example:

  • we abstract away the underlying machine when we use high-level programming languages, like JavaScript and Python
  • whereas low-level languages are tied closely to the architecture and organization of the machine that you're working on

Abstraction Continued

What are some constructs for abstraction in Java?

  • Classes (there's even a keyword, abstractwhat does that do?)
  • Interfaces
  • Methods / Functions

Higher Order Functions as Constructs for Abstractions

A higher order function is a function that does at least one of the following:

  • takes a function or functions as a parameter (input)
  • returns a function

You Might Know These Higher Order Functions From…

In languages where functions are first class citizens it's common to have some implementation of the following higher order functions that work with arrays:

  • foreach - calls a function for every element in an array (no return value)
  • map - calls a function on every element in an array to return a new array composed of those elements
  • filter - creates and returns a new array composed of only elements that pass a supplied test function
  • reduce - reduces an array to a single value, and returns that value


Are you familiar with these functions?

  • array_walk, array_map, array_filter, array_reduce in PHP
  • for in, map, filter, reduce in Python

We Can Implement All of These Higher Order Functions in JavaScript

(Pssssst. They actually all exist as array methods, but let's try exploring their implementations.)

First, let's see why we would use these functions

(think expressiveness, abstraction)

Array Traversal / Do Something With Every Element

Write a quick loop. For every element in the following array, print it out.

var numbers = [1, 2, 3];


for (var i = 0; i < numbers.length; i++) {
	var current = numbers[i];
	console.log(current); 
}

Seems Pretty Conventional

Easy, but… for code that just prints out every element, we have to do a bunch of extra things that / deal with some practical, yet immaterial details… like: →

  • use a counter variable i
  • continually check against the array's length
  • pick out / index the current element


What are some places where we could accidentally make a mistake?

  • inadvertently reuse the i variable
  • misspell lenght
  • confuse the i and current variables (are we looking at the index or the actual current element!)
  • (I know… a for loop isn't that difficult to deal with)

Let's Create a Function

One simple abstraction is to hide the details of the iteration by creating a function called logEach.

  • it'll take an array as a parameter
  • … and just print out every element

var numbers = [1, 2, 3];
function logEach(arr) {
	for (var i = 0; i < arr.length; i++) {
		console.log(arr[i]); 
	}
}
logEach(numbers);

Ok, the logEach Function was Pretty Straightforward

But… what if we want to perform a different action instead of printing out each element? We could:

  • create a function for each action that we want to perform
  • abstract out the action so that it's passed in as a parameter

Our Implementation of forEach

Create a function called forEach.

  • it'll take two parameters, the array, arr, and some sort of function, action
  • it will run the function for every element in the array

var numbers = [1, 2, 3];
function forEach(arr, action) {
	for (var i = 0; i < arr.length; i++) {
		action(arr[i]); 
	}
}
// instead of just logging each number as is, log the square of ever number
forEach(numbers, function(arrayElement) { console.log(arrayElement * arrayElement)});

Great We've Just Implemented ForEach!

What are some advantages?

  • no indexing, convenient, eh?
  • reads more clearly / like natural language


Of course, JavaScript already has a forEach implementation:

  • simply a method called on an array object
  • works off of the elements of the object it's called on, so it only needs one parameter, the callback
  • callback is executed with element value, element index and original array object
  • doesn't return anything (gives back undefined)

generateCards

Write a function that creates a deck of cards. The function will

  • generate and return an array of card objects
  • each card object has a suit (♠, ♥, ♦, ♣) and a face (the strings '2' .. '10', 'J', 'Q', 'K', 'A')
  • the 52 resulting objects should represent every combination of suit and face
  • example list with two card objects: [{ suit: '♣', face: '2' }, { suit: '♦', face: '6' } ]
  • example usage: var cards = generateCards()

generateCards with Standard For Loop


var generateCards = function() {
	var suits = ['♠','♥','♦','♣'],
        faces = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A'],
        cards = [];
	for(var i = 0; i < suits.length; i++) {
		for(var j = 0; j < faces.length; j++) {
			cards.push({'suit':suits[i], 'face':faces[j]}); 
		}
	}
	return cards;
};
console.log(generateCards());

generateCards with forEach

Perhaps slightly more expressive: only dealing with suit and face in the forEach loops… →


var generateCards = function() {
	var suits = ['♠','♥','♦','♣'],
        faces = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A'],
        cards = [];
	suits.forEach(function(suit) {
		faces.forEach(function(face) {
			cards.push({'suit':suit, 'face':face}); 
		});
	});
	return cards;
};
console.log(generateCards());

A Few Other Notes on Both Implementations

  • all variables are declared at the top of the function (why? →) …to avoid ambiguities when a variable declaration is hoisted
  • the formatting of the variable declarations is one per line, indented… in a single statement
  • indentation for both nested for loops and nested forEach calls greatly helps readability
  • either function works fine!
    • forEach - more expressive / reads more naturally, less complexity
    • for - slightly faster

Filtering an Array

Create a function called filter that filters an array by:

  • taking an array as one argument and a function, a test, as the other
  • it should return an entirely new array populated with only the elements that pass the test
  • test by using it to keep only the odd numbers in an array of numbers

function filter(arr, test) {
	var filtered = [];
	arr.forEach(function(element) {
		if(test(element)) {
			filtered.push(element)
		}
	});
	return filtered;
}
result = filter([1, 2, 3, 4], function(x) { 
	return x % 2 == 1;
});
console.log(result);

Using Filter!

Again, JavaScript arrays already have a filter method:

  • an array method, so only one parameter, a callback function (the test) … no need to pass in the array
  • callback is executed with element value, element index and original array object
  • callback returns either true or false
  • filter returns a new filtered array


Try using it to filter our deck of cards so that we only have cards that have a numeric value that's less than or equal to 3


var deck = generateCards();
var filtered = deck.filter(function(card) {
	return parseInt(card.face, 10) <= 3;
});
console.log(filtered);