【问题标题】:making a range function in javascript在javascript中制作范围函数
【发布时间】:2016-09-30 19:47:19
【问题描述】:

我正在尝试在 javascript 中创建一个函数,该函数从 range(start,end) 返回一个数组,并且我应该创建一个可选参数,当它未定义时默认为一个。当我提供所有参数时,我可以让函数工作,但当我只传递两个参数时返回一个空数组。问题来了:

编写一个范围函数,它接受两个参数,start 和 end,并返回一个数组,其中包含从 start 到(包括)end 的所有数字。

接下来,编写一个 sum 函数,它接受一个数字数组并返回这些数字的总和。运行前面的程序,看看它是否确实返回了 55。

作为一个额外的分配,修改你的 range 函数以采用可选的第三个参数,该参数指示用于构建数组的“步长”值。如果未指定步长,则数组元素以 1 为增量递增,对应于旧行为。函数调用 range(1, 10, 2) 应该返回 [1, 3, 5, 7, 9]。确保它也适用于负步长值,以便 range(5, 2, -1) 产生 [5, 4, 3, 2]。

这是我的代码:

function range(start, end, increment){
var array = [];
var current = start;
var counter;
if (increment == undefined){
    counter = 1;
}

else {
    counter = increment;
}

if (increment > 0){
    while(current <= end){
        array.push(current);
        current += counter;
    }
}
  
else if (increment < 0){
    while(current >= end){
        array.push(current);
        current += counter;
        
    }
}

return array;
}

有人能解释为什么它会坏吗?我知道一些 c#,并且我曾经能够在出现与 javascript 不同的问题时跳入 Visual Studio 中的调试器。

【问题讨论】:

  • JS 也有很多调试器。 Chrome中的那个可能是最好的。弹出它,您将能够单步执行代码。
  • 常见的 JS 习语 if undefined, be this instead 是:var counter = increment || 1;
  • @4castle 你能进一步解释一下吗?不太确定这是如何工作的。使用? 运算符会更好吗?
  • @Nate 请参阅this question,它很好地解释了它。如果左边的值为 falsey(又名undefinednull0""falseNaN),那么它将分配右边的值.

标签: javascript


【解决方案1】:

一个非常简单的单向(递增),包含范围——从xy,每次递增1

// range :: (Int, Int) -> [Int]
const range = (x,y) =>
  x > y ? [] : [x, ...range(x + 1, y)];

console.log(range(1,4)); // [1,2,3,4]
console.log(range(3,3)); // [3]
console.log(range(6,3)); // []

支持双向(升序降序)范围的轻微调整——仍以1递增或递减@

// range :: (Int, Int) -> [Int]
const range = (x,y) => {
  if (x > y)
    return range(y,x).reverse();
  else
    return x === y ? [y] : [x, ...range(x + 1, y)];
}

console.log(range(1,4)); // [1,2,3,4]
console.log(range(3,3)); // [3]
console.log(range(6,3)); // [6,5,4,3]

另一个使用高阶函数来更好地控制范围的改编——这有效地为你提供了一些你正在寻找的步进/递增行为——虽然这更强大,因为它允许你使用一个函数,t , 选择下一个值。

const gte = x => y => y >= x;
const lte = x => y => y <= x;
const add = x => y => y + x;
const sub = x => y => y - x;

// range :: (Int, (Int -> Bool), (Int -> Int)) -> [Int]
const range = (x, p, t) => {
  if (p(x))
    return [x, ...range(t(x), p, t)];
  else
    return [];
};

console.log(range(2, lte(8), add(2))); // [2,4,6,8]
console.log(range(9, gte(0), sub(3))); // [9,6,3,0]
console.log(range(9, gte(0), sub(5))); // [9, 4]

// very power. wow.
const double = x => x + x;
console.log(range(2, lte(50), double)); // [2,4,8,16,32]

此函数具有与 forwhile 相同的固有风险 - 由您决定不要将其置于无限循环中。


功能过载

警告:前面有深奥的、不切实际的功能。以下信息仅供您学术娱乐。

range 函数也恰好是我最喜欢的 Y 组合器演示之一。我将在这里向您展示两个示例。

天真range

const U = f => f (f);
const Y = U (h => f => f (x => h (h) (f) (x)));

const range = Y (f => acc => x => y =>
  x > y ? acc : f ([...acc, x]) (x + 1) (y)
) ([]);

console.log(range (3) (6)); // [3,4,5,6]
console.log(range (6) (6)); // [6]
console.log(range (9) (6)); // []

和更高阶的range

const U = f => f (f);
const Y = U (h => f => f (x => h (h) (f) (x)));

const lt = x => y => y < x;
const gt = x => y => y > x;
const add1 = x => x + 1;
const sub1 = x => x - 1;

const range = Y (f => acc => x => p => t =>
  p(x) ? f ([...acc, x]) (t(x)) (p) (t) : acc 
) ([]);

console.log(range (3) (lt(6)) (add1)); // [3,4,5]
console.log(range (6) (lt(6)) (add1)); // []
console.log(range (9) (gt(6)) (sub1)); // [9,8,7]

这是多么美好的事情。

【讨论】:

    【解决方案2】:

    您可以稍微简化代码并使用increment 变量进行递增。但在此之前,我建议测试该值是否为假(0nullundefined 等),然后将 1 分配给它。

    未实现:检查startend 是否合适。

    function range(start, end, increment) {
        var array = [];
        var current = start;
    
        increment = increment || 1;
        if (increment > 0) {
            while (current <= end) {
                array.push(current);
                current += increment;
            }
        } else {
            while (current >= end) {
                array.push(current);
                current += increment;
            }
        }
        return array;
    }
    
    console.log(range(1, 3, 0));    
    console.log(range(2, 5));
    console.log(range(1, 9, 1));
    console.log(range(5, 2, -1));    

    【讨论】:

    • 这很好,因为它还可以防止第三个参数为0时的无限循环。
    • @4castle 我会反对。将您从自己手中拯救出来并不是实用程序的目的。没有什么可以阻止您将doforwhile 放入无限循环中。为什么我们编写的函数应该有所不同?我实际上更喜欢具有硬边的 API。我觉得我们没有指定的行为应该是undefined。请参阅我的回答以澄清我的观点。
    • @NinaScholz,除此之外,这是一个非常好的答案^_^
    • @naomik 这叫做防御性编程。不过我同意,如果它在increment === 0 时返回undefined 会更好,或者可能是一个空数组。
    【解决方案3】:

    首先您检查increment 是否未定义并相应地设置counter,但稍后您再次检查if (increment &gt; 0){。虽然是undefined,但您的所有案例都不匹配,所以什么也没有发生。

    把你的支票改成这样:

    if (counter > 0){
      // ...
    }
    else if (counter < 0){
      // ...
    }
    

    【讨论】:

      【解决方案4】:

      给出的答案很棒。我只是想告诉你一个更实用的方法如何解决这个任务:

      // auxiliary functions:
      const append = (x, xs) => xs.concat([x]);
      const prepend = (x, xs) => [x].concat(xs);
      
      // main function
      const range = (x, y, step, acc = [], op = append) =>
       step && step < 0
        ? range(y, x, -step, acc, prepend)
        : step && x <= y
         ? range(x + step, y, step, op(x, acc), op) // tail call
         : acc;
      
      console.log(range(1,5,1)); // [1,2,3,4,5]
      console.log(range(1,5,2)); // [1,3,5]
      console.log(range(1,5,6)); // [1]
      console.log(range(5,1,1)); // []
      console.log(range(1,5,0)); // []
      console.log(range(5,1,-1)); // [5,4,3,2,1]
      console.log(range(5,1,-2)); // [5,3,1]
      console.log(range(5,1,-6)); // [1]
      console.log(range(1,5,-1)); // []
      

      算法:

      • acc = []op = append 默认参数值(如果在函数调用期间省略,则采用)
      • 如果step 为零,则step &amp;&amp; step &lt; 0 短路,否则检查步长是否为负
      • step 为负数时调用range(y, x, -step, acc, prepend),并转换range 的参数化,使step 可以为正数(注意-step 等价于-(-1),计算结果为1)李>
      • range(x + step, y, step, op(x, acc), op) 递归情况,这意味着函数调用自身(注意 op 可以是 appendprepend,具体取决于 step 的初始符号,x 增加 step 并附加/添加到acc)
      • acc 停止递归并返回累积数组的基本情况

      【讨论】:

        【解决方案5】:

        处理浮点数和负数的非常紧凑的范围函数:

        const range = (lower,upper,step)=>{
          return Array.from(new Array(Math.floor(upper/step-lower/step)+1),(_,i)=>lower/step+i).map(x=>x*step)
        }
        

        例如,您可以像这样使用它:

        range(10,30,3) // [10, 13, 16, 19, 22, 25, 28]
        range(0,0.5,0.01) // [0, 0.01, 0.02, ... , 0.48, 0.49, 0.5]
        range(1,10,2) // [1, 3, 5, 7, 9]
        range(-5,10,0.5) // [-5, -4.5, -4, ... , 1, 1.5, 2]
        range(5,2,-0.5) // [5, 4.5, 4, 3.5, 3, 2.5, 2]
        

        这里是一个更容易理解的版本:

        const range = (lower, upper, step) => {
          end = upper / step // Upper bound
          start = lower / step // Lower bound
          n = Math.floor(end - start) + 1 // Size that includes upper bound as well
          zeros_arr = Array(n).fill(0) // Init array with n zeros
          unscaled_arr = zeros_arr.map((_, i) => (start + i)) // Setting each zero to the upper bound + the index
          range_arr = unscaled_arr.map(x => (x * step)) // Scaling every numbers with the step
          return range_arr
        }
        

        【讨论】:

          【解决方案6】:

          我在 eloquent javascript 课程中解决了这个问题。根据问题陈述,我选择default parameters 作为增量,并使用while 循环来包含第二个参数。

          
          function range(start, stop, increment=1)
          {
            let range_arr = [];
            if(start < stop)
            {
              while( start <= stop)
              {
                range_arr.push(start);
                start += increment;
              }
            }
            else{
              while( start >= stop)
              {
                range_arr.push(start);
                start += increment;
              }
            }
            return range_arr;
          }
          
          

          【讨论】:

            【解决方案7】:

            我决定使用 for 循环来创建增量,如下所示。

            function range(start,end) {
              let array = [];
              
              for (let counter = 0; counter < end; counter++) {
                array.push(start);
                start += 1;
              }
              return array;
              }
              console.log(range(1,10)); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

            【讨论】:

              猜你喜欢
              • 2011-11-15
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-07-04
              • 1970-01-01
              • 2013-11-04
              • 2011-11-13
              • 1970-01-01
              相关资源
              最近更新 更多