我已经基于针对同一练习的Haskell解决方案创建了用于代码练习的功能性TypeScript解决方案。练习是采用一个非常大的整数,并找到一组长度为n的相邻数字的最大乘积。
我写了一个咖喱函数groups
,该函数接受一个数字数组并返回一个数组数组,每个嵌套数组均由多个数字组成。
例如:
groups([1,2,3,4,5,6,7,8,9])(3)
将返回:
[[1,2,3], [4,5,6], [7,8,9]]
该groups
函数:
type groups = <A>(xs: A[]) => (n: number) => A[][];
const groups: groups = xs => n => xs.length < n ? [] : [take(n)(xs)].concat(groups(drop(1)(xs))(n));
// groups uses the functions 'take' and 'drop':
type take = <A>(n: number) => (xs: A[]) => A[];
const take: take = n => xs => xs.slice(0, n);
type drop = <A>(n: number) => (xs: A[]) => A[];
const drop: drop = n => xs => xs.slice(n);
但是groups
给出了错误:
TS2322: Type '{}[][]' is not assignable to type 'A[][]'.
但是,当我添加as []
错误时,错误消失了:
const groups: groups = xs => n => xs.length < n ? [] : [take(n)(xs)].concat(groups(drop(1)(xs))(n)) as [];
我的第一个问题是:为什么?
我说过groups
is的返回类型,A[][]
参数xs
是type,A[]
那么为什么将其解析为{}[][]
?我不完全了解这是如何工作的。
然后,当我在Webstorm中使用Quokka运行最终解决方案时,我得到了正确答案,但又出现了另一个错误。
一,最终解决方案:
const largestProduct = (count: number): number[] => {
const num = '73167176531330624919225119674426574742355349194934969835203127745063262395';
const digits = num.split('').map((x: string) => parseInt(x, 10));
return Math.max(...map(product)(groups(digits)(count)));
};
// the functions 'map' and 'product' used:
type map = <A, B>(f: (a: A) => B) => (xs: A[]) => B[];
const map: map = f => xs => xs.map(f);
type product = (xs: number[]) => number;
const product: product = xs => xs.reduce((acc, x) => acc * x, 1);
该return
语句行给出了错误:
TS2322: Type 'number' is not assignable to type 'number[]'
该map
函数确实返回了,number[]
但是我在这里使用了散布运算符。例如Math.max(...[1,2,3])
没有给出此错误。
我在这里做错了什么?
问题是您的take
和drop
函数A
在外部函数上具有类型参数,而该参数的推理位置(以参数的形式xs
)在内部函数上。Typescript不能真正解决这个问题,它想在检查第一个调用时确定所有类型参数(例如take(1)
),并且由于无处可推断A
,只能推断出它会{}
导致您的问题。
最简单的解决方案是在内部函数上移动type参数:
type groups = <A>(xs: A[]) => (n: number) => A[][];
const groups: groups = xs => n => xs.length < n ? [] : [take(n)(xs)].concat(groups(drop(1)(xs))(n));
type take = (n: number) => <A>(xs: A[]) => A[];
const take: take = n => xs => xs.slice(0, n);
type drop = (n: number) => <A>(xs: A[]) => A[];
const drop: drop = n => xs => xs.slice(n);
顺便说一句,在您之前定义函数类型的方法上,我不是100%出售的。您可以完全注释箭头功能(包括返回类型):
const groups = <A>(xs: A[]) => (n: number) : A[][] => xs.length < n ? [] : [take(n)(xs)].concat(groups(drop(1)(xs))(n));
const take = (n: number) => <A>(xs: A[]) : A[]=> xs.slice(0, n);
const drop = (n: number) => <A>(xs: A[]): A[] => xs.slice(n);
尽管在这种情况下,返回类型不是严格必需的take
,drop
并且可以根据返回类型进行推断(group
由于函数的递归性质,所以必须这样做)
const groups = <A>(xs: A[]) => (n: number) : A[][] => xs.length < n ? [] : [take(n)(xs)].concat(groups(drop(1)(xs))(n));
const take = (n: number) => <A>(xs: A[]) => xs.slice(0, n);
const drop = (n: number) => <A>(xs: A[]) => xs.slice(n);
我的首选不是写出编译器可以找出的类型。那几次我实际上想要符号的完整类型,我只需要将其悬停在符号上就可以知道为省略的类型推断出的TS。但是就像我说的那样,这只是一种偏爱,您的方式也不错:)
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句