回答編集履歴
1
ロジックと回答文を大リファクタリング
answer
CHANGED
@@ -1,6 +1,39 @@
|
|
1
|
-
まず、
|
1
|
+
まず、今回の質問文の改良点的な所が幾つか見受けられます。
|
2
|
-
3という文字が含まれる時というのは後で対応していくので、まずは3の倍数に集中してくださいね。
|
3
2
|
|
3
|
+
- document.writeは非推奨なのでやめよう
|
4
|
+
- 数字がアホになるか否かの判定ロジックは関数として切り分けよう
|
5
|
+
- 数字を入れるとアホ、もしくは数字を返すロジックも関数として切り分けよう
|
6
|
+
- タイマー機能を改良しよう
|
7
|
+
|
8
|
+
---
|
9
|
+
|
10
|
+
> document.writeは非推奨なのでやめよう
|
11
|
+
|
12
|
+
document.writeはJavaScript(以下JS)黎明期の書き方ですが、
|
13
|
+
これは様々な箇所で不具合が出るダメな子です。
|
14
|
+
結果、どのJSの書籍でも「document.write」は使うな!と明記される程嫌われている機能なので忘れてください。
|
15
|
+
|
16
|
+
DOMツリー構造を動的に変更するやり方を覚えましょう。
|
17
|
+
jQueryというライブラリを扱うのが初心者にはオススメです。
|
18
|
+
[jQueryのDOMを挿入](https://qiita.com/nekoneko-wanwan/items/227ccad5f8cc449e91e9)を覚えてください。
|
19
|
+
|
20
|
+
この反映は質問者さんへの課題として残しておきます。
|
21
|
+
|
22
|
+
従って、今回の回答文では`console.log`とデベロッパーツールによる簡易的な表示方法を利用していきます。
|
23
|
+
F12キーを押すと、デベロッパーツールが立ち上がります。
|
24
|
+
`console.log(123)`という風に実行すると、デベロッパーツールのconsoleタブに結果を出力してくれます。
|
25
|
+
|
26
|
+
---
|
27
|
+
|
28
|
+
> 数字がアホになるか否かの判定ロジックは関数として切り分けよう
|
29
|
+
|
30
|
+
3の倍数でアホになる判定ロジックは関数として外部に切り出してください。
|
31
|
+
関数にしてしまえば、結果の確認がとても楽になりますよ。
|
32
|
+
|
33
|
+
`isナベアツ`です!
|
34
|
+
numという数値の引数を要求しており、アホになりそうだったら`true`
|
35
|
+
普通の回答で数字を教えてくれそうなら`false`になります。
|
36
|
+
|
4
37
|
```JavaScript
|
5
38
|
var isNabeatsu = function (num) {
|
6
39
|
return num % 3 === 0;
|
@@ -8,209 +41,196 @@
|
|
8
41
|
console.log(isNabeatsu(1)); // false
|
9
42
|
console.log(isNabeatsu(2)); // false
|
10
43
|
console.log(isNabeatsu(3)); // true
|
44
|
+
console.log(isNabeatsu(4)); // false
|
45
|
+
console.log(isNabeatsu(5)); // false
|
46
|
+
console.log(isNabeatsu(6)); // true
|
11
47
|
```
|
12
48
|
|
13
|
-
|
49
|
+
よしよし、うまく動作しているようですね。
|
14
|
-
|
50
|
+
ですが、これはまだ3の倍数だけで判定するので、肝心の13や23等で反応しなさそうです。
|
15
51
|
|
16
|
-
とりあえずはデベロッパーツールのコンソールに表示するやり方で作っていきます。
|
17
|
-
コードを下記のように変えて、F12キーでデベロッパーツールを起動してみてください。
|
18
|
-
(タイマー機能は一旦潰しています。これも後で復活させますからね。)
|
19
|
-
|
20
52
|
```JavaScript
|
21
53
|
var isNabeatsu = function (num) {
|
22
54
|
return num % 3 === 0;
|
23
55
|
}
|
24
|
-
var start = function () {
|
25
|
-
var suuji = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
26
|
-
for (var it of suuji) {
|
27
|
-
|
56
|
+
console.log(isNabeatsu(13)); // false
|
28
|
-
|
57
|
+
console.log(isNabeatsu(23)); // false
|
29
|
-
}
|
30
|
-
}
|
31
|
-
start();
|
32
|
-
// 1
|
33
|
-
// 2
|
34
|
-
//
|
58
|
+
console.log(isNabeatsu(31)); // false
|
35
|
-
// 4
|
36
|
-
// 5
|
37
|
-
// AHO!
|
38
|
-
// 7
|
39
|
-
// 8
|
40
|
-
// AHO!
|
41
|
-
// 10
|
42
59
|
```
|
43
60
|
|
61
|
+
あらら、全滅です…
|
44
|
-
|
62
|
+
では、これを改良して完成させましょうか。
|
45
|
-
|
63
|
+
「数字の3が含まれている場合はfalseではなくtrueになる」が条件です。
|
46
64
|
|
47
|
-
|
65
|
+
この数字の3を探す…といった風な用途は「文字列検索」といいます。
|
66
|
+
JSには[数値型や文字列型、配列、オブジェクト…など、様々な型](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects)が存在します。
|
67
|
+
それらの型には値を加工するのに使う便利な機能(メソッド)が用意されています。
|
48
68
|
|
49
|
-
|
69
|
+
先程3の倍数か否かを確認する為に、`num % 3`と入力しましたね。
|
70
|
+
数値型は数値計算に利用される型であり、他の型では数値計算は不可能です。
|
50
|
-
で
|
71
|
+
でも数字の3を探したい……こういう要望は文字列型が得意とする仕事なので数値型から文字列型へ変換(キャスト)する必要があります。
|
51
72
|
|
52
|
-
|
73
|
+
プログラマのテクニックの一つとして、数値として判断出来る値は基本的には数値型として所持しておき、
|
53
|
-
|
74
|
+
文字列検索がしたくなったらその時だけ、文字列型にキャストする使い方が分かりやすくオススメです。
|
54
|
-
デベロッパーツールのコンソールで下記のコマンドを入力して動作を確かめてみましょう。
|
55
75
|
|
76
|
+
今回は[String.prototype.match](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/match)を利用することにしました。
|
77
|
+
正規表現を使って文字列を探す少々高難度の手法ですが、3という文字列を探すだけの正規表現は`/3/`とわりと簡単なので採用しました。
|
78
|
+
(他にも様々な方法で解決することが可能で、10人に頼めば10人共がちょっとずつ違う判定ロジックを持ってくると思います。探してみてね!!)
|
79
|
+
|
56
80
|
```JavaScript
|
57
|
-
// この書き方はエラー
|
58
|
-
//
|
81
|
+
// 文字列検索を行うmatchメソッドは文字列型限定のメソッド
|
82
|
+
var num = 123;
|
59
|
-
|
83
|
+
num.match(/3/);
|
60
|
-
// Uncaught
|
84
|
+
// Uncaught TypeError: num.match is not a function
|
61
85
|
|
62
|
-
// カッコで包めば大丈夫…でもこの書き方もエラー
|
63
|
-
// 数値型にはsplitというメソッドは存在しない
|
64
|
-
(30).split("3")
|
65
|
-
//
|
86
|
+
// JSでは.toString()を使えば全ての値が文字列型にキャストされる
|
87
|
+
num.toString().match(/3/);
|
88
|
+
// ["3", index: 2, input: "123", groups: undefined]
|
66
89
|
|
90
|
+
// 値をカッコで包んだりしてそのまま.toString()で文字列型にして使うことも可能
|
91
|
+
(17).toString().match(/3/);
|
67
|
-
//
|
92
|
+
// null
|
68
|
-
"15".split("3")
|
69
|
-
// ["15"]
|
70
93
|
|
71
|
-
//
|
94
|
+
// 論理型にキャストするにはBooleanを使う
|
72
|
-
"130".split("3")
|
73
|
-
// ["1", "0"]
|
74
|
-
|
75
|
-
// 数値が文字列になってくれればなぁ…
|
76
|
-
// JSではどんな型にも.toString()が用意されてて、文字列に変換できる!
|
77
|
-
(
|
95
|
+
Boolean((31).toString().match(/3/));
|
96
|
+
// true
|
78
|
-
//
|
97
|
+
Boolean((16).toString().match(/3/));
|
98
|
+
// false
|
79
99
|
```
|
80
100
|
|
101
|
+
これを元に`isナベアツ`関数を完成させましょう。
|
102
|
+
「3で割り切れる数字」もしくは「数字の3がある」場合ですので、
|
81
|
-
|
103
|
+
論理和であるor演算子`||`を利用します。
|
82
|
-
`.split`を通った文字列は、`3`という文字列があろうがなかろうが必ず配列に変化します。
|
83
|
-
そして、配列には`length`という要素数を調べるプロパティが存在します。
|
84
104
|
|
85
105
|
```JavaScript
|
106
|
+
var isNabeatsu = function (num) {
|
107
|
+
return (num % 3 === 0) || Boolean(num.toString().match(/3/));
|
108
|
+
}
|
109
|
+
console.log(isNabeatsu(1)); // false
|
110
|
+
console.log(isNabeatsu(2)); // false
|
86
|
-
|
111
|
+
console.log(isNabeatsu(3)); // true
|
87
|
-
// 1
|
88
|
-
|
89
|
-
|
112
|
+
console.log(isNabeatsu(13)); // true
|
90
|
-
//
|
113
|
+
console.log(isNabeatsu(23)); // true
|
114
|
+
console.log(isNabeatsu(31)); // true
|
115
|
+
console.log(isNabeatsu(25)); // false
|
91
116
|
```
|
92
117
|
|
93
|
-
そうなんです、130のように途中に3がある文字列はそこでぶった切られる為に配列の要素数が1を超えるんですね。
|
94
|
-
|
118
|
+
いい感じに動作してますね。
|
119
|
+
isナベアツ関数完成です。
|
95
120
|
|
96
|
-
|
121
|
+
---
|
97
|
-
(13).toString().split("3")
|
98
|
-
// ["1", ""]
|
99
|
-
(13).toString().split("3").length
|
100
|
-
// 2
|
101
122
|
|
102
|
-
(31).toString().split("3")
|
103
|
-
|
123
|
+
> 数字を入れるとアホ、もしくは数字を返すロジックも関数として切り分けよう
|
104
|
-
(31).toString().split("3").length
|
105
|
-
// 2
|
106
|
-
```
|
107
124
|
|
108
|
-
|
125
|
+
今度は`toナベアツ`関数を作成しましょうか。
|
126
|
+
これも切り分ける事でテストが非常にしやすくなります。
|
109
127
|
|
128
|
+
もうアホになるべき数字か否かを判定する関数は出来ていますね?
|
129
|
+
今回は「特定条件でAHO!」か「そうでなければ数字をそのまま使う」ので、三項演算子を使ってみましょうか。
|
130
|
+
ついでに、`AHO!`という文字列が返ってくる可能性がある以上、もう片方の数字も文字列型にキャストして同じ文字列型に合わせておいたほうが使いやすそうですね。
|
131
|
+
|
110
132
|
```JavaScript
|
111
133
|
var isNabeatsu = function (num) {
|
112
|
-
if (num % 3 === 0) {
|
113
|
-
return true;
|
114
|
-
|
134
|
+
return (num % 3 === 0) || Boolean(num.toString().match(/3/));
|
115
|
-
return true;
|
116
|
-
} else {
|
117
|
-
return false;
|
118
|
-
}
|
119
135
|
}
|
136
|
+
var toNabeatsu = function (num) {
|
120
|
-
|
137
|
+
return isNabeatsu(num) ? 'AHO!' : num.toString();
|
138
|
+
}
|
121
|
-
console.log(
|
139
|
+
console.log(toNabeatsu(1)); // 1
|
140
|
+
console.log(toNabeatsu(2)); // 2
|
141
|
+
console.log(toNabeatsu(3)); // AHO!
|
122
|
-
console.log(
|
142
|
+
console.log(toNabeatsu(13)); // AHO!
|
143
|
+
console.log(toNabeatsu(23)); // AHO!
|
144
|
+
console.log(toNabeatsu(31)); // AHO!
|
123
145
|
```
|
124
146
|
|
147
|
+
はい完了。
|
148
|
+
このように同じ数字を入れると、必ず同じ値が返ってくるような処理は関数化しておくと、
|
125
|
-
う
|
149
|
+
このように動作確認が楽ですね。
|
126
150
|
|
151
|
+
---
|
152
|
+
|
153
|
+
早速これを質問文に組み込みましょう。
|
154
|
+
suuji変数に入っている配列を全て消化したら完了というロジックに変更しています。
|
155
|
+
|
127
156
|
```JavaScript
|
128
157
|
var isNabeatsu = function (num) {
|
129
|
-
if (num % 3 === 0) {
|
130
|
-
return true;
|
131
|
-
|
158
|
+
return (num % 3 === 0) || Boolean(num.toString().match(/3/));
|
132
|
-
return true;
|
133
|
-
} else {
|
134
|
-
return false;
|
135
|
-
}
|
136
159
|
}
|
137
|
-
var
|
160
|
+
var toNabeatsu = function (num) {
|
161
|
+
return isNabeatsu(num) ? 'AHO!' : num.toString();
|
162
|
+
}
|
138
|
-
|
163
|
+
var suuji = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
164
|
+
var aaa = 0;
|
165
|
+
var bbb = setInterval(start, 1000);
|
166
|
+
|
167
|
+
function start() {
|
168
|
+
// 表示したい文字列は今回はconsole.logを使って表示
|
169
|
+
console.log(toNabeatsu(suuji[aaa]));
|
170
|
+
aaa++;
|
139
|
-
|
171
|
+
if (aaa >= suuji.length) {
|
140
|
-
|
172
|
+
clearInterval(bbb); // タイマー停止
|
141
|
-
console.log(output);
|
142
173
|
}
|
143
174
|
}
|
144
|
-
|
175
|
+
// 1
|
176
|
+
// 2
|
177
|
+
// AHO!
|
178
|
+
// 4
|
179
|
+
// 5
|
180
|
+
// AHO!
|
181
|
+
// 7
|
182
|
+
// 8
|
183
|
+
// AHO!
|
184
|
+
// 10
|
145
185
|
```
|
146
186
|
|
187
|
+
1秒おきにconsole.logが実行されて、表示されていく様が分かります。
|
188
|
+
10秒後に数字の値が全て表示されて、これで完了ですね。
|
189
|
+
|
147
190
|
---
|
148
191
|
|
149
|
-
|
192
|
+
【おまけ】
|
150
|
-
そのまえに`suuji`の手打ちが面倒なのでさくっと数値の配列作ってくれる関数でも用意しましょうかね。
|
151
|
-
これはイディオムというおまじない的なものなので、興味が出たら調べてみてください。
|
152
193
|
|
153
|
-
```JavaScript
|
154
|
-
var range = function (start, end) {
|
155
|
-
return Array(end - start + 1).fill(0).map(function (it, i) {
|
156
|
-
|
194
|
+
> タイマー機能を改良しよう
|
157
|
-
})
|
158
|
-
}
|
159
|
-
console.log(range(1, 3)); // [1, 2, 3]
|
160
|
-
console.log(range(5, 8)); // [5, 6, 7, 8]
|
161
|
-
```
|
162
195
|
|
196
|
+
見事動くようになりましたが、
|
197
|
+
関数はともかく、この`suuji`、`aaa`、`bbb`がキモいんですよねぇ……
|
163
|
-
|
198
|
+
何がキモいって、start関数の中から決め打ちで読みにいってるあたりです。
|
164
|
-
まずは完成形をドン!
|
165
199
|
|
200
|
+
こういう変数は「状態変数」といって、
|
201
|
+
出来るだけ減らした方がかっこいいのです。
|
202
|
+
|
203
|
+
そのためのテクニックはいくつかありますが、今回は2つ加えます。
|
204
|
+
|
205
|
+
- ループの仕組みをsetTimeoutを使った再起ループ化
|
206
|
+
- start関数は引数の配列を監視して続けるかやめるか選ぶように
|
207
|
+
|
166
208
|
```JavaScript
|
167
209
|
var isNabeatsu = function (num) {
|
168
|
-
if (num % 3 === 0) {
|
169
|
-
return true;
|
170
|
-
|
210
|
+
return (num % 3 === 0) || Boolean(num.toString().match(/3/));
|
171
|
-
return true;
|
172
|
-
} else {
|
173
|
-
return false;
|
174
|
-
}
|
175
211
|
}
|
176
|
-
var
|
212
|
+
var toNabeatsu = function (num) {
|
177
|
-
return
|
213
|
+
return isNabeatsu(num) ? 'AHO!' : num.toString();
|
178
|
-
return i + start;
|
179
|
-
})
|
180
214
|
}
|
181
215
|
var start = function (numbers) {
|
182
|
-
if (numbers
|
216
|
+
if (numbers[0] == null) return;
|
183
|
-
var it = numbers[0];
|
184
|
-
var output = isNabeatsu(it) ? 'AHO!' : ''+it;
|
185
|
-
console.log(
|
217
|
+
console.log(toNabeatsu(numbers[0]));
|
186
218
|
setTimeout(start.bind(null, numbers.slice(1)), 1000);
|
187
219
|
}
|
188
|
-
start(
|
220
|
+
start([1, 2, 3, 4, 5, 6, 7, 8, 9, 10,]);
|
189
221
|
// 1
|
190
222
|
// 2
|
191
|
-
// AHO
|
223
|
+
// AHO!
|
192
224
|
// 4
|
193
225
|
// 5
|
194
|
-
// AHO
|
226
|
+
// AHO!
|
195
227
|
// 7
|
196
228
|
// 8
|
197
|
-
// AHO
|
229
|
+
// AHO!
|
198
230
|
// 10
|
199
|
-
// 11
|
200
|
-
// AHO!
|
201
|
-
// AHO!
|
202
231
|
```
|
203
232
|
|
204
|
-
ちゃんと1秒おきに文字がコンソールに表示されましたね。
|
205
|
-
setIntervalは1秒毎に時を刻んでくれるので便利ですが、
|
206
|
-
変数のハンドリングが超大変です。
|
207
|
-
|
208
|
-
今回はstart関数を一度実行したら、
|
209
|
-
2度目以降は毎回setTimeoutを使う仕様にしました。
|
210
|
-
|
211
|
-
いやいや、start関数はnumbersという数字の配列寄越せって言ってるんだから、
|
212
|
-
setTimeoutやsetIntervalで使えないでしょ!どうすんの!?
|
213
|
-
|
233
|
+
全ての関数には[function.prototype.bind](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Function/bind)というメソッドが用意されています。
|
214
234
|
まぁ、要するに関数に値を適用しつつ、実行しないで待ってくれるという必殺技があるんです。
|
215
235
|
|
216
236
|
```JavaScript
|
@@ -223,12 +243,15 @@
|
|
223
243
|
// 15
|
224
244
|
```
|
225
245
|
|
226
|
-
|
246
|
+
更に配列には[array.prototype.slice](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/slice)という配列の一部を切り出すメソッドがあります。
|
227
|
-
|
247
|
+
これを利用して金太郎アメのようにnumbers変数の関数を頭から1個ずつ削り取って利用する作りにしています。
|
228
248
|
|
229
249
|
```JavaScript
|
230
250
|
[1, 2, 3].slice(1)
|
231
251
|
// [2, 3]
|
252
|
+
|
253
|
+
[1, 2, 3].slice(1).slice(1)
|
254
|
+
// [3]
|
232
255
|
```
|
233
256
|
|
234
257
|
全体的な流れとしてはこうです。
|