[解決済み】ES6(ECMAScript 6)でミュータブル変数を使わずにx回ループする仕組みはありますか?
質問
ループの典型的な方法
x
回をJavaScriptで作成します。
for (var i = 0; i < x; i++)
doStuff(i);
しかし、私は
++
演算子やミュータブル変数を一切使用しません。では、ES6で、ループする方法はあるのでしょうか?
x
の回数は別の方法なのでしょうか?Rubyの仕組みが好きなんです。
x.times do |i|
do_stuff(i)
end
JavaScript/ES6で似たようなものはありますか?私はちょっとズルして自分のジェネレータを作ることができました。
function* times(x) {
for (var i = 0; i < x; i++)
yield i;
}
for (var i of times(5)) {
console.log(i);
}
もちろん、今でも
i++
. 少なくとも見えないところではありますが :) 、ES6でもっと良い仕組みがあることを期待しています。
どのように解決するのか?
OK!
以下のコードはES6の構文で書かれていますが、ES5やそれ以下の構文でも同じように簡単に書くことができます。ES6とは ではなく は、"x回ループする仕組み"を作ることが条件です。
コールバックでイテレータが不要な場合 これは最もシンプルな実装です。
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'))
イテレータが必要な場合 カウンタパラメータを持つ名前付き内部関数を使用して、反復処理を行うことができます。
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'))
もっといろいろなことを知りたいと思わなければ、ここで読むのをやめてください ...
でも、何か違和感があるはず...。
-
シングルブランチ
if
ステートメントは醜いです - 。 もう一方のブランチでは何が起こるのでしょうか? - 関数本体の中に複数の文や式がある場合 プロシージャの懸念が混在しているのでは?
-
暗黙のうちに返される
undefined
- 不純物、副作用のある関数の表示
もっといい方法はないのでしょうか?
ありますよ。まず、最初の実装を再確認してみましょう。
// times :: Int -> (void -> void) -> void
const times = x => f => {
if (x > 0) {
f() // has to be side-effecting function
times (x - 1) (f)
}
}
確かにシンプルですが、ただ単に
f()
で、何もしない。これでは、何度も繰り返すことができる関数の種類も限られてしまいます。たとえイテレータが利用できたとしてもです。
f(i)
は、あまり汎用性がありません。
もっと良い関数の繰り返し手順から始めてはどうでしょうか?入力と出力をうまく利用するような。
汎用的な関数の繰り返し
// 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
上記では、一般的な
repeat
この関数は、1つの関数の繰り返し適用を開始するために使用される追加の入力を取ります。
// 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)))
実装について
times
と
repeat
まあ、これはもう簡単なことで、ほとんどすべての作業は終わっているのです。
// 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'))
この関数は
i
を入力とし
i + 1
に渡すイテレータとして機能します。
f
を毎回実行します。
箇条書きにした課題も修正しました
-
醜いシングルブランチを廃止
if
ステートメント - 単一表現のボディは、きれいに分離された関心事を示します。
-
もう無駄な暗黙の了解は必要ない
undefined
JavaScriptのカンマ演算子である
最後の例がどのように動作しているのかがわからない場合は、JavaScriptの最も古い戦いの軸の一つである カンマ演算子 - つまり、左から右へ式を評価し は以下を返します。 最後に評価された式の値
(expr1 :: a, expr2 :: b, expr3 :: c) :: c
上記の例では
(i => (f(i), i + 1))
というのは、単に簡潔な書き方です。
(i => { f(i); return i + 1 })
テールコール最適化
再帰的な実装はセクシーですが、今のところ JavaScript VM 私は、適切なテールコールの除去をサポートしていると考えることができます - babelはそれをトランスパイルしていましたが、それは1年以上も"broken; will reimplement"の状態になっています。
repeat (1e6) (someFunc) (x)
// => RangeError: Maximum call stack size exceeded
の実装を見直す必要があります。
repeat
をスタックセーフにする。
以下のコード
が行います。
ミュータブル変数を使用する
n
と
x
に局在しているが、すべての変異が
repeat
関数の外からは、状態の変化 (変異) は見えません。
// repeat :: Int -> (a -> a) -> (a -> a)
const repeat = n => f => x =>
{
let m = 0, acc = x
while (m < n)
(m = m + 1, acc = f (acc))
return acc
}
// inc :: Int -> Int
const inc = x =>
x + 1
console.log (repeat (1e8) (inc) (0))
// 100000000
でも、そんなの機能的じゃない!」とおっしゃる方もいらっしゃるでしょう。私たちはClojureスタイルの
loop
/
recur
を使用した定空間ループのためのインターフェースです。
純粋式
そのようなことはありません。
while
のようなものです。
ここでは、抽象的な
while
を、私たちの
loop
関数 - これは特別な
recur
という型を使って、ループを継続させます。非
recur
型に遭遇した場合,ループは終了し,計算結果が返されます.
const recur = (...args) =>
({ type: recur, args })
const loop = f =>
{
let acc = f ()
while (acc.type === recur)
acc = f (...acc.args)
return acc
}
const repeat = $n => f => x =>
loop ((n = $n, acc = x) =>
n === 0
? acc
: recur (n - 1, f (acc)))
const inc = x =>
x + 1
const fibonacci = $n =>
loop ((n = $n, a = 0, b = 1) =>
n === 0
? a
: recur (n - 1, b, a + b))
console.log (repeat (1e7) (inc) (0)) // 10000000
console.log (fibonacci (100)) // 354224848179262000000
関連
-
[解決済み】Heroku:ノードアプリで「このアプリにはデフォルトの言語が検出されませんでした」エラーがスローされる
-
[解決済み】コンソールがUnterminated JSX contentsエラーを投げる【終了しました
-
[解決済み】 Uncaught Error: Invariant Violation: 解決済み】 Uncaught Error: Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function but got: object.
-
[解決済み】Uncaught TypeError: 未定義のプロパティ 'msie' を読み取れない - jQuery tools
-
[解決済み】react router v^4.0.0 Uncaught TypeError: 未定義のプロパティ'location'を読み取れない
-
[解決済み] JavaScriptでNULL、未定義、空白の変数をチェックする標準的な関数はありますか?
-
[解決済み] forループを壊さずに配列をループして項目を削除する
-
[解決済み] JavaScriptで文字列中の変数を連結せずに補間する方法は?
-
[解決済み] for-ofループ内でのES6配列の要素インデックスへのアクセス
-
[解決済み】Node.jsがES6(ECMAScript 2015)モジュールのインポート/エクスポートに対応予定
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】React Js: Uncaught (in promise) SyntaxError: 位置 0 の JSON で予期しないトークン < が発生しました。
-
[解決済み] Uncaught TypeError: 未定義のプロパティ 'top' を読み込めない
-
[解決済み】Reactのeslintエラーはpropsの検証で見つからない
-
[解決済み】React.jsの配列の子要素のユニークキーを理解する
-
[解決済み】Jestが予期しないトークンに遭遇した
-
[解決済み】TypeError: res.status は関数ではありません。
-
[解決済み】Uncaught ReferenceError。Firebase は定義されていません。
-
[解決済み】未定義のプロパティ 'forEach' を読み取ることができない
-
[解決済み] ES2015のみで0からnまでの数値範囲を生成する方法とは?
-
[解決済み] 範囲を反復処理する関数的な方法 (ES6/7) [重複]。