这是我参与「第五届青训营 」笔记创作活动的第5天
重点内容
- 代码写作关注事项
- left-pad 事件背后的代码规范
- 代码实践1 - 交通灯
- 代码实践2 - 洗牌
- 代码实践3 - 分红包
代码写作关注事项
风格、效率、约定、使用场景、设计
当年的Left-pad事件
- 需要关注的问题
- 模块粒度
- 代码风格
- 代码质量/效率
代码实践
交通灯
实现一个切换多个交通灯状态切换的功能
版本一
- 每隔一秒钟进行切换
版本二(数据抽象)
- 将交通灯的状态进行抽象,每个状态持续的时间不同
版本三(过程抽象)
- 定义 wait 方法,表示等待
- 定义 poll 方法,表示轮询
版本四(异步+函数式)
- 定义 wait 方法,表示等待
- 定义 setState方法,设置className
- 通过 while 循环不断重复轮询过程
判断是否是4的幂
方法一
- 通过 while 循环对4取模
function isPowerOfFour(num) {
num = parseInt(num);
while(num > 1) {
if(num % 4) return false;
num /= 4;
}
return num === 1;
}
方法二
- 通过 while 循环进行位操作
- 位操作:通过按位与 0b11,判断二进制数最后两位是否为0
- 若是0:右移两位
- 不是0:说明该数不是4的幂
function isPowerOfFour(num) {
num = parseInt(num);
while(num > 1) {
if(num & 0b11) return false;
num >>>=2;
}
return num === 1;
}
方法三
- 通过三个条件进行判断:
- 数值是否大于0
- 二进制数num 与 num-1 相与,结果少一个1
- 依据:2/4的幂转为二进制数后只有一个1
- num 与 十六进制数 0xAAAAAAAAAAAAA 相与,判断偶数位是否为1
- 依据:4的幂转为二进制数后0的个数为偶数
function isPowerOfFour(num){
num = parseInt(num);
return num > 0 &&
(num & (num - 1)) === 0 &&
(num & 0xAAAAAAAAAAAAA) === 0;
}
方法四
- 通过正则表达式判断
function isPowerOfFour(num) {
num = parseInt(num).toString(2);
return /^1(?:00)*$/.test(num);
}
洗牌
错误方法
- 使用 sort 方法排序导致洗牌次数不均匀
const cards = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
function shuffle(cards) {
return [...cards].sort(() => Math.random() > 0.5 ? -1 : 1);
}
console.log(shuffle(cards));
const result = Array(10).fill(0);
for(let i = 0; i < 1000000; i++) {
const c = shuffle(cards);
for(let j = 0; j < 10; j++) {
result[j] += c[j];
}
}
console.table(result);
正确方法
- 遍历数组
- 随机取出一位数并放到数组末尾
- 剩下的数循环上述步骤
const cards = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
function shuffle(cards) {
const c = [...cards];
for(let i = c.length; i > 0; i--) {
const pIdx = Math.floor(Math.random() * i);
[c[pIdx], c[i - 1]] = [c[i - 1], c[pIdx]];
}
return c;
}
console.log(shuffle(cards));
const result = Array(10).fill(0);
for(let i = 0; i < 10000; i++) {
const c = shuffle(cards);
for(let j = 0; j < 10; j++) {
result[j] += c[j];
}
}
console.table(result);
改进:生成器
- 不做完循环,直接使用yield方法
const cards = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
function * draw(cards){
const c = [...cards];
for(let i = c.length; i > 0; i--) {
const pIdx = Math.floor(Math.random() * i);
[c[pIdx], c[i - 1]] = [c[i - 1], c[pIdx]];
yield c[i - 1];
}
}
const result = draw(cards);
console.log([...result]);
- 完整代码
分红包
切西瓜法
- 将红包总额随机分成两份
- 对数值较大的一份重复第一步
- 直到红包数量足够
function generate(amount, count){
let ret = [amount];
while(count > 1){
//挑选出最大一块进行切分
let cake = Math.max(...ret),
idx = ret.indexOf(cake),
part = 1 + Math.floor((cake / 2) * Math.random()),
rest = cake - part;
ret.splice(idx, 1, part, rest);
count--;
}
return ret;
}
抽牌法
- 随机抽出 n-1 个数,作为数组分隔符
- 例:抽出49和199,4.9为一份,19.9-4.9为一份
function * draw(cards){
const c = [...cards];
for(let i = c.length; i > 0; i--) {
const pIdx = Math.floor(Math.random() * i);
[c[pIdx], c[i - 1]] = [c[i - 1], c[pIdx]];
yield c[i - 1];
}
}
// 0, 1, 2....9999
// 49 199
function generate(amount, count){
if(count <= 1) return [amount];
const cards = Array(amount - 1).fill(0).map((_, i) => i + 1);
const pick = draw(cards);
const result = [];
for(let i = 0; i < count - 1; i++) {
result.push(pick.next().value);
}
result.sort((a, b) => a - b);
result.push(amount);
for(let i = result.length - 1; i > 0; i--) {
result[i] = result[i] - result[i - 1];
}
return result;
}