TylerYang / practises

0 stars 0 forks source link

randomArray 的实现思路 #1

Open TylerYang opened 6 years ago

TylerYang commented 6 years ago

最近感觉略无聊,分享一道来自@phodal 微信公众号的一道题,简单而有趣.

编写一个javascript 函数randomArray, 该函数接受参数n,返回值为一个数组。为n个随机且不重复的整数,且整数的取值范围是[2, 32]。

考核点可以概括为三个,可靠性,健壮性以及实现细节。

可靠性

考察一个代码最基础的要求在于可用,简单说就是正确的输入能保证有正确的输出。仅考虑可用性,不考虑容错,健壮,代码风格,可用数据结构等,略为笨拙的实现为,

function randomArray(n) {
  const set = {}
  const result = []
  for (let i = 0; i < n; i++) {
    let num = randomBetween(MIN, MAX)
    while (set.hasOwnProperty(num)) {
      num = randomBetween(MIN, MAX)
    }
    set[num] = true
    result.push(num)
  }
  return result
}

健壮性

健壮性考察你的代码是否足够强壮,比如在这个实现中,你的代码是否能不同输入的时候,能符合预期的运行?

举个例子,输入参数n可以为任何类型值(嗯,javascript 没有类型检查),我们需要自己对类型做校验。比如允许的输入是整型,浮点型以及字符串类型的数字,该如何处理? n 的最大数应该为32 - 2 + 1 => 30, 最小数为0的情况下,如何处理?

总结下,需要处理的情况为

在这基础上,笨拙的实现可以为,

const MAX = 31
const MIN = 2
function randomArray(n) {
  if (typeof n !== 'string' && typeof n !== 'number') {
    throw new Error('Invalid type of parameter, should be integer number')
  }
  if (typeof n === 'string' || (n % 1) > 0) {
    debug(`Incorrect parameter, required integer, but got '${extractType(n)}'`)
    n = parseInt(n, 10)
  }

  if (n < 1 || n > MAX - MIN + 1) {
    throw new Error(`Invalid parameter, should be between 1 and ${MAX - MIN + 1}`)
  }

  const set = {}
  const result = []
  for (let i = 0; i < n; i++) {
    let num = randomBetween(MIN, MAX)
    while (set.hasOwnProperty(num)) {
      num = randomBetween(MIN, MAX)
    }
    set[num] = true
    result.push(num)
  }
  return result
}

实现细节

看到这里,熟悉es6 的同学会将set 换成Set 而数组result则可以直接被spread operator 所取代。 代码可以实现为,

const MAX = 31
const MIN = 2
function randomArray(n) {
  if (typeof n !== 'string' && typeof n !== 'number') {
    throw new Error('Invalid type of parameter, should be integer number')
  }
  if (typeof n === 'string' || (n % 1) > 0) {
    debug(`Incorrect parameter, required integer, but got '${extractType(n)}'`)
    n = parseInt(n, 10)
  }

  if (n < 1 || n > MAX - MIN + 1) {
    throw new Error(`Invalid parameter, should be between 1 and ${MAX - MIN + 1}`)
  }
  // set 从object换成了Set
  const set = new Set()
  for (let i = 0; i < n; i++) {
    let num = randomBetween(MIN, MAX)
    while (set.has(num)) {
      num = randomBetween(MIN, MAX)
    }
    set.add(num)
  }
  // 这里直接用了spread operator 来返回
  return [...set]
}

而再仔细看一下set 的使用,加上set.size,我们可以去掉forloop,

const MAX = 31
const MIN = 2
function randomArray(n) {
  if (typeof n !== 'string' && typeof n !== 'number') {
    throw new Error('Invalid type of parameter, should be integer number')
  }
  if (typeof n === 'string' || (n % 1) > 0) {
    debug(`Incorrect parameter, required integer, but got '${extractType(n)}'`)
    n = parseInt(n, 10)
  }

  if (n < 1 || n > MAX - MIN + 1) {
    throw new Error(`Invalid parameter, should be between 1 and ${MAX - MIN + 1}`)
  }

  const set = new Set()
  // 利用set.size 直接去掉了不必的forloop
  while (set.size < n) {
    set.add(randomBetween(MIN, MAX))
  }

  return [...set]
}

以上只是个人的实现,方式有很多种。

其他诸如node 的util 包的使用,简单搭建测试用例框架并写完对应的测试用例等(如果是后端的话)于我来说都算是额外的加分项。 包括测试框架的搭建,测试用例的书写完成时间应该在30分钟左右。

整理下这道题会发现考察的并没有任何特定api,都是简单的逻辑完整性的考察,简单而全面。这也是我非常喜欢尝试做各种编程问题的原因(比如两三年前我的leetcode java实现)。因为归根到底,在明白设计原理的基础上,API 翻翻文档就好,基础足够扎实,解决问题有条理,完整的思考才是关键。

完整实现戳这里。

phodal commented 6 years ago