功能性TypeScript和泛型

丹尼·莫克

我已经基于针对同一练习的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 [];

我的第一个问题是:为什么?

我说过groupsis的返回类型,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])没有给出此错误。

我在这里做错了什么?

提香·切尔尼科娃·德拉戈米尔

问题是您的takedrop函数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);

尽管在这种情况下,返回类型不是严格必需的takedrop并且可以根据返回类型进行推断(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] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章