用typescript类型推算斐波那契(二)

写在前面

本文执行环境typescript,版本4.6.2
复制代码

背景

之前推算过一次斐波那契函数 -> 文章:用typescript类型推算斐波那契
本以为万事大吉了,结果原来是因为我的测试例子不够,竟然才推算到12位电脑就罢工!

image.png

因为上文实现的 +1操作 就是 push数组后获取length 来实现,所有的加法都通过递归 +1操作 来实现,搁我也罢工了,那怎么解决?

重构吧!

重构

思考吧,怎么重构?
斐波那契本身就需要递归,这个跳不过,看来优化重点就是加法。敲黑板,重点是:

重构加法
复制代码

而加法就要先优化 +1操作,因为加法机制是对位相加,如果有进位则进位

重构+1操作

其实很简单,实现个位数的 +1 操作,这是加法的基础

type G_ADD_ONE<N extends string> = N extends '0' ? '1' :
    N extends '1' ? '2' :
    N extends '2' ? '3' :
    N extends '3' ? '4' :
    N extends '4' ? '5' :
    N extends '5' ? '6' :
    N extends '6' ? '7' :
    N extends '7' ? '8' :
    N extends '8' ? '9' :
    N extends '9' ? '10' :
    '';
复制代码

加法的规则是从个位数开始相加,有进位则进位,所以需要递归获取到单个数字

// 获取末尾的数字
type GetEnd<N extends string> = N extends simpleNum ? N :
    N extends `${string}${infer P}` ? GetEnd<P> : '';

// 获取末尾之外的数字
type GetEndPre<N extends string> = N extends simpleNum ? '' :
    N extends `${infer P}${GetEnd<N>}` ? P : '';

type end1 = GetEnd<'123'>; // 3
type end2 = GetEndPre<'123'>; // 12
复制代码

+1操作的逻辑是:

获取到数字个位后就可以进行任意数的+1操作     
复制代码

之前n个数需要递归n次,即5+6需要递归11次 +1操作,现在完成任意数的 +1 只需要递归数字长度即可

// 个位数
type simpleNum = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9';

type ADD_ONE<N extends string> = N extends simpleNum ? G_ADD_ONE<N> :
    GetEnd<N> extends '9' ? `${ADD_ONE<GetEndPre<N>>}0` :
    `${GetEndPre<N>}${G_ADD_ONE<GetEnd<N>>}`;

type addOne0 = ADD_ONE<'7'>; // 8
type addOne1 = ADD_ONE<'99'>; // 100
type addOne2 = ADD_ONE<'299'>; // 300
type addOne3 = ADD_ONE<'159'>; // 160
复制代码

好起来了,到现在 重构+1完成

重构加法

既然 +1操作 都完成了,那递归 +1操作 不就可以了吗?
是可以,但是还是面临了 n次递归,所以这里要避开递归!
怎么处理呢,跟实现 +1操作 类似,实现99加法表

type CD_SIMPLE_ADD<X extends string, Y extends string> =  X extends '' ? Y :
    Y extends '' ? X :
    X extends '0' ? Y :
    Y extends '0' ? X :
    X extends '1' ? G_ADD_ONE<Y> :
    Y extends '1' ? G_ADD_ONE<X> :
    X extends '2' ?
        Y extends '2' ? '4' :
        Y extends '3' ? '5' :
        Y extends '4' ? '6' :
        Y extends '5' ? '7' :
        Y extends '6' ? '8' :
        Y extends '7' ? '9' :
        Y extends '8' ? '10' :
        Y extends '9' ? '11' :
        never :
    X extends '3' ?
        Y extends '2' ? '5' :
        Y extends '3' ? '6' :
        Y extends '4' ? '7' :
        Y extends '5' ? '8' :
        Y extends '6' ? '9' :
        Y extends '7' ? '10' :
        Y extends '8' ? '11' :
        Y extends '9' ? '12' :
        never :
    X extends '4' ?
        Y extends '2' ? '6' :
        Y extends '3' ? '7' :
        Y extends '4' ? '8' :
        Y extends '5' ? '9' :
        Y extends '6' ? '10' :
        Y extends '7' ? '11' :
        Y extends '8' ? '12' :
        Y extends '9' ? '13' :
        never :
    X extends '5' ?
        Y extends '2' ? '7' :
        Y extends '3' ? '8' :
        Y extends '4' ? '9' :
        Y extends '5' ? '10' :
        Y extends '6' ? '11' :
        Y extends '7' ? '12' :
        Y extends '8' ? '13' :
        Y extends '9' ? '14' :
        never :
    X extends '6' ?
        Y extends '2' ? '8' :
        Y extends '3' ? '9' :
        Y extends '4' ? '10' :
        Y extends '5' ? '11' :
        Y extends '6' ? '12' :
        Y extends '7' ? '13' :
        Y extends '8' ? '14' :
        Y extends '9' ? '15' :
        never :
    X extends '7' ?
        Y extends '2' ? '9' :
        Y extends '3' ? '10' :
        Y extends '4' ? '11' :
        Y extends '5' ? '12' :
        Y extends '6' ? '13' :
        Y extends '7' ? '14' :
        Y extends '8' ? '15' :
        Y extends '9' ? '16' :
        never :
    X extends '8' ?
        Y extends '2' ? '10' :
        Y extends '3' ? '11' :
        Y extends '4' ? '12' :
        Y extends '5' ? '13' :
        Y extends '6' ? '14' :
        Y extends '7' ? '15' :
        Y extends '8' ? '16' :
        Y extends '9' ? '17' :
        never :
    X extends '9' ?
        Y extends '2' ? '11' :
        Y extends '3' ? '12' :
        Y extends '4' ? '13' :
        Y extends '5' ? '14' :
        Y extends '6' ? '15' :
        Y extends '7' ? '16' :
        Y extends '8' ? '17' :
        Y extends '9' ? '18' :
    never : never;
复制代码

加法的逻辑是:

同样根据递归数字长度来实现任意数对位的加法
复制代码
type ADD_Check<X extends string, Y extends string> = X extends simpleNum ?
    Y extends simpleNum ?
    CD_SIMPLE_ADD<X, Y> : ADD_ALL<X, Y> : ADD_ALL<X, Y>;

type ADD_ALL<
    X extends string,
    Y extends string,
    // 储存结果
    Z extends string = '',
    // 是否进位,'1'为进位,'0'为不进位
    pre extends string = '0',
    // 计算新一轮的值
    data extends string = CD_SIMPLE_ADD<GetEnd<X>, GetEnd<Y>>,
    // 新一轮的值 + 进位
    preData extends string = ADD_Check<data, pre>,
    // 为了当 x 或者 y 的为空的时候导出,['data']
    Q extends string = `${X}${Y}`
> = {
    ['loop']: ADD_ALL<
                X extends '' ? '' : GetEndPre<X>,
                Y extends '' ? '' : GetEndPre<Y>,
                preData extends simpleNum ?
                    `${preData}${Z}` :
                    `${GetEnd<preData>}${Z}`,
                preData extends simpleNum ? '0' : '1'
            >;
    ['data']: `${ADD_Check<Q, pre>}${Z}`;
    ['result']: pre extends '1' ? `1${Z}`: Z;
}[X extends '' ? Y extends '' ? 'result' : 'data' : 'loop'];

type addall1 = ADD_Check<'111', '999'>; // 1110
type addall2 = ADD_Check<'555', '445'>; // 1000
type addall3 = ADD_Check<'1123', '456'>; // 1579
复制代码

实现斐波那契的推算

加法已经重构完成,那么递归加法就可以写出斐波那契函数了!

type FIB2<
    T extends string,
    A extends string = '0',
    B extends string = '1',
    N extends string = '1'
> = T extends '0' | '1' ? T :
    {
        ['loop']: FIB2<T, B, ADD_Check<A, B>, ADD_ONE<N>>;
        ['result']: B;
}[T extends N ? 'result' : 'loop'];

type FIv0 = FIB2<'0'> // 0
type FIv1 = FIB2<'1'>; // 1
type FIv2 = FIB2<'2'>; // 1
type FIv3 = FIB2<'3'>; // 2
type FIv4 = FIB2<'4'>; // 3
type FIv5 = FIB2<'5'>; // 5
type FIv6 = FIB2<'6'>; // 8
type FIv7 = FIB2<'7'>; // 13
type FIv8 = FIB2<'8'>; // 21
type FIv9 = FIB2<'9'>; // 34
type FIv10 = FIB2<'10'>; // 55
type FIv11 = FIB2<'11'>; // 89
type FIv12 = FIB2<'12'>; // 144
type FIv22 = FIB2<'22'>; // 17711
type FIv23 = FIB2<'23'>; // 28657
type FIv24 = FIB2<'24'>; // 46368
type FIv25 = FIB2<'25'>; // 75025
type FIv26 = FIB2<'26'>; // 121393
type FIv27 = FIB2<'27'>; // 196418
type FIv28 = FIB2<'28'>; // 317811
type FIv33 = FIB2<'33'>; // 3524578
type FIv42 = FIB2<'42'>; // 267914296
复制代码

可能有人会问,为啥到42位就不举例了,因为到43位电脑又罢工了!

image.png

真是头疼啊,这个问题留着下次优化吧~

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享