R Programming By Example
上QQ阅读APP看书,第一时间看更新

Functions as arguments

Sometimes when you want to generalize functions, you may want to plug in a certain functionality into a function. You can do that in various ways. For example, you may use conditionals, as we will see in the following section in this chapter, to provide them with different functionality based on context. However, conditional should be avoided when possible because they can introduce unnecessary complexity into our code. A better solution would be to pass a function as a parameter which will be called when appropriate, and if we want to change how a function behaves, we can change the function we're passing through for a specific task.

That may sound complicated, but in reality, it's very simple. Let's start by creating a l1_norm() function that calculates the distance between two vectors but uses the sum of absolute differences among corresponding coordinates instead of the sum of squared differences as our l2_norm() function does. For more information, take a look at the Taxicab geometry article on Wikipedia (https://en.wikipedia.org/wiki/Taxicab_geometry).

Note that we use the same signature for our two functions, meaning that both receive the same required as well as optional arguments, which are x and y in this case. This is important because if we want to change the behavior by switching functions, we must make sure they are able to work with the same inputs, otherwise, we may get unexpected results or even errors:

l1_norm <- function(x, y = 0) sum(abs(x - y))

l1_norm(a)
#> [1] 6
l1_norm(a, b)
#> [1] 9

Now that our l2_norm() and l1_norm() are built so that they can be switched among themselves to provide different behavior, we will create a third distance() function, which will take the two vectors as arguments, but will also receive a norm argument, which will contain the function we want to use to calculate the distance.

Note that we are specifying that we want to use the l2_norm() by default in case there's no explicit selection when calling the function, and to do so we simply specify the symbol that contains the function object, without parenthesis. Finally note, that if we want to avoid sending the y vector, but we want to specify what norm should be used, then we must pass it through as a named argument, otherwise R would interpret the second argument as the y vector, not the norm function:

distance <- function(x, y = 0, norm = l2_norm) norm(x, y)

distance(a)
#> [1] 14
distance(a, b)
#> [1] 27
distance(a, b, l2_norm)
#> [1] 27
distance(a, b, l1_norm)
#> [1] 9
distance(a, norm = l1_norm)
#> [1] 6