Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.
This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.
Background knowledge
- Pure functions and Currization are easy to write onion code
h(g(f(x)))
// Get the last element of the array and convert it to uppercase
// Invert the data first --> take the first element --> convert it to uppercase
_.toUpper(_.first(_.reverse(array)))
Copy the code
Function composition allows us to recombine fine-grained functions to generate a new function without writing onion code
The pipe
a –> fn –> b
a-> f3 -> m -> f2 -> n -> f1 -> b
In fact, we don’t care what the middle m and n are, right
Similar to the following function
fn = compose(f1, f2, f3)
b = fn(a)
Copy the code
-
Compose: If a function needs to be processed by multiple functions to get the final value, you can combine the intermediate functions into a single function
-
Function combinations are executed from right to left by default
// function combination demo
function compose(f, g) {
return function (value) {
return f(g(value))
}
}
// Array flipping function
function reverse (array) {
return array.reverse()
}
// Get the first element of the function function
function first (array) {
return array[0]}// Get the last element of the function
const last = compose(first, reverse)
console.log(last([1.2.3.4])) / / 4
Copy the code
Combined functions in Lodash — flow()/flowRight()
Lodash combines the flow() or flowRight() functions, both of which can combine multiple functions.
flow()
It runs from left to rightflowRight()
It runs from right to left, it uses a little bit more
The following example takes the last element of an array and converts it to uppercase
const _ = require('lodash')
const reverse = arr= > arr.reverse()
const first = arr= > arr[0]
const toUpper = s= > s.toUpperCase()
const f = _.flowRight(toUpper, first, reverse)
console.log(f(['one'.'two'.'three'])) // THREE
Copy the code
Function combination principle simulation
Let’s analyze the above example:
The input parameter is not fixed, the argument is a function, the output parameter is a function, the function must have an initial parameter value
function compose (. args) {
// Returns a function that takes an initial argument, value
return function (value) {
/ /... Args is an array of functions that are executed, executed from right to left so that array is reversed
// reduce: For each element in the array, execute one of our provided functions and aggregate it into a single result
// The first argument to reduce is a callback function, and the second argument is the initial value of acc, which is value
The reduce callback takes two arguments. The first argument is the result of the summary and the second argument is the function that processes the summary and returns a new value
// fn refers to each element (function) in the array to process acc, after which the next array element processes the result of the previous array acc
return args.reverse().reduce(function (acc, fn) {
return fn(acc)
}, value)
}
}
//test
const fTest = compose(toUpper, first, reverse)
console.log(fTest(['one'.'two'.'three'])) // THREE
// ES6 (functions become arrow functions)
const compose = (. args) = > value= > args.reverse().reduce((acc, fn) = > fn(acc), value)
Copy the code
Combination of functions – associative law
What is associative law of function combinations?
The following three cases have the same result, we can combine g with h, or we can combine f with g.
// Associativity
let f = compose(f, g, h)
let associative = compose(compose(f, g), h) == compose(f, compose(g, h))
// true
Copy the code
Let’s use the previous example to elaborate:
const _ = require('lodash')
/ / way
const f = _.flowRight(_.toUpper, _.first, _.reverse)
2 / / way
const f = _.flowRight(_.flowRight(_.toUpper, _.first), _.reverse)
3 / / way
const f = _.flowRight(_.toUpper, _.flowRight(_.first, _.reverse))
// Print the same result as THREE
console.log(f(['one'.'two'.'three'])) // THREE
Copy the code
Function composition – debug
If we run results that are not what we expect, how do we debug? How do we know the outcome of the intermediate run?
NEVER SAY DIE corresponds to nerver-say-die
Note: each time you write the parameters you add to the front, the value passed to the back
const _ = require('lodash')
// The split function takes two arguments, and the last time we call it we pass a string, so the string is passed in the second position, so we need to wrap a split function ourselves
// _.split(string, separator)
// Convert multiple arguments to a single argument, using the currization of the function
const split = _.curry((sep, str) = > _.split(str, sep))
Use toLower(). Since this function has only one argument, it can be used directly in function combinations
The first parameter is an array, the second parameter is a delimiter, and the array is also passed last
const join = _.curry((sep, array) = > _.join(array, sep))
const f = _.flowRight(join(The '-'), _.toLower, split(' '))
console.log(f('NEVER SAY DIE')) //n-e-v-e-r-,-s-a-y-,-d-i-e
Copy the code
But the end result is not what we want, so how do we debug?
// NEVER SAY DIE --> nerver-say-die
const _ = require('lodash')
const split = _.curry((sep, str) = > _.split(str, sep))
const join = _.curry((sep, array) = > _.join(array, sep))
// We need to print the intermediate value and know its position
const log = _.curry((tag, v) = > {
console.log(tag, v)
return v
})
// Add a log to each function from right to left, and pass in the value of the tag
const f = _.flowRight(join(The '-'), log('after toLower:'), _.toLower, log('after split:'), split(' '))
// From right to left
// first log: after split: ['NEVER', 'SAY', 'DIE'
// The second log: after toLower: never,say,die is converted to a string
console.log(f('NEVER SAY DIE')) //n-e-v-e-r-,-s-a-y-,-d-i-e
// Use the map method of the array to iterate over each element of the array and make it lowercase
// Map takes two arguments, the first is an array, and the second is a callback function, which needs to be currified
const map = _.curry((fn, array) = > _.map(array, fn))
const f1 = _.flowRight(join(The '-'), map(_.toLower), split(' '))
console.log(f1('NEVER SAY DIE')) // never-say-die
Copy the code