是否有机制在ES6(ECMAScript 6)中循环x次,没有可变变量? [英] Is there a mechanism to loop x times in ES6 (ECMAScript 6) without mutable variables?

查看:122
本文介绍了是否有机制在ES6(ECMAScript 6)中循环x次,没有可变变量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  for(var i = 0; i  doStuff(i); 

但是我不想使用 ++ 运算符或完全可变变量。那么有一种办法,在ES6中,以另一种方式循环 x ?我喜欢Ruby的机制:

  x.times do | i | 
do_stuff(i)
end

在JavaScript / ES6中类似的东西?我可以作弊,让我自己的生成器:

  function * times(x){
for = 0; i yield i;
}

for(var i of times(5)){
console.log(i);
}

当然我还在使用 i ++ 。至少它是不可见的:),但我希望在ES6中有一个更好的机制。

解决方案

p>

下面的代码是使用ES6语法编写的,但可以很容易地用ES5或更少的方式写入。 ES6是创建循环x次机制的要求






如果你不需要回调的迭代器,这是最简单的实现



  const times = x => f => {if(x> 0){f()times(x-1)(f)}} //使用ittimes(3)(()=> console.log('hi'))//或定义中间(2)//两倍的功能(()=> console.log('double vision')) 



如果您需要迭代器,可以使用带有counter参数的命名内部函数来迭代< data =truedata-bab =false>

  const times = n => f => {let iter = i => {if(i === n)return f(i)iter(i + 1)} return iter(0)} times(3)(i => console.log(i,'hi')) c $ c> 







如果你不喜欢学习更多的东西,请不要在这里阅读...


...




  • 单个分支如果语句是丑陋—

  • 功能主体中的多个语句/表达式—

  • 隐式返回 undefined —不正确的副作用功能



没有更好的方法吗? >



有。我们首先重新审视我们的初始实现

  // times :: Int  - >(void  - > void) - > void 
const times = x => f => {
if(x> 0){
f() //必须是副作用函数
次(x - 1)(f)
}
}

当然,这很简单,我们如何调用 f(),不要做任何事情。这真的限制了我们可以重复多次的功能类型。即使我们有迭代器可用, f(i)也不是多功能。



如果我们从更好的功能重复程序开始?可能会更好地使用输入和输出。



通用功能重复



  // repeat :: forall a。 Int  - > (a→a)→> a  - > aconst repeat = n => f => x => {if(n> 0)return repeat(n-1)(f)(f(x))else return x} // power :: Int  - > Int  - > Intconst power = base => exp => {// repeat< exp>次,< base> *< x>从1返回重复(exp)(x => base * x)(1)} console.log(power(2)(8))// => 256  



上面我们定义了一个通用的重复函数,该函数需要一个用于启动单个函数的重复应用的附加输入。

  //重复3次,函数f从x开始
var result = repeat(3)(f)(x)

//与..相同。
var result = f(f(f(x)))






实施重复



现在很容易,几乎所有的工作都已经完成了。



  // repeat :: forall一个。 Int  - > (a→a)→> a  - > aconst repeat = n => f => x => {if(n> 0)return repeat(n-1)(f)(f(x))else return x} // times :: Int  - > (Int→Int)→> Int const times = n => F => repeat(n)(i =>(f(i),i + 1))(0)//使用ittimes(3)(i => console.log(i,'hi')) 



由于我们的功能需要 i 作为输入,并返回 i + 1 ,这有效地作为我们的迭代器,我们每次传递给 f



我们已经修复了项目符号列表




  • 不再丑陋单个分支如果语句

  • 单个表达式主体表示很好地分离的关注

  • 没有更多的无用,隐含地返回 undefined






JavaScript逗号运算符



如果您无法看到上一个示例的工作原理,则取决于您对JavaScript最古老的战轴逗号运算符 - 简而言之,它评估表达式从左到右,返回最后计算表达式的值

 (expr1 :: a ,expr2 :: b,expr3 :: c):: c 

在上面的例子中,使用

 (i =&(f(i),i + 1))

这只是一种简洁的写作方式

 (i => {f(i); return i + 1})






尾部调用优化



与递归实现一样性感,在这一点上是不负责任的因为我认为没有JavaScript虚拟机可以支持正确的尾部呼叫消除 - babel曾经使用它,但是它已经破碎,将重新实现一年以上。 / p>

  repeat(1e6)(someFunc)(x)
// => RangeError:最大调用堆栈大小超过

因此,我们应该重新访问我们的

下面的代码使用可变变量 n x 但请注意,所有突变都本地化到重复函数 - 否状态变化(突变)从函数外部可见



  // repeat :: forall a。 Int  - > (a→a)→> a  - > aconst repeat = n => f => x => {while(true){if(n === 0)return x else(n = n-1,x = f(x))}} const inc = x => x + 1console.log(repeat(1e8)(inc)(0))// 100000000  


The typical way to loop x times in JavaScript is:

