Open akira-cn opened 5 years ago
感觉上生成扑克牌其实用generator还是大材小用了。
可能这样就够了:
const cards = []
for (const suit of ['♠️','♣️','♥️','♦️']) for (const point of '123456789TJQK') cards.push([suit, point])
或者
const cards = ['♠️','♣️','♥️','♦️'].flatMap(suit => [...'123456789TJQK'].map(point => [suit, point]))
一种函数组合风格
const pipe = (...args) => args.reduce((a, f) => f(a))
const bind = f => list => [...list.map(f)]
const flat = list => list.flat(1)
const pair = list => a => pipe(list, bind(b => [a, b]))
const cards = pipe(['♠️', '♣️', '♥️', '♦️'], bind(pair(Array.from('123456789TJQK'))), flat)
在项目中我们经常会遇到初始化数据的需求,比如创建一个100个元素的数组,让每个元素的初始值为0。
非常传统的一种方式就是创建好数组,然后用循环来赋初始值。
有同学可能想用数组的迭代方法来赋值,比如:
或者:
但是事实上这两种方式都是不行的,因为数组被创建的时候,元素的初始值是
empty
,而迭代方法并不会遍历数组中empty的元素,for...in
也一样。这个规则对于访问稀疏数组中的元素是有帮助的,能提升效率,但不在这篇文章讨论的范围,后续有机会我们会对数组迭代进行单独讨论,有兴趣的同学可以关注。
回到主题,虽然直接创建数组时数组元素的默认值是empty,但是因为数组是一个可迭代对象,所以我们可以用spread操作将它展开,再进行迭代就可以了,所以:
此外,我们还可以通过
Array.from
方法创建初始数组。👉🏻 Array.from方法从一个类数组或可迭代对象中创建一个新的、浅拷贝的数组实例。
一个包含length属性,且值为非负整数的对象会被当做类数组对象,被
Array.from
处理成一个长度为length的数组。👉🏻Array.from 对象的第二个参数是迭代算子(Map Functor),所以我们不必用
Array.from + map
转两次,直接一次转换就可以了:由于我们例子中初始化数组的初始值是固定的数值0,所以这里我们其实可以使用
Array.prototype.fill
方法。💡注意fill方法用来填充元素的值如果是引用类型,它并不会拷贝这个值,而是直接将引用赋给数组,这也就是说,如果我们企图通过fill来初始化二维数组,是有问题的。
上面的代码,matrix[0]、matrix[1]和matrix[2]指向同一个引用。
另外,fill还有两个可选的参数,可以对数组进行部分赋值:
所以对于初始化相同的非引用类型值的数组,使用
Array.prototype.fill
是比较简单的方案。但是,如果我们初始化的值不同呢?
比如我们想初始化一个长度10的数组,初始值分别是0~9。
通过前面的
Array.from
是一个简单的办法:就这个问题还有个取巧的办法,因为我们要初始化的值恰好是数组元素的下标值,所以:
👉🏻
Array.protoype.keys
返回一个以数组下标为迭代值的可迭代对象,将它展开就是我们要的结果了。使用生成器
对于更复杂的初始化需求,我们可以构建可复用的生成器。
在这里,有同学可能会说,我们不用生成器,直接将数组构建出来也是可以的:
这个的确是可以的,不过用生成器将构建迭代器和生成数组的步骤分开,会更灵活。
我们稍微修改一下生成器,构建更复杂的数据:
👉🏻yield* 可以将一个可迭代对象委托给一个生成器,所以上面的代码判断如果value返回的是一个可迭代对象,那么递归迭代这个对象并返回,所以我们用它来生成一个3x3的初始矩阵,它是一个长度为9的数组,初始值是
[1, 0, 0, 0, 1, 0, 0, 0, 1]
。上面的代码有一个问题,就是它会将所有可迭代对象继续展开,这也许不是我们期望的结果,比如:
可能我们的预期是初始化成
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
,而不是完全展开成[0, 0, 0, 0, 0, 0, 0, 0, 0]
。当然我们可以把调用代码修改一下,嵌套一层数组:
但是这很容易给使用者造成困扰。因此我们可以修改一下设计,只有mapFunc是生成器函数的时候,才用
yield*
迭代展开,否则直接yield
返回。最后,再强调一下,生成器是个好东西,用它来写简洁易读的代码来初始化数据吧,比如下面的代码初始化一副扑克牌:
[花色, 点数]
(不包括大小王):关于生成结构化的初始数据,你还有什么想要讨论的,欢迎在issue中讨论。