2629. 复合函数
请你编写一个函数,它接收一个函数数组 [f1, f2, f3,…, fn],并返回一个新的函数 fn ,它是函数数组的 复合函数 。
[f(x), g(x), h(x)] 的 复合函数 为 fn(x) = f(g(h(x))) 。
一个空函数列表的 复合函数 是 恒等函数 f(x) = x 。
你可以假设数组中的每个函数接受一个整型参数作为输入,并返回一个整型作为输出。
示例 1:
输入:functions = [x => x + 1, x => x * x, x => 2 * x], x = 4
输出:65
解释:
从右向左计算......
Starting with x = 4.
2 * (4) = 8
(8) * (8) = 64
(64) + 1 = 65
示例 2:
输出:functions = [x => 10 * x, x => 10 * x, x => 10 * x], x = 1
输入:1000
解释:
从右向左计算......
10 * (1) = 10
10 * (10) = 100
10 * (100) = 1000
示例 3:
输入:functions = [], x = 42
输出:42
解释:
空函数列表的复合函数就是恒等函数
思路
其实就是用一个工厂函数,将 functions 作为闭包挂载在工厂返回的新函数上。当执行新函数的时候,反向挨个执行 functions 里存的函数,最后的结果就是想要的答案。
代码一 (常规)
type F = (x: number) => number;
function compose(functions: F[]): F {
return function (x) {
for (let i = functions.length - 1; i >= 0; i--) {
x = functions[i](x);
}
return x;
}
};
/**
* const fn = compose([x => x + 1, x => 2 * x])
* fn(4) // 9
*/
代码二 (reverse + for...of... 遍历)
type F = (x: number) => number;
function compose(functions: F[]): F {
return function (x) {
for (const fun of functions.reverse()) {
x = fun(x);
}
return x;
}
};
代码三 (reduceRight API)
reductRight是 reduce的反向版,从右到左进行遍历。
developer.mozilla.org/zh-CN/docs/…
例如:
const arrays = [
[0, 1],
[2, 3],
[4, 5],
];
const flattened = arrays.reduceRight((arr, num) => arr.concat(num), []);
// flattened 的值是 [4, 5, 2, 3, 0, 1]
arr 为要返回的值,num 为每次遍历的元素。
所以答案与上同理,只是每次遍历的是函数数组里的函数。
type F = (x: number) => number;
function compose(functions: F[]): F {
return function (x) {
return functions.reduceRight((target, fn) => fn(target), x);
}
};
2703. 返回传递的参数的长度
请你编写一个函数 argumentsLength,返回传递给该函数的参数数量。
示例 1:
输入:args = [5]
输出:1
解释:
argumentsLength(5); // 1
只传递了一个值给函数,因此它应返回 1。
示例 2:
输入:args = [{}, null, "3"]
输出:3
解释:
argumentsLength({}, null, "3"); // 3
传递了三个值给函数,因此它应返回 3。
提示:
args是一个有效的 JSON 数组0 <= args.length <= 100
代码
送分题,不说了...
type JSONValue = null | boolean | number | string | JSONValue[] | { [key: string]: JSONValue };
function argumentsLength(...args: JSONValue[]): number {
// 直接返回剩余参数长度
return args.length;
};
/**
* argumentsLength(1, 2, 3); // 3
*/
2666. 只允许一次函数调用
给定一个函数 fn ,它返回一个新的函数,返回的函数与原始函数完全相同,只不过它确保 fn 最多被调用一次。
- 第一次调用返回的函数时,它应该返回与
fn相同的结果。 - 第一次后的每次调用,它应该返回
undefined。
示例 1:
输入:fn = (a,b,c) => (a + b + c), calls = [[1,2,3],[2,3,6]]
输出:[{"calls":1,"value":6}]
解释:
const onceFn = once(fn);
onceFn(1, 2, 3); // 6
onceFn(2, 3, 6); // undefined, fn 没有被调用
示例 2:
输入:fn = (a,b,c) => (a * b * c), calls = [[5,7,4],[2,3,6],[4,6,8]]
输出:[{"calls":1,"value":140}]
解释:
const onceFn = once(fn);
onceFn(5, 7, 4); // 140
onceFn(2, 3, 6); // undefined, fn 没有被调用
onceFn(4, 6, 8); // undefined, fn 没有被调用
提示:
calls是一个有效的 JSON 数组1 <= calls.length <= 101 <= calls[i].length <= 1002 <= JSON.stringify(calls).length <= 1000
思路
在工厂函数返回前声明一个闭包用于判断是否已经被调用过。如果没有被调用过,记录为调用过,然后返回函数执行的结果。如果调用过,直接返回 undefined。
代码
type JSONValue = null | boolean | number | string | JSONValue[] | { [key: string]: JSONValue };
type OnceFn = (...args: JSONValue[]) => JSONValue | undefined
function once(fn: Function): OnceFn {
// 声明一个闭包,用于判断函数是否已经被调用过了
let isCalled = false;
return function (...args) {
if (!isCalled) {
isCalled = true;
return fn(...args);
} else {
return undefined;
}
};
}
/**
* let fn = (a,b,c) => (a + b + c)
* let onceFn = once(fn)
*
* onceFn(1,2,3); // 6
* onceFn(2,3,6); // returns undefined without calling fn
*/
2623. 记忆函数
请你编写一个函数,它接收另一个函数作为输入,并返回该函数的 记忆化 后的结果。
记忆函数 是一个对于相同的输入永远不会被调用两次的函数。相反,它将返回一个缓存值。
你可以假设有 3 个可能的输入函数:sum 、fib 和 factorial 。
sum接收两个整型参数a和b,并返回a + b。fib接收一个整型参数n,如果n <= 1则返回1,否则返回fib (n - 1) + fib (n - 2)。factorial接收一个整型参数n,如果n <= 1则返回1,否则返回factorial(n - 1) * n。
示例 1:
输入:
fnName = "sum"
actions = ["call","call","getCallCount","call","getCallCount"]
values = [[2,2],[2,2],[],[1,2],[]]
输出:[4,4,1,3,2]
解释:
const sum = (a, b) => a + b;
const memoizedSum = memoize(sum);
memoizedSum (2, 2);// "call" - 返回 4。sum() 被调用,因为之前没有使用参数 (2, 2) 调用过。
memoizedSum (2, 2);// "call" - 返回 4。没有调用 sum(),因为前面有相同的输入。
// "getCallCount" - 总调用数: 1
memoizedSum(1、2);// "call" - 返回 3。sum() 被调用,因为之前没有使用参数 (1, 2) 调用过。
// "getCallCount" - 总调用数: 2
示例 2:
输入:
fnName = "factorial"
actions = ["call","call","call","getCallCount","call","getCallCount"]
values = [[2],[3],[2],[],[3],[]]
输出:[2,6,2,2,6,2]
解释:
const factorial = (n) => (n <= 1) ? 1 : (n * factorial(n - 1));
const memoFactorial = memoize(factorial);
memoFactorial(2); // "call" - 返回 2。
memoFactorial(3); // "call" - 返回 6。
memoFactorial(2); // "call" - 返回 2。 没有调用 factorial(),因为前面有相同的输入。
// "getCallCount" - 总调用数:2
memoFactorial(3); // "call" - 返回 6。 没有调用 factorial(),因为前面有相同的输入。
// "getCallCount" - 总调用数:2
示例 3:
输入:
fnName = "fib"
actions = ["call","getCallCount"]
values = [[5],[]]
输出:[8,1]
解释:
fib(5) = 8 // "call"
// "getCallCount" - 总调用数:1
提示:
0 <= a, b <= 10^51 <= n <= 10actions.length === values.lengthactions[i]为 "call" 和 "getCallCount" 中的一个fnName为 "sum", "factorial" 和 "fib" 中的一个
思路
其实还是需要一个闭包。这里声明一个哈希表做闭包,用来记录输入的参数是否曾经也被输入过。
代码
type Fn = (...params: number[]) => number
function memoize(fn: Fn): Fn {
// map 做闭包,用于存储调用过的参数与对应值哈希表
const map = new Map();
return function (...args) {
// 参数序列化,作为 key
const key = JSON.stringify(args);
if (map.has(key)) {
// 哈希表中查得到,说明调用过了,直接返回哈希表里存的值
return map.get(key);
} else {
const value = fn(...args);
// 没调用过,将返回值存入哈希表
map.set(key, value);
return value;
}
}
}
/**
* let callCount = 0;
* const memoizedFn = memoize(function (a, b) {
* callCount += 1;
* return a + b;
* })
* memoizedFn(2, 3) // 5
* memoizedFn(2, 3) // 5
* console.log(callCount) // 1
*/