for (var i = 0; i < x; i++)
  doStuff(i);

But I don't want to use the ++ operator or have any mutable variables at all. So is there a way, in ES6, to loop x times another way? I love Ruby's mechanism:

x.times do |i|
  do_stuff(i)
end

Anything similar in JavaScript/ES6? I could kind of cheat and make my own generator:

function* times(x) {
  for (var i = 0; i < x; i++)
    yield i;
}

for (var i of times(5)) {
  console.log(i);
}

Of course I'm still using i++. At least it's out of sight :), but I'm hoping there's a better mechanism in ES6.

解决方案

OK!

The code below is written using ES6 syntaxes but could just as easily be written in ES5 or even less. ES6 is not a requirement to create a "mechanism to loop x times"


If you don't need the iterator in the callback, this is the most simple implementation

const times = x => f => {
  if (x > 0) {
    f()
    times (x - 1) (f)
  }
}

// use it
times (3) (() => console.log('hi'))

// or define intermediate functions for reuse
let twice = times (2)

// twice the power !
twice (() => console.log('double vision'))

If you do need the iterator, you can use a named inner function with a counter parameter to iterate for you

const times = n => f => {
  let iter = i => {
    if (i === n) return
    f (i)
    iter (i + 1)
  }
  return iter (0)
}

times (3) (i => console.log(i, 'hi'))


Stop reading here if you don't like learning more things ...

But something should feel off about those...

  • single branch if statements are ugly — what happens on the other branch ?
  • multiple statements/expressions in the function bodies — are procedure concerns being mixed ?
  • implicitly returned undefined — indication of impure, side-effecting function

"Isn't there a better way ?"

There is. Let's first revisit our initial implementation

// times :: Int -> (void -> void) -> void
const times = x => f => {
  if (x > 0) {
    f()               // has to be side-effecting function
    times (x - 1) (f)
  }
}

Sure, it's simple, but notice how we just call f() and don't do anything with it. This really limits the type of function we can repeat multiple times. Even if we have the iterator available, f(i) isn't much more versatile.

What if we start with a better kind of function repetition procedure ? Maybe something that makes better use of input and output.

Generic function repetition

// repeat :: forall a. Int -> (a -> a) -> a -> a
const repeat = n => f => x => {
  if (n > 0)
    return repeat (n - 1) (f) (f (x))
  else
    return x
}

// power :: Int -> Int -> Int
const power = base => exp => {
  // repeat <exp> times, <base> * <x>, starting with 1
  return repeat (exp) (x => base * x) (1)
}

console.log(power (2) (8))
// => 256

Above, we defined a generic repeat function which takes an additional input which is used to start the repeated application of a single function.

// repeat 3 times, the function f, starting with x ...
var result = repeat (3) (f) (x)

// is the same as ...
var result = f(f(f(x)))


Implementing times with repeat

Well this is easy now; almost all of the work is already done.

// repeat :: forall a. Int -> (a -> a) -> a -> a
const repeat = n => f => x => {
  if (n > 0)
    return repeat (n - 1) (f) (f (x))
  else
    return x
}

// times :: Int -> (Int -> Int) -> Int 
const times = n=> f=>
  repeat (n) (i => (f(i), i + 1)) (0)

// use it
times (3) (i => console.log(i, 'hi'))

Since our function takes i as an input and returns i + 1, this effectively works as our iterator which we pass to f each time.

We've fixed our bullet list of issues too

  • No more ugly single branch if statements
  • Single-expression bodies indicate nicely separated concerns
  • No more useless, implicitly returned undefined

JavaScript comma operator

In case you're having trouble seeing how the last example is working, it depends on your awareness of one of JavaScript's oldest battle axes; the comma operator – in short, it evaluates expressions from left to right and returns the value of the last evaluated expression

(expr1 :: a, expr2 :: b, expr3 :: c) :: c

In our above example, I'm using

(i => (f(i), i + 1))

which is just a succinct way of writing

(i => { f(i); return i + 1 })


Tail Call Optimisation

As sexy as the recursive implementations are, at this point it would be irresponsible for me to recommend them given that no JavaScript VM I can think of supports proper tail call elimination – babel used to transpile it, but it's been in "broken; will reimplement" status for well over a year.

repeat (1e6) (someFunc) (x)
// => RangeError: Maximum call stack size exceeded

As such, we should revisit our implementation of repeat to make it stack-safe.

The code below does use mutable variables n and x but note that all mutations are localized to the repeat function – no state changes (mutations) are visible from outside of the function

// repeat :: forall a. Int -> (a -> a) -> a -> a
const repeat = n => f => x => {
  while (true) {
    if (n === 0)
      return x
    else
      (n = n - 1, x = f (x))
  }
}

const inc = x => x + 1

console.log(repeat (1e8) (inc) (0)) // 100000000

这篇关于是否有机制在ES6(ECMAScript 6)中循环x次,没有可变变量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