From 7265e6ff97381196cae586c7e21bd958bd7f9dd5 Mon Sep 17 00:00:00 2001 From: azu Date: Wed, 6 Jul 2016 20:15:39 +0900 Subject: [PATCH 1/8] =?UTF-8?q?feat(loop):=20for=E6=96=87=E3=80=81forEach?= =?UTF-8?q?=E3=80=81reduce=E3=81=AE=E3=82=84=E3=82=8A=E6=96=B9=E3=82=92?= =?UTF-8?q?=E6=AF=94=E8=BC=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prh.yml | 4 + source/README.md | 1 + source/basic/loop/README.md | 113 +++++++++++++++++++ source/basic/loop/src/sum-for-example.js | 9 ++ source/basic/loop/src/sum-forEach-example.js | 9 ++ source/basic/loop/src/sum-reduce-example.js | 7 ++ 6 files changed, 143 insertions(+) create mode 100644 source/basic/loop/README.md create mode 100644 source/basic/loop/src/sum-for-example.js create mode 100644 source/basic/loop/src/sum-forEach-example.js create mode 100644 source/basic/loop/src/sum-reduce-example.js diff --git a/prh.yml b/prh.yml index 7f358a08ea..b1dafce6b0 100644 --- a/prh.yml +++ b/prh.yml @@ -87,6 +87,10 @@ rules: - expected: break文 patterns: - break式 + - expected: 反復処理 + patterns: + - 繰り返し処理 + prh: 繰り返しがループ的なものか、同じことを何度も繰り返すかが曖昧なので反復処理にする # typo - expected: $1進数 patterns: diff --git a/source/README.md b/source/README.md index 401f581202..3db9d9fd68 100644 --- a/source/README.md +++ b/source/README.md @@ -10,6 +10,7 @@ - [値の評価と表示](basic/read-eval-print/README.md) - [データ型とリテラル](basic/data-type/README.md) - [条件分岐](basic/condition/README.md) + - [ループと反復処理](basic/loop/README.md) - 式と演算子 - 文 - String diff --git a/source/basic/loop/README.md b/source/basic/loop/README.md new file mode 100644 index 0000000000..82908ab044 --- /dev/null +++ b/source/basic/loop/README.md @@ -0,0 +1,113 @@ +---- +author: azu +---- + +# ループと反復処理 + + +プログラミングにおいて、同じ処理を繰り返すために同じコードを書くことはありません。 +ループや再帰呼び出し、イテレータなどを使い反復処理は抽象化されます。 +ここでは、もっとも基本的な反復処理となるループについてを学んでいきます。 + +## for文 + +for文はもっとも基本となるループ処理です。 +JavaScriptのfor文はC言語やJavaと同様の構文になります。 + +```js +for (初期化式; 条件式; 増分式) + 実行する文; +``` + +for文の実行フローは次のようになります。 + +1. `初期化式` で変数の宣言 +2. `条件式` の評価結果が`true`なら処理を続け、`false`なら終了 +3. `実行する文` を実行 + - 複数行である場合は、`{`と`}`のブロック文にする必要があります +4. `増分式` で変数を更新 +5. ステップ2へ戻り繰り返す + +次のコードでは、for文を使い1から10の合計値を計算しています。 + +```js +var total = 0; // 初期値は0 +for (var i = 0; i < 10; i++) { + total += i + 1; // 1...10 +} +console.log(total); // => 55 +``` + +このコードは1から10の合計を電卓で計算すればいいので、普通は必要ありませんね。 +実際に扱うなら、数値の入った配列を受け取り、その合計を計算して返すという関数を実装することになります。 + +次のコードでは、任意の数値が入った配列を受け取り、その合計値を返す `sum` 関数を実装しています。 +関数とブロック文それぞれのスコープがあるので、`var`を`let`に書き換えると間違って同じ変数名を再定義できなくなるのでより安全です。 + + +[import, sum-for-example.js](./src/sum-for-example.js) + +この`let`を`const`に変更することはできません。 +なぜなら、for文は一度定義した変数に値の代入を繰り返し行う処理といえるからです。 +`const` は再代入できない変数を宣言するキーワードであるためfor文とは相性がよくありません。 + +一方、反復処理の多くは、配列に入れた値を処理する方法と言い換えることができます。 +そのため、JavaScriptの配列である`Array`オブジェクトには反復処理をするためのメソッドが備わっています。 + +`array.forEach(コールバック関数)`もそのひとつです。 +先ほどのfor文を`forEach`メソッドを使って書き換えてみます。 + +[import, sum-forEach-example.js](./src/sum-forEach-example.js) + +JavaScriptでは、関数はファーストクラスであるため、その場で作った匿名関数(名前のない関数)を引数として渡すことができます。 + +引数として渡される関数のことを**コールバック関数**と呼びます。 +また、`forEach`メソッドのようなコールバック関数を引数として受け取る関数やメソッドのことを**高階関数**と呼びます。 + +`array.forEach(コールバック関数)`のコールバック関数には、配列の先頭から順番に要素が渡されて実行されます。 +つまり、コールバック関数の`num`には1, 2, 3 …という値が順番に渡されて実行されます。 + +```js +[1, 2, 3, 4, 5].forEach(num => { + console.log(num); +}); +// 1 +// 2 +// 3 +// 4 +// 5 +// と順番に出力される +``` + +`forEach`は`条件式`がなく配列のすべてを走査するという、for文よりもシンプルな処理です。 +一方、`forEach`でも`let total`は`const`にはできませんでした。 +なぜなら`forEach`もfor文と同じく反復処理をして新しい値を作るものではないからです。 + +反復処理から新しい値を作るArrayメソッドとして`Array.prototype.reduce`があります。 +`array.reduce(コールバック関数, 初期値)`は配列から新しい値を作り返すメソッドです。 + +さきほどの例である、配列から合計値を返すものを`reduce`メソッドを使い実装してみましょう。 + +`reduce`メソッドは2つづつの要素を取り出し(左から右へ)、その値を`コールバック関数`を適用し、 +`次の値`として1つの値を返します。 +最終的な、`reduce`メソッドの返り値は、コールバック関数が最後に`return`した値となります。 + +```js +arrayObj.reduce((前回の値, 現在の値) => { + return 次の値; +}, 初期値); +``` + +先ほどの配列の全要素の合計値を計算するものは`reduce`メソッドでは、次のように書くことができます。 +`初期値`に`0`を指定し、`前回の値`と`現在の値`を足していくことで合計を計算できます。 +`初期値`を指定していた場合は、最初の`前回の値`に初期値が、配列の先頭の値が`現在の値`となった常体で開始されます。 + +[import, sum-reduce-example.js](./src/sum-reduce-example.js) + +`reduce`メソッドを使った例では、そもそも変数宣言をしていないことが分かります。 +`reduce`メソッドでは常に新しい値を返すことで、1つの変数の値を更新していく必要がなくなります。 +これは`const`と同じく、一度作った変数の値を変更しないため、意図しない変数の更新を避けることにつながります。 + +## [コラム] for文で実装されている + +- Arrayのメソッドをはfor文で実装されています diff --git a/source/basic/loop/src/sum-for-example.js b/source/basic/loop/src/sum-for-example.js new file mode 100644 index 0000000000..630e160be0 --- /dev/null +++ b/source/basic/loop/src/sum-for-example.js @@ -0,0 +1,9 @@ +function sum(numbers) { + let total = 0; + for (let i = 0; i < numbers.length; i++) { + total += numbers[i]; + } + return total; +} + +sum([1, 2, 3, 4, 5]); // => 15 diff --git a/source/basic/loop/src/sum-forEach-example.js b/source/basic/loop/src/sum-forEach-example.js new file mode 100644 index 0000000000..a62b9868d2 --- /dev/null +++ b/source/basic/loop/src/sum-forEach-example.js @@ -0,0 +1,9 @@ +function sum(numbers) { + let total = 0; + numbers.forEach(num => { + total += num; + }); + return total; +} + +sum([1, 2, 3, 4, 5]); // => 15 diff --git a/source/basic/loop/src/sum-reduce-example.js b/source/basic/loop/src/sum-reduce-example.js new file mode 100644 index 0000000000..575e0b9c33 --- /dev/null +++ b/source/basic/loop/src/sum-reduce-example.js @@ -0,0 +1,7 @@ +function sum(numbers) { + return numbers.reduce((total, num) => { + return total + num; + }, 0); // 初期値が0 +} + +sum([1, 2, 3, 4, 5]); // => 15 From 0d378eaca6e7cf6425952ba929374d5c838ec48b Mon Sep 17 00:00:00 2001 From: azu Date: Wed, 6 Jul 2016 20:16:51 +0900 Subject: [PATCH 2/8] =?UTF-8?q?chore(loop):=20Front=20Matter=E3=82=92?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/basic/loop/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/basic/loop/README.md b/source/basic/loop/README.md index 82908ab044..e5af55012d 100644 --- a/source/basic/loop/README.md +++ b/source/basic/loop/README.md @@ -1,6 +1,6 @@ ----- +--- author: azu ----- +--- # ループと反復処理 From c955e65f9eb33ba5b6adbe64487a9d73fb761984 Mon Sep 17 00:00:00 2001 From: azu Date: Wed, 6 Jul 2016 21:04:09 +0900 Subject: [PATCH 3/8] =?UTF-8?q?chore(varibales):=20=E4=B8=8A=E8=A8=98?= =?UTF-8?q?=E3=81=AE=E3=82=B3=E3=83=BC=E3=83=89=E3=82=92=E4=BF=AE=E6=AD=A3?= =?UTF-8?q?=20=E2=80=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/basic/variables/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/basic/variables/README.md b/source/basic/variables/README.md index 0fab18b4ee..69e322540a 100644 --- a/source/basic/variables/README.md +++ b/source/basic/variables/README.md @@ -136,14 +136,13 @@ const bookTitle = "JavaScriptの本"; ``` そして、一度`const`で宣言された変数には再代入できなくなります。 +そのため、次のコードでは`bookTitle`を上書きしようとして`TypeError`となります。 ```js const bookTitle = "JavaScriptの本"; bookTitle = "上書き"; // TypeError: invalid assignment to const `bookTitle' ``` -そのため、上記のコードでは`bookTitle`を上書きしようとして`TypeError`となります。 - 一般に変数への再代入は「変数の値は最初に定義した値と常に同じである」という参照透過性を壊すため、 バグを発生させやすい要因として知られています。 変数を再代入をしたいケースはループ中に値の変化させたい場合などがありますが、 From df05e3f36bbd951f2d5ebae2979730536c11403a Mon Sep 17 00:00:00 2001 From: azu Date: Fri, 8 Jul 2016 21:10:36 +0900 Subject: [PATCH 4/8] =?UTF-8?q?refactor(loop):=20reduce=E3=81=AB=E3=81=A4?= =?UTF-8?q?=E3=81=84=E3=81=A6=E3=82=92=E6=AC=84=E5=A4=96=E3=81=AB=E7=A7=BB?= =?UTF-8?q?=E5=8B=95=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/basic/loop/README.md | 45 ++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/source/basic/loop/README.md b/source/basic/loop/README.md index e5af55012d..62637a1a9e 100644 --- a/source/basic/loop/README.md +++ b/source/basic/loop/README.md @@ -47,17 +47,22 @@ console.log(total); // => 55 [import, sum-for-example.js](./src/sum-for-example.js) -この`let`を`const`に変更することはできません。 -なぜなら、for文は一度定義した変数に値の代入を繰り返し行う処理といえるからです。 -`const` は再代入できない変数を宣言するキーワードであるためfor文とは相性がよくありません。 - 一方、反復処理の多くは、配列に入れた値を処理する方法と言い換えることができます。 そのため、JavaScriptの配列である`Array`オブジェクトには反復処理をするためのメソッドが備わっています。 -`array.forEach(コールバック関数)`もそのひとつです。 -先ほどのfor文を`forEach`メソッドを使って書き換えてみます。 +## Array.prototype.forEach -[import, sum-forEach-example.js](./src/sum-forEach-example.js) +`Array`オブジェクトは、`map`、`reduce`などの反復処理のためのメソッドが用意されています。 +`array.forEach(コールバック関数)`もそのひとつでfor文に近い反復処理をします。 + +`forEach`メソッドは次のように書くことができます。 + +```js +const array = [1, 2, 3, 4, 5]; +array.forEach((currentValue, index, array) => { + // 処理する文 +}); +``` JavaScriptでは、関数はファーストクラスであるため、その場で作った匿名関数(名前のない関数)を引数として渡すことができます。 @@ -65,10 +70,10 @@ JavaScriptでは、関数はファーストクラスであるため、その場 また、`forEach`メソッドのようなコールバック関数を引数として受け取る関数やメソッドのことを**高階関数**と呼びます。 `array.forEach(コールバック関数)`のコールバック関数には、配列の先頭から順番に要素が渡されて実行されます。 -つまり、コールバック関数の`num`には1, 2, 3 …という値が順番に渡されて実行されます。 +つまり、コールバック関数の`currentValue`には1, 2, 3 …という値が順番に渡されて実行されます。 ```js -[1, 2, 3, 4, 5].forEach(num => { +[1, 2, 3, 4, 5].forEach(currentValue => { console.log(num); }); // 1 @@ -79,9 +84,21 @@ JavaScriptでは、関数はファーストクラスであるため、その場 // と順番に出力される ``` -`forEach`は`条件式`がなく配列のすべてを走査するという、for文よりもシンプルな処理です。 -一方、`forEach`でも`let total`は`const`にはできませんでした。 -なぜなら`forEach`もfor文と同じく反復処理をして新しい値を作るものではないからです。 +先ほどのfor文で合計値を計算する`sum`関数を`forEach`メソッドで書いてみます。 + +[import, sum-forEach-example.js](./src/sum-forEach-example.js) + +`forEach`は`条件式`がなく、配列のすべての要素を走査するため、for文よりもシンプルな処理です。 + + +### [コラム] `let`ではなく`const`で処理する + +先ほどのfor文や`forEach`メソッドでは`let`を`const`に変更することはできませでした。 +なぜなら、for文は一度定義した変数に値の代入を繰り返し行う処理といえるからです。 +`const` は再代入できない変数を宣言するキーワードであるためfor文とは相性がよくありません。 + +`let`ではなく`const`を使うためには、一度定義した変数に値を代入しつつ反復処理するのではなく、 +反復処理処理からひとつの新しい値を返す方法が必要になります。 反復処理から新しい値を作るArrayメソッドとして`Array.prototype.reduce`があります。 `array.reduce(コールバック関数, 初期値)`は配列から新しい値を作り返すメソッドです。 @@ -107,7 +124,3 @@ arrayObj.reduce((前回の値, 現在の値) => { `reduce`メソッドを使った例では、そもそも変数宣言をしていないことが分かります。 `reduce`メソッドでは常に新しい値を返すことで、1つの変数の値を更新していく必要がなくなります。 これは`const`と同じく、一度作った変数の値を変更しないため、意図しない変数の更新を避けることにつながります。 - -## [コラム] for文で実装されている - -- Arrayのメソッドをはfor文で実装されています From 330e6424143324cbd464322a949962567b3f78df Mon Sep 17 00:00:00 2001 From: azu Date: Fri, 8 Jul 2016 21:25:51 +0900 Subject: [PATCH 5/8] =?UTF-8?q?refactor(loop):=20forEach=E3=83=A1=E3=82=BD?= =?UTF-8?q?=E3=83=83=E3=83=89=E3=81=AB=E7=B5=B1=E4=B8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/basic/loop/README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/source/basic/loop/README.md b/source/basic/loop/README.md index 62637a1a9e..86834323b0 100644 --- a/source/basic/loop/README.md +++ b/source/basic/loop/README.md @@ -53,7 +53,7 @@ console.log(total); // => 55 ## Array.prototype.forEach `Array`オブジェクトは、`map`、`reduce`などの反復処理のためのメソッドが用意されています。 -`array.forEach(コールバック関数)`もそのひとつでfor文に近い反復処理をします。 +`forEach`メソッドもそのひとつでfor文に近い反復処理をします。 `forEach`メソッドは次のように書くことができます。 @@ -69,7 +69,12 @@ JavaScriptでは、関数はファーストクラスであるため、その場 引数として渡される関数のことを**コールバック関数**と呼びます。 また、`forEach`メソッドのようなコールバック関数を引数として受け取る関数やメソッドのことを**高階関数**と呼びます。 -`array.forEach(コールバック関数)`のコールバック関数には、配列の先頭から順番に要素が渡されて実行されます。 +```js +const array = [1, 2, 3, 4, 5]; +array.forEach(コールバック関数); +``` + +`forEach`メソッドのコールバック関数には、配列の先頭から順番に要素が渡されて実行されます。 つまり、コールバック関数の`currentValue`には1, 2, 3 …という値が順番に渡されて実行されます。 ```js @@ -90,7 +95,6 @@ JavaScriptでは、関数はファーストクラスであるため、その場 `forEach`は`条件式`がなく、配列のすべての要素を走査するため、for文よりもシンプルな処理です。 - ### [コラム] `let`ではなく`const`で処理する 先ほどのfor文や`forEach`メソッドでは`let`を`const`に変更することはできませでした。 From 3a947e4d0f24a1e4ebc552420824523d397c328a Mon Sep 17 00:00:00 2001 From: azu Date: Sat, 9 Jul 2016 13:54:42 +0900 Subject: [PATCH 6/8] =?UTF-8?q?chore(loop):=20=E3=81=A9=E3=81=93=E3=82=92?= =?UTF-8?q?=E7=A4=BA=E3=81=97=E3=81=A6=E3=81=84=E3=82=8B=E3=81=AE=E3=81=8B?= =?UTF-8?q?=E3=82=8F=E3=81=8B=E3=82=89=E3=81=AA=E3=81=84"=E4=B8=80?= =?UTF-8?q?=E6=96=B9"=E3=82=92=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/basic/loop/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/basic/loop/README.md b/source/basic/loop/README.md index 86834323b0..d28302f965 100644 --- a/source/basic/loop/README.md +++ b/source/basic/loop/README.md @@ -47,7 +47,7 @@ console.log(total); // => 55 [import, sum-for-example.js](./src/sum-for-example.js) -一方、反復処理の多くは、配列に入れた値を処理する方法と言い換えることができます。 +反復処理の多くは、配列に入れた値を処理する方法と言いかえることができます。 そのため、JavaScriptの配列である`Array`オブジェクトには反復処理をするためのメソッドが備わっています。 ## Array.prototype.forEach From 0cc27066e07a017a07fd54cd8230046eb48f9f8f Mon Sep 17 00:00:00 2001 From: azu Date: Sat, 9 Jul 2016 14:09:31 +0900 Subject: [PATCH 7/8] =?UTF-8?q?chore(loop):=20=E3=83=AB=E3=83=BC=E3=83=97?= =?UTF-8?q?=E5=87=A6=E7=90=86=20=E3=82=92=20=E5=8F=8D=E5=BE=A9=E5=87=A6?= =?UTF-8?q?=E7=90=86=E3=81=AB=E7=B5=B1=E4=B8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prh.yml | 1 + source/basic/loop/README.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/prh.yml b/prh.yml index b1dafce6b0..a638d8a0d8 100644 --- a/prh.yml +++ b/prh.yml @@ -90,6 +90,7 @@ rules: - expected: 反復処理 patterns: - 繰り返し処理 + - ループ処理 prh: 繰り返しがループ的なものか、同じことを何度も繰り返すかが曖昧なので反復処理にする # typo - expected: $1進数 diff --git a/source/basic/loop/README.md b/source/basic/loop/README.md index d28302f965..079f8ba625 100644 --- a/source/basic/loop/README.md +++ b/source/basic/loop/README.md @@ -11,7 +11,7 @@ author: azu ## for文 -for文はもっとも基本となるループ処理です。 +for文はもっとも基本的な反復処理です。 JavaScriptのfor文はC言語やJavaと同様の構文になります。 ```js From 33a78b668c75c914414fa2e72a8d0425cee8f8c5 Mon Sep 17 00:00:00 2001 From: azu Date: Sat, 9 Jul 2016 14:10:12 +0900 Subject: [PATCH 8/8] =?UTF-8?q?chore(loop):=20=20=E3=83=96=E3=83=AD?= =?UTF-8?q?=E3=83=83=E3=82=AF=3D=20=E5=9B=B2=E3=82=93=E3=81=A0=E6=96=87?= =?UTF-8?q?=E3=81=AB=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/basic/loop/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/basic/loop/README.md b/source/basic/loop/README.md index 079f8ba625..0a1771d420 100644 --- a/source/basic/loop/README.md +++ b/source/basic/loop/README.md @@ -24,7 +24,7 @@ for文の実行フローは次のようになります。 1. `初期化式` で変数の宣言 2. `条件式` の評価結果が`true`なら処理を続け、`false`なら終了 3. `実行する文` を実行 - - 複数行である場合は、`{`と`}`のブロック文にする必要があります + - 複数行である場合は、`{`と`}`で囲んだブロック文にする必要があります 4. `増分式` で変数を更新 5. ステップ2へ戻り繰り返す