实现 asyncSum - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
custw
V2EX    问与答

实现 asyncSum

  •  
  • /li>
  •   custw 2020-03-29 11:04:37 +08:00 2191 次点击
    这是一个创建于 2021 天前的主题,其中的信息可能已经有所发展或是发生改变。

    字节跳动面试题目:利用已知函数 add 实现 asyncSum

    function add (a, b, callback) { callback(a + b) } async function asyncSum(...args) { // 具体实现 } // await asyncSum(1, 2, 3, 4, 5) 的结果应当为 1 + 2 + 3 + 4+ 5 = 15 

    解题思路:

    1. 实现任意参数的求和函数 sum
    2. 求和函数依赖了异步的 add 函数,add 函数一次只能计算两数之和,所以任意参数要分组,每组两个参数调用 add 函数
    3. 重复第 2 步,直到 可分组参数数量为 1
    [1, 2, 3, 4, 5] // 传参 [[1, 2], [3, 4], [5, 0]] // 分组 [3, 7, 5] // 各组求和 [[3, 7], [5, 0]] // 继续分组 [10, 5] // 继续各组求和 [[10, 5]] // 继续分组 [15] // 求和 

    首先,实现分组函数,将参数分成 2 个一组。

    function chunk(arr) { let ret = [] for (let i = 0; i < arr.length; i += 2) { ret.push([arr[i], arr[i + 1] ? arr[i + 1] : 0]) } return ret } chunk([1, 2, 3, 4, 5]) // [[1, 2], [3, 4], [5, 0]] 

    然后,实现 sum 函数对分组的数字依次求和。由于需要强依赖 add 函数,首先将 add 函数 promisify

    function add (a, b, callback) { callback(a + b) } function asyncAdd(a, b) { return new Promise(resolve => add(a, b, sum => resolve(sum))) } 

    实现 sum 函数。

    function sum(nums) { return Promise.all(chunk(nums).map(([a, b]) => asyncAdd(a, b))) } // await sum([1, 2, 3, 4, 5]) 结果 [3, 7, 5] 

    至此,可以看出数字长度在缩减,所以只需要继续拆分数组调用 sum 函数直到数字长度为 1 即为最终求和。

    async function asyncSum(...args) { let ret = await sum(args) while (ret.length > 1) { ret = await sum(ret) } return ret[0] } 

    完整实现。

    function chunk(arr) { let ret = [] for (let i = 0; i < arr.length; i += 2) { ret.push([arr[i], arr[i + 1] ? arr[i + 1] : 0]) } return ret } function add (a, b, callback) { callback(a + b) } function asyncAdd(a, b) { return new Promise(resolve => add(a, b, sum => resolve(sum))) } function sum(nums) { return Promise.all(chunk(nums).map(([a, b]) => asyncAdd(a, b))) } async function asyncSum(...args) { let ret = await sum(args) while (ret.length > 1) { ret = await sum(ret) } return ret[0] } 
    11 条回复    2020-03-29 14:54:52 +08:00
    yuenc
        1
    yuenc  
       2020-03-29 11:47:15 +08:00
    ```js
    function add(a, b, callback) {
    callback(a + b);
    }

    async function asyncSum(...args) {
    // 具体实现
    if (args.length === 0) {
    return null;
    }
    if (args.length % 2 === 1) {
    args.push(0);
    }
    const step = args.length / 2;
    const endIndex = args.length - 1;
    let sum = 0;
    for (let i = 0; i < step; i++) {
    sum += await new Promise(resolve => add(args[i], args[endIndex - i], resolve));
    }
    return sum;
    }
    ```
    chuxiaonan
        2
    chuxiaonan  
       2020-03-29 11:52:38 +08:00
    楼主的解题思路非常棒 过程清晰 结果明了

    不过 由于这道题题设就已经给定了 Promise + callback 的组合
    而我作为面试官的话 我会把题目调整一下 会要求不允许使用 callback
    (当然我们知道实际业务中结合使用是完全没有问题 这里只是在面试环境下的一种理想情况下的提问而已)

    那么 问题的答案就会变为
    ```js
    // 注意这里的 add 函数不存在 callback
    const add = (...args) => args.reduce((sum, cur) => sum += cur, 0);

    const chunk = (arr) => {
    const ret = [];

    for (let i = 0; i < arr.length; i += 2) {
    ret.push([arr[i], arr[i + 1] ? arr[i + 1] : 0]);
    }

    return ret;
    };


    // 其实你会发现这个函数可以不使用 async 关键字修饰
    async function asyncSum(...args) {
    // 首先构造 promise 链
    const chain = chunk(args).map((item) => (sum) => add(sum, add(...item)));

    // 其次构造初始化 promise 实例
    let result = Promise.resolve(0);

    // 最后根据 promise chaining 的特性得到最终的运算结果
    while (chain.length) {
    result = result.then(chain.shift());
    }

    // 输出
    result.then(console.log.bind(console));
    }

    ```
    chuxiaonan
        3
    chuxiaonan  
       2020-03-29 11:53:31 +08:00
    @yuenc 哈哈 咱俩想到一块儿去了兄弟
    EPr2hh6LADQWqRVH
        4
    EPr2hh6LADQWqRVH  
       2020-03-29 11:54:43 +08:00
    你得问面试的人想要什么,想要性能,想要可读性,还是想要你解释心路历程。

    有可能你想要性能,但对面想要可读性,结果驴唇不对马嘴。

    你会写代码还不够,必须对面还会面试,有时候遇到对面不太会面试的,你得帮他一把。
    lizheming
        5
    lizheming  
       2020-03-29 11:57:10 +08:00
    直接用 reduce 链式一波流就可以了吧,两两分组感觉很麻烦的样子…
    ```js
    async function asyncSum(...args) {
    return args.reduce((o, n) =>
    new Promise(resolve => o.then(v => add(v, n, resolve))
    ), Promise.resolve(0));
    }
    ```
    EPr2hh6LADQWqRVH
        6
    EPr2hh6LADQWqRVH  
       2020-03-29 12:01:30 +08:00   1
    你看你这个题目,本质上是个 reduce 操作,要运行的过程是个计算密集型任务,那你在单线程的 js 世界里面二分并发就意义不大,平白损失了可读性,多此一举。

    但如果说你是一个 io 密集型,那么你的优化就有意义。

    即便优化有意义,是不是要牺牲可读性做优化也还是要看场景。
    即便是进行优化,也可以最小化可读性的牺牲,把二分代码单独封装,保持业务代码简单。
    yuenc
        7
    yuenc  
       2020-03-29 12:19:34 +08:00
    ```Javascript
    题目意思应该是所有的加只能用 函数 add
    function add(a, b, callback) {
    callback(a + b);
    }

    async function asyncSum(...args) {
    // 具体实现
    switch (args.length) {
    case 0: return 0;
    case 1: return args[0];
    case 2: return new Promise(resolve => add(args[0], args[1], resolve));
    }
    if (args.length % 2 === 1) {
    args.push(0);
    }
    const endIndex = args.length - 1;
    const promiseValues = Array.from(
    { length: args.length / 2 },
    (_, i) => new Promise(resolve => add(args[i], args[endIndex - 1], resolve))
    );
    return asyncSum(...await Promise.all(promiseValues));
    }

    ```
    maomaomao001
        8
    maomaomao001  
       2020-03-29 13:08:55 +08:00
    有点没看懂,这题目异步想体现在哪里??

    ```
    function add(a, b, callback) {
    callback(a + b);
    }

    async function asyncSum(...args) {
    const sum = args.flat(Number.POSITIVE_INFINITY).reduce((s, v) => {
    add(s, v, r => {
    s = r;
    });
    return s;
    }, 0);
    return sum;
    }

    ```
    maomaomao001
        9
    maomaomao001  
       2020-03-29 13:10:31 +08:00
    @avastms 这个题目为什么会用到 promise,这个我没看懂 ? 还是说, 那个 callback(a+b) 的具体实现其实是个异步?
    rabbbit
        10
    rabbbit  
       2020-03-29 14:50:20 +08:00
    没看懂,谁能给我讲讲这题想考啥?
    我这么实现能算对吗?

    async function asyncSum(...args) {
    ...let num = 0;
    ...const callback = sum => (num = sum);
    ...args.forEach(i => add(num, i, callback));
    ...return num;
    }
    rabbbit
        11
    rabbbit  
       2020-03-29 14:54:52 +08:00
    function add (a, b, callback) {
    ...callback(a + b)
    }

    async function asyncSum(...args) {
    ...let num = 0;
    ...const callback = sum => (num = sum);
    ...args.forEach(i => add(num, i, callback));
    ...return num;
    }

    (async () => {
    ...console.log(await asyncSum()) // 0
    ...console.log(await asyncSum(1,2, 3, 4, 5)) // 15
    })()
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2864 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 14:19 PVG 22:19 LAX 07:19 JFK 10:19
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86