当前位置: 首页 > 科技观察

TypeScript类型体操:数组长度实现数值运算

时间:2023-03-20 23:05:36 科技观察

本文转载自微信公众号《神光的编程秘籍》,作者神说必有光。转载本文请联系神光编程秘籍公众号。TS型体操小册子掘金定档4月,有点晚了。..因此,我把其中一个套路提出来作为一篇文章贴了出来。大家可以提前感受一下,届时会设置为小册子的试阅章节。这个套路叫做数组长度计数,就是利用数组的长度来实现加减乘除各种计数。是六套路中最尴尬的一套。以下是正文(小册子原文):例程4:计算数组长度TypeScript类型系统不是图灵完备的,能不能写出各种逻辑,但是好像没有找到值相关的逻辑.没错,数值相关的逻辑比较绕,单独挑出来,这就是我这一节要讲的内容。这是类型操的第四套路:数数组的长度。计算数组的长度。TypeScript类型系统没有加减乘除运算符。如何进行数值运算?不知道大家有没有注意到数组类型的长度就是数字。比如:我们可以构造数组类型,那么构造不同长度的数组,然后取长度,不就是一个数值运算吗?TypeScript类型系统中没有加减乘除运算符,但是可以构造不同的数组然后取length完成数值计算,将值的加减乘除转换成数组的提取和构建。这一点可以说是体操中最头疼的一点。绕过这个弯需要一些思维上的改变。让我们做一些真实的案例来掌握它。数组长度实现加减乘除Add我们知道数值计算需要转化为对数组类型的运算,所以加法的实现很容易想到:构造两个数组,然后合并为一个,取长度。比如3+2就是构造一个长度为3的数组类型,再构造一个长度为2的数组类型,然后将它们合并成一个数组,取长度。待构造的数组长度不确定,需要递归构造。我们已经实现了这个:typeBuildArray=Arr['length']extendsLength?Arr:BuildArray<长度,Ele,[...Arr,Ele]>;类型参数Length是要构造的数组的长度。类型参数Ele为数组元素,默认为unknown。类型参数Arr为构造数组,默认为[]。如果Arr的长度达到Length,则返回构造好的Arr,否则继续递归构造。数组的构造实现了,那么可以基于它实现加法:typeAdd=[...BuildArray,...BuildArray]['length'];我们用更大的数字进行测试:结果是正确的。这样我们就通过构造一个一定长度的数组,取长度来实现加法运算。减法加法就是构造一个数组,那么减法怎么做呢?减法是从值中删除一部分。很容易想到,提取数组类型就可以搞定。比如3是[unknown,unknown,unknown]的数组类型。提取2个元素后,剩余数组的长度为1。所以减法的实现是这样的:typeSubtract=BuildArrayextends[...arr1:BuildArray,...arr2:推断休息]?休息['长度']:从不;类型参数Num1和Num2分别是被减数和减数,由extends限制为数字。构造一个长度为Num1的数组,通过模式匹配提取长度为Num2的元素,剩下的放在infer声明的局部变量Rest中。取Rest的长度返回,就是减法的结果。这样我们就通过数组类型的提取来实现减法运算。乘法我们将加法转换为数组构造,将减法转换为数组提取。如何做乘法?为了解释乘法,翻到小学课本上,找到了这样一张图:1乘以5等于1+1+1+1+1,也就是说乘法是多次加法的结果累加。然后在加法的基础上,我们增加一个额外的参数来传递中间结果数组,计算完再取length实现乘法:typeMutiply=Num2扩展0?ResultArr['length']:Mutiply,[...BuildArray,...ResultArr]>;类型参数Num1和Num2是加数和加数。因为乘法是多次加法结果的累加,所以我们增加了一个类型参数ResultArr来保存中间结果。默认值为[],相当于从0开始相加,每相加Num2减1,直到Num2为0,即表示相加完成。添加的过程就是将Num1个元素放入ResultArr数组中。这个递归累加就是把元素递归放到ResultArr中。最后,ResultArr的长度就是相乘的结果。这样,我们通过递归累加实现了乘法。除乘法是递归累加,那么减法不就是递归累加吗?我去翻了翻小学课本,发现了这张图:我们有9个苹果,3个给漂亮的绵羊,3个给懒惰的羊,还有3只山羊,3只山羊分给煮羊,剩下0只山羊。所以9/3=3。因此,除法的实现就是不断地从被减数中减去减数,直到减为0,记录减法的次数作为结果。即:类型Divide=Num1extends0?CountArr['length']:Divide,Num2,[未知,...CountArr]>;类型参数Num1和Num2分别是被减数和减数。类型参数CountArr用于记录被减去几次的累加数组。如果Num1减为0,那么此时的减法次数就是除法的结果,即CountArr['length']。否则,继续递归减法,从Num1中减去Num2,在CountArr中再增加一个元素,代表又一次减法。这样就实现了除法:这样我们通过递归累加记录减法的次数来实现除法。加减乘除之后,我们再做一些其他类型的数值计算。数组长度实现计数StrLen数组的长度可以通过取长度得到,但是字符串类型不能取长度,所以我们来实现一个高级类型求字符串的长度。字符串长度不确定,显然需要递归。一次取一个数,数完即为绳子的长度。输入StrLen=Strextends`${string}${inferRest}`?StrLen:CountArr['length']类型参数Str是要处理的字符串。类型参数CountArr是一个用于计数的数组,默认值[]表示从0开始。每次通过模式匹配提取去掉一个字符后的剩余字符串,再向计数数组中放入一个元素。递归获取字符并计数。如果不满足模式匹配,则表示计数结束,返回计数数组CountArr['length']的长度。这样就可以求出字符串的长度:GreaterThan可以做计数,也可以比较两个值。我们不断将元素放入数组类型中以获得长度。如果A在前,则B更大;否则,A更大:键入GreaterThan=Num1extendsNum2?false:CountArr['length']扩展Num2?真:CountArr['length']扩展Num1?false:GreaterThan;类型参数Num1和Num2是要比较的两个数。类型参数CountArr用于计数,会不断累加。默认值为[],表示从0开始。如果Num1extendsNum2为true,则表示它们相等,直接返回false。否则判断计数数组的长度。如果先到达Num2,则Num1更大,返回true。相反,如果Num1先到达,则Num2更大并返回false。如果没有,则将一个元素放入计数数组CountArr中,然后递归继续。这样可以进行数值比较。3与4比较时:6与4比较时:斐波那契说到数值运算,就不得不提到经典斐波那契数列的计算。斐波那契数列是1,1,2,3,5,8,13,21,34,...这样的数列具有当前数是前两个数之和的规律。F(0)=1,F(1)=1,F(n)=F(n-1)+F(n-2)(n≥2,n∈N*)为递归加法,在TypeScript中类型编程中,这种加法是通过构造数组实现的:typeFibonacciLoop=IndexArr['length']扩展数?CurrentArr['length']:FibonacciLoop类型Fibonacci=FibonacciLoop<[1],[],[],数量>;类型参数PrevArr是一个数组,表示之前的累加值。类型参数CurrentArr是一个表示当前值的数组。类型参数IndexArr用于记录索引,每次递归加一,默认值为[],表示从0开始。类型参数Num表示要查找的序列号。判断当前索引即IndexArr['length']是否达到Num,返回当前值CurrentArr['length']。否则,找到当前索引对应的值,将当前数与前一个数相加[...PrevArr,...CurrentArr]。然后继续递归,index+1,即[...IndexArr,unknown]。这就是递归计算斐波那契数列数的过程。可以正确计算出第8个数是21:总结TypeScript类型系统没有加减乘除运算符,所以我们通过构造和提取数组类型,然后取长度来实现数值运算。我们通过构造和抽取数组类型实现了加减乘除,也实现了各种计数逻辑。用数组长度计数是TypeScript类型体操中最麻烦的方面之一,也是最让新手感到困惑的方面。