目录
2629. 复合函数
2631. 分组
2634. 过滤数组中的元素
2635. 转换数组中的每个元素
2637. 有时间限制的 Promise 对象
2648. 生成斐波那契数列
2649. 嵌套数组生成器
2665. 计数器 II
2666. 只允许一次函数调用
恒等函数
在数学里,恒等函数为一无任何作用的函数:它总是传回和其引数相同的值。换句话说,恒等函数为函数f(x) = x,输入等于输出。
复合函数
复合函数是数学中的概念,它指的是将一个函数的输出作为另一个函数的输入,从而创建一个新的函数。简单来说,复合函数是通过将一个函数的结果传递给另一个函数来实现函数的组合。
复合函数的定义通常表示为 f(g(x)),其中 g(x) 是一个函数,f(x) 是另一个函数。这意味着先应用 g(x),然后将结果作为 f(x) 的输入。通过这种方式,我们可以将多个简单函数组合成一个更复杂的函数。
请你编写一个函数,它接收一个函数数组 [f1, f2, f3,…, fn]
,并返回一个新的函数 fn
,它是函数数组的 复合函数 。
[f(x), g(x), h(x)]
的 复合函数 为 fn(x) = f(g(h(x)))
。
一个空函数列表的 复合函数 是 恒等函数 f(x) = x
。
你可以假设数组中的每个函数接受一个整型参数作为输入,并返回一个整型作为输出。
题解
var compose = function (funcs) {
if (funcs.length === 0) {
return function (x) {
return x;
};
}
return function (x) {
let result = x;
for (let i = funcs.length - 1; i >= 0; i--) {
result = funcs[i](result);
}
return result;
};
}
这个函数接受一个函数数组 funcs
作为参数。
首先,我们检查函数数组是否为空,如果为空,意味着没有需要复合的函数,因此返回一个恒等函数 function(x) { return x; }
。
然后,我们返回一个新的函数,这个函数接受参数 x
,表示输入的整型参数。
在函数内部,我们使用一个循环从后往前遍历函数数组,并将每个函数的结果作为下一个函数的输入,最后返回整个复合函数的结果。
例如,如果函数数组为 [f, g, h]
,那么复合函数为 fn(x) = f(g(h(x)))
。
使用这个复合函数,可以按照数组中函数的顺序依次调用它们,将每个函数的结果作为下一个函数的输入,最终得到复合函数的结果。
请你编写一段可应用于所有数组的代码,使任何数组调用 array. groupBy(fn)
方法时,它返回对该数组 分组后 的结果。
数组 分组 是一个对象,其中的每个键都是 fn(arr[i])
的输出的一个数组,该数组中含有原数组中具有该键的所有项。
提供的回调函数 fn
将接受数组中的项并返回一个字符串类型的键。
每个值列表的顺序应该与元素在数组中出现的顺序相同。任何顺序的键都是可以接受的。
请在不使用 lodash 的 _.groupBy
函数的前提下解决这个问题。
示例
输入: array = [ {"id":"1"}, {"id":"1"}, {"id":"2"} ], fn = function (item) { return item.id; } 输出: { "1": [{"id": "1"}, {"id": "1"}], "2": [{"id": "2"}] } 解释: 输出来自函数 array.groupBy(fn)。 分组选择方法是从数组中的每个项中获取 "id" 。 有两个 "id" 为 1 的对象。所以将这两个对象都放在第一个数组中。 有一个 "id" 为 2 的对象。所以该对象被放到第二个数组中。
题解
Array.prototype.groupBy = function (fn) {
return this.reduce((result, item) => {
const key = fn(item);
if (result[key]) {
result[key].push(item);
} else {
result[key] = [item];
}
return result;
}, {});
};
这段代码定义了一个名为groupBy
的方法,该方法是通过修改Array
的原型来实现的。通过调用groupBy
方法,我们可以将一个数组按照指定的函数进行分组。
代码的实现逻辑如下:
groupBy
方法接受一个函数fn
作为参数。
在方法体内,使用reduce
函数对数组进行迭代。reduce
函数的作用是对数组中的每个元素执行一个回调函数,并将回调函数的返回值用作下一次迭代的初始值。
在每次迭代中,调用fn
函数并传入当前元素item
作为参数,将返回值保存在key
变量中。该函数用于确定每个元素所属的组别。
判断result
对象中是否已经存在以key
为键的属性。如果已经存在,则将当前元素item
添加到对应的数组中。如果不存在,则创建以key
为键的属性,并将当前元素item
作为数组的第一个元素。
最后,返回最终的结果对象result
。
总之,这段代码的作用是将一个数组按照指定的函数对元素进行分组,返回一个以分组结果为值的对象。
给定一个整数数组 arr
和一个过滤函数 fn
,并返回一个过滤后的数组 filteredArr
。
fn
函数接受一个或两个参数:
arr[i]
- arr
中的数字i
- arr[i]
的索引filteredArr
应该只包含使表达式 fn(arr[i], i)
的值为 真值 的 arr
中的元素。真值 是指 Boolean(value)
返回参数为 true
的值。
请在不使用内置的 Array.filter 方法的情况下解决该问题。
输入:arr = [0,10,20,30], fn = function greaterThan10(n) { return n > 10; } 输出: [20,30] 解释: const newArray = filter(arr, fn); // [20, 30] 过滤函数过滤掉不大于 10 的值
题解
var filter = function (arr, fn) {
let filteredArr = [];
for (let i = 0; i < arr.length; i++) {
if (fn(arr[i], i)) {
filteredArr.push(arr[i]);
}
}
return filteredArr;
}
这段代码定义了一个名为filter的函数,它接受两个参数,arr和fn。
函数的作用是对数组arr中的每个元素调用fn函数进行过滤,将满足条件的元素放入新的数组filteredArr中,并将filteredArr作为函数的返回值。
具体的实现逻辑如下:
总结:这段代码实现了一个自定义的filter函数,可以通过传入不同的条件函数对数组中的元素进行过滤,并将满足条件的元素放入一个新数组中返回。
编写一个函数,这个函数接收一个整数数组 arr
和一个映射函数 fn
,通过该映射函数返回一个新的数组。
返回数组的创建语句应为 returnedArray[i] = fn(arr[i], i)
。
请你在不使用内置方法 Array.map
的前提下解决这个问题。
示例
输入:arr = [1,2,3], fn = function plusone(n) { return n + 1; } 输出:[2,3,4] 解释: const newArray = map(arr, plusone); // [2,3,4] 此映射函数返回值是将数组中每个元素的值加 1。
题解
var map = function(arr, fn) {
var returnedArray = [];
for (var i = 0; i < arr.length; i++) {
returnedArray[i] = fn(arr[i], i);
}
return returnedArray;
};
这段代码定义了一个名为 "map" 的函数,它接受两个参数:一个数组和一个函数。函数的作用是对数组中的每个元素进行处理,并返回处理后的结果。
在函数中,首先创建了一个名为 "returnedArray" 的空数组。接下来,使用循环遍历数组中的每个元素,并调用传入的函数对其进行处理。处理后的结果被存储在 "returnedArray" 数组的相应索引位置上。
最后,函数返回了处理后的数组 "returnedArray"。
请你编写一个函数,它接受一个异步函数 fn
和一个以毫秒为单位的时间 t
。它应根据限时函数返回一个有 限时 效果的函数。函数 fn
接受提供给 限时 函数的参数。
限时 函数应遵循以下规则:
fn
在 t
毫秒的时间限制内完成,限时 函数应返回结果。fn
的执行超过时间限制,限时 函数应拒绝并返回字符串 "Time Limit Exceeded"
。示例
输入: fn = async (n) => { await new Promise(res => setTimeout(res, 100)); return n * n; } inputs = [5] t = 50 输出:{"rejected":"Time Limit Exceeded","time":50} 解释: const limited = timeLimit(fn, t) const start = performance.now() let result; try { const res = await limited(...inputs) result = {"resolved": res, "time": Math.floor(performance.now() - start)}; } catch (err) { result = {"rejected": err, "time": Math.floor(performance.now() - start)}; } console.log(result) // 输出结果 提供的函数设置在 100ms 后执行完成,但是设置的超时时间为 50ms,所以在 t=50ms 时拒绝因为达到了超时时间。
题解
var timeLimit = function (fn, t) {
return async function (...args) {
return new Promise((resolve, reject) => {
let timedOut = false;
// 执行异步函数,并传递参数
const result = fn(...args);
// 设置超时时间,超过指定时间后,Promise对象会被拒绝并返回字符串"Time Limit Exceeded"
setTimeout(() => {
timedOut = true;
reject("Time Limit Exceeded");
}, t);
// 监听异步函数的完成
result.then((value) => {
if (!timedOut) {
resolve(value);
}
}).catch((error) => {
if (!timedOut) {
reject(error);
}
});
});
};
}
这段代码定义了一个名为timeLimit的函数,该函数接受两个参数fn和t。fn是一个异步函数,t是一个表示超时时间的数字。该函数返回一个新的异步函数。
新的异步函数接受任意数量的参数,它的功能是在指定的时间内执行传入的异步函数fn,并返回一个Promise对象。
在新的异步函数内部,首先声明了一个变量timedOut,其初始值为false。然后,通过调用fn并传入参数args,执行传入的异步函数fn,并将结果赋值给变量result。
接着,调用setTimeout函数,设置一个超时时间。当超过指定时间后,timedOut的值将被设置为true,并将Promise对象拒绝,并返回字符串"Time Limit Exceeded"。
接下来,监听异步函数result的完成。如果在超时时间内完成,则通过resolve将结果值传递给外部Promise对象的回调函数。如果超时时间已经过去,即timedOut的值为true,则忽略结果,并通过reject将错误传递给外部Promise对象的回调函数。
最后,返回新的异步函数作为timeLimit函数的结果。
请你编写一个生成器函数,并返回一个可以生成 斐波那契数列 的生成器对象。
斐波那契数列 的递推公式为
Xn = Xn-1 + Xn-2
。这个数列的前几个数字是
0, 1, 1, 2, 3, 5, 8, 13
。
示例
输入:callCount = 5
输出:[0,1,1,2,3]
解释:
const gen = fibGenerator();
gen.next().value; // 0
gen.next().value; // 1
gen.next().value; // 1
gen.next().value; // 2
gen.next().value; // 3
题解
function* fibonacciSequence() {
let prev = 0; //前一个值
let curr = 1;//当前值
yield prev;
yield curr;
while (true) {
const next = prev + curr;//
yield next;
prev = curr;
curr = next;
}
}
var fibGenerator = function* () {
let prev = 0; //前一个值
let curr = 1; //当前值
yield prev;
yield curr;
while (true) {
const next = prev + curr;//下一个值
yield next;
prev = curr;//前一个值=当前值
curr = next;//当前值= 下一个值
}
}
const fn = fibGenerator()
console.log(fn);
console.log(fn.next()); //prev:0
console.log(fn.next()); //curr:1
console.log(fn.next()); //next:1 prev+curr(0+1) prev = curr; curr = next( prev:1 curr:1)
console.log(fn.next()); //next:2:prev+curr(1+1) prev = curr; curr = next( prev:1 curr:2)
console.log(fn.next()); //next:3:prev+curr(1+2) prev = curr; curr = next( prev:2 curr:3)
console.log(fn.next()); //next:5:prev+curr(2+3) prev = curr; curr = next( prev:3 curr:5)
这段代码定义了一个生成斐波那契数列的函数fibonacciSequence()
。该函数通过yield
关键字实现了生成器函数的特性,即可以暂停执行并返回一个值,然后可以从暂停的地方恢复执行。
在函数内部,首先定义了prev
和curr
两个变量,分别用来保存前一个和当前的斐波那契数。
然后通过yield
关键字分别返回了prev
和curr
,即斐波那契数列的第1和第2个数。
接下来使用while
循环来生成数列的其他数。在每次循环中,根据斐波那契数列的定义,计算下一个数next
的值,然后通过yield
关键字返回next
。
然后将prev
的值更新为当前数curr
,再将curr
的值更新为next
,以便下一次循环使用。
最后通过const fibonacciGenerator = fibonacciSequence
将该生成器函数赋值给变量fibonacciGenerator
,生成一个用于产生斐波那契数列的生成器对象。
现给定一个整数的 多维数组 ,请你返回一个生成器对象,按照 中序遍历 的顺序逐个生成整数。
多维数组 是一个递归数据结构,包含整数和其他 多维数组。
中序遍历 是从左到右遍历每个数组,在遇到任何整数时生成它,遇到任何数组时递归应用 中序遍历 。
示例
输入:arr = [[[6]],[1,3],[]]
输出:[6,1,3]
解释:
const generator = inorderTraversal(arr);
generator.next().value; // 6
generator.next().value; // 1
generator.next().value; // 3
generator.next().done; // true
题解
var inorderTraversal = function*(arr) {
for (let item of arr) {
if (Array.isArray(item)) {
yield* inorderTraversal(item);
} else {
yield item;
}
}
};
这段代码定义了一个名为inorderTraversal的generator函数,接收一个数组作为参数。
函数开始时,它通过使用for...of循环遍历输入的数组中的每个元素。如果当前元素是一个数组,则递归调用inorderTraversal函数,并使用yield*关键字迭代生成的值。如果当前元素不是数组,则使用yield关键字返回当前元素。
这样,当inorderTraversal函数被调用时,它会遍历整个输入数组,并按顺序yield每个元素或递归遍历内部数组。通过使用generator函数,可以逐步获取数组中的元素,而不是一次性返回整个数组。
请你写一个函数 createCounter
. 这个函数接收一个初始的整数值 init
并返回一个包含三个函数的对象。
这三个函数是:
increment()
将当前值加 1 并返回。decrement()
将当前值减 1 并返回。reset()
将当前值设置为 init
并返回。示例
输入:init = 5, calls = ["increment","reset","decrement"]
输出:[6,5,4]
解释:
const counter = createCounter(5);
counter.increment(); // 6
counter.reset(); // 5
counter.decrement(); // 4
题解
var createCounter = function(init) {
let count = init;
function increment() {
count++;
return count;
}
function decrement() {
count--;
return count;
}
function reset() {
count = init;
return count;
}
return { //返回对象
increment,
decrement,
reset
};
};
这段代码定义了一个函数createCounter,该函数接受一个参数init作为初始值。函数内部定义了三个函数increment、decrement和reset,用于增加、减少和重置计数器的值。
函数内部声明了一个变量count并将其初始化为init。函数increment将count增加1,并返回count的值。函数decrement将count减少1,并返回count的值。函数reset将count重置为init,并返回count的值。
最后,函数createCounter返回一个包含increment、decrement和reset三个方法的对象,这样就创建了一个计数器对象。通过调用该对象的方法,可以对计数器的值进行增加、减少和重置操作。
给定一个函数 fn
,它返回一个新的函数,返回的函数与原始函数完全相同,只不过它确保 fn
最多被调用一次。
fn
相同的结果。undefined
。示例
输入: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 没有被调用
题解
function once(fn) {
let called = false;
let calls = 0;
let result;
function wrapper(...args) {
if (!called) {
called = true;
result = fn(...args);
calls += 1;
return result;
} else {
calls += 1;
return undefined;
}
}
wrapper.calls = () => calls;
wrapper.value = () => result;
return wrapper;
};
这段代码定义了一个函数once
,它接受一个函数fn
作为参数。
在函数内部,定义了三个变量called
、calls
和result
,并且初始化为false
、0
和undefined
。
函数内部还定义了一个名为wrapper
的嵌套函数,它使用扩展运算符收集所有参数。接下来,它会判断called
的值是否为false
。
如果called
的值为false
,表示函数fn
还未被调用过。在这种情况下,会将called
的值设为true
,表示函数已被调用。
然后,函数fn
会以传入的参数被调用,并将返回值赋给变量result
。
接着,变量calls
会被递增1,表示函数调用的次数。
最后,函数wrapper
会返回result
。
如果called
的值为true
,表示函数fn
已经被调用过。在这种情况下,只是将变量calls
递增1,并且返回undefined
。
函数wrapper
还定义了两个新属性calls
和value
。
属性calls
是一个箭头函数,它返回变量calls
的值。
属性value
也是一个箭头函数,它返回变量result
的值。
最后,函数once
会返回wrapper
函数。