回答編集履歴

1

ロジックと回答文を大リファクタリング

2018/03/19 16:38

投稿

miyabi-sun
miyabi-sun

スコア21145

test CHANGED
@@ -1,6 +1,72 @@
1
+ まず、今回の質問文の改良点的な所が幾つか見受けられます。
2
+
3
+
4
+
5
+ - document.writeは非推奨なのでやめよう
6
+
7
+ - 数字がアホになるか否かの判定ロジックは関数として切り分けよう
8
+
9
+ - 数字を入れるとアホ、もしくは数字を返すロジックも関数として切り分けよう
10
+
11
+ - タイマー機能を改良しよう
12
+
13
+
14
+
15
+ ---
16
+
17
+
18
+
19
+ > document.writeは非推奨なのでやめよう
20
+
21
+
22
+
23
+ document.writeはJavaScript(以下JS)黎明期の書き方ですが、
24
+
25
+ これは様々な箇所で不具合が出るダメな子です。
26
+
27
+ 結果、どのJSの書籍でも「document.write」は使うな!と明記される程嫌われている機能なので忘れてください。
28
+
29
+
30
+
31
+ DOMツリー構造を動的に変更するやり方を覚えましょう。
32
+
33
+ jQueryというライブラリを扱うのが初心者にはオススメです。
34
+
35
+ [jQueryのDOMを挿入](https://qiita.com/nekoneko-wanwan/items/227ccad5f8cc449e91e9)を覚えてください。
36
+
37
+
38
+
39
+ この反映は質問者さんへの課題として残しておきます。
40
+
41
+
42
+
43
+ 従って、今回の回答文では`console.log`とデベロッパーツールによる簡易的な表示方法を利用していきます。
44
+
45
+ F12キーを押すと、デベロッパーツールが立ち上がります。
46
+
47
+ `console.log(123)`という風に実行すると、デベロッパーツールのconsoleタブに結果を出力してくれます。
48
+
49
+
50
+
51
+ ---
52
+
53
+
54
+
55
+ > 数字がアホになるか否かの判定ロジックは関数として切り分けよう
56
+
57
+
58
+
1
- まず、3の倍数でアホになるは関数ょう
59
+ 3の倍数でアホになる判定ロジックは関数て外部に切り出てください
60
+
2
-
61
+ 関数にしてしまえば、結果の確認がとても楽になりますよ。
62
+
63
+
64
+
65
+ `isナベアツ`です!
66
+
3
- 3という文字が含まれる時というは後で対応していくのでまずは3の倍数集中してくさいね。
67
+ numという数値引数を要求しておりアホなりそうったら`true`
68
+
69
+ 普通の回答で数字を教えてくれそうなら`false`になります。
4
70
 
5
71
 
6
72
 
@@ -18,21 +84,227 @@
18
84
 
19
85
  console.log(isNabeatsu(3)); // true
20
86
 
87
+ console.log(isNabeatsu(4)); // false
88
+
89
+ console.log(isNabeatsu(5)); // false
90
+
91
+ console.log(isNabeatsu(6)); // true
92
+
21
- ```
93
+ ```
94
+
95
+
96
+
97
+ よしよし、うまく動作しているようですね。
98
+
99
+ ですが、これはまだ3の倍数だけで判定するので、肝心の13や23等で反応しなさそうです。
100
+
101
+
102
+
103
+ ```JavaScript
104
+
105
+ var isNabeatsu = function (num) {
106
+
107
+ return num % 3 === 0;
108
+
109
+ }
110
+
111
+ console.log(isNabeatsu(13)); // false
112
+
113
+ console.log(isNabeatsu(23)); // false
114
+
115
+ console.log(isNabeatsu(31)); // false
116
+
117
+ ```
118
+
119
+
120
+
121
+ あらら、全滅です…
122
+
123
+ では、これを改良して完成させましょうか。
124
+
125
+ 「数字の3が含まれている場合はfalseではなくtrueになる」が条件です。
126
+
127
+
128
+
129
+ この数字の3を探す…といった風な用途は「文字列検索」といいます。
130
+
131
+ JSには[数値型や文字列型、配列、オブジェクト…など、様々な型](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects)が存在します。
132
+
133
+ それらの型には値を加工するのに使う便利な機能(メソッド)が用意されています。
134
+
135
+
136
+
137
+ 先程3の倍数か否かを確認する為に、`num % 3`と入力しましたね。
138
+
139
+ 数値型は数値計算に利用される型であり、他の型では数値計算は不可能です。
140
+
141
+ でも数字の3を探したい……こういう要望は文字列型が得意とする仕事なので数値型から文字列型へ変換(キャスト)する必要があります。
142
+
143
+
144
+
145
+ プログラマのテクニックの一つとして、数値として判断出来る値は基本的には数値型として所持しておき、
146
+
147
+ 文字列検索がしたくなったらその時だけ、文字列型にキャストする使い方が分かりやすくオススメです。
148
+
149
+
150
+
151
+ 今回は[String.prototype.match](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/match)を利用することにしました。
152
+
153
+ 正規表現を使って文字列を探す少々高難度の手法ですが、3という文字列を探すだけの正規表現は`/3/`とわりと簡単なので採用しました。
154
+
155
+ (他にも様々な方法で解決することが可能で、10人に頼めば10人共がちょっとずつ違う判定ロジックを持ってくると思います。探してみてね!!)
156
+
157
+
158
+
159
+ ```JavaScript
160
+
161
+ // 文字列検索を行うmatchメソッドは文字列型限定のメソッド
162
+
163
+ var num = 123;
164
+
165
+ num.match(/3/);
166
+
167
+ // Uncaught TypeError: num.match is not a function
168
+
169
+
170
+
171
+ // JSでは.toString()を使えば全ての値が文字列型にキャストされる
172
+
173
+ num.toString().match(/3/);
174
+
175
+ // ["3", index: 2, input: "123", groups: undefined]
176
+
177
+
178
+
179
+ // 値をカッコで包んだりしてそのまま.toString()で文字列型にして使うことも可能
180
+
181
+ (17).toString().match(/3/);
182
+
183
+ // null
184
+
185
+
186
+
187
+ // 論理型にキャストするにはBooleanを使う
188
+
189
+ Boolean((31).toString().match(/3/));
190
+
191
+ // true
192
+
193
+ Boolean((16).toString().match(/3/));
194
+
195
+ // false
196
+
197
+ ```
198
+
199
+
200
+
201
+ これを元に`isナベアツ`関数を完成させましょう。
202
+
203
+ 「3で割り切れる数字」もしくは「数字の3がある」場合ですので、
204
+
205
+ 論理和であるor演算子`||`を利用します。
206
+
207
+
208
+
209
+ ```JavaScript
210
+
211
+ var isNabeatsu = function (num) {
212
+
213
+ return (num % 3 === 0) || Boolean(num.toString().match(/3/));
214
+
215
+ }
216
+
217
+ console.log(isNabeatsu(1)); // false
218
+
219
+ console.log(isNabeatsu(2)); // false
220
+
221
+ console.log(isNabeatsu(3)); // true
222
+
223
+ console.log(isNabeatsu(13)); // true
224
+
225
+ console.log(isNabeatsu(23)); // true
226
+
227
+ console.log(isNabeatsu(31)); // true
228
+
229
+ console.log(isNabeatsu(25)); // false
230
+
231
+ ```
232
+
233
+
234
+
235
+ いい感じに動作してますね。
236
+
237
+ isナベアツ関数完成です。
238
+
239
+
240
+
241
+ ---
242
+
243
+
244
+
245
+ > 数字を入れるとアホ、もしくは数字を返すロジックも関数として切り分けよう
246
+
247
+
248
+
249
+ 今度は`toナベアツ`関数を作成しましょうか。
250
+
251
+ これも切り分ける事でテストが非常にしやすくなります。
252
+
253
+
254
+
255
+ もうアホになるべき数字か否かを判定する関数は出来ていますね?
256
+
257
+ 今回は「特定条件でAHO!」か「そうでなければ数字をそのまま使う」ので、三項演算子を使ってみましょうか。
258
+
259
+ ついでに、`AHO!`という文字列が返ってくる可能性がある以上、もう片方の数字も文字列型にキャストして同じ文字列型に合わせておいたほうが使いやすそうですね。
260
+
261
+
262
+
263
+ ```JavaScript
264
+
265
+ var isNabeatsu = function (num) {
266
+
267
+ return (num % 3 === 0) || Boolean(num.toString().match(/3/));
268
+
269
+ }
270
+
271
+ var toNabeatsu = function (num) {
272
+
273
+ return isNabeatsu(num) ? 'AHO!' : num.toString();
274
+
275
+ }
276
+
277
+ console.log(toNabeatsu(1)); // 1
278
+
279
+ console.log(toNabeatsu(2)); // 2
280
+
281
+ console.log(toNabeatsu(3)); // AHO!
282
+
283
+ console.log(toNabeatsu(13)); // AHO!
284
+
285
+ console.log(toNabeatsu(23)); // AHO!
286
+
287
+ console.log(toNabeatsu(31)); // AHO!
288
+
289
+ ```
290
+
291
+
292
+
293
+ はい完了。
294
+
295
+ このように同じ数字を入れると、必ず同じ値が返ってくるような処理は関数化しておくと、
296
+
297
+ このように動作確認が楽ですね。
298
+
299
+
300
+
301
+ ---
22
302
 
23
303
 
24
304
 
25
305
  早速これを質問文に組み込みましょう。
26
306
 
27
- …と、document.writeは推奨されてない書き方なので、[jQueryのDOMを挿入](https://qiita.com/nekoneko-wanwan/items/227ccad5f8cc449e91e9)を覚えてください。
28
-
29
-
30
-
31
- りあえずはデベロッパーツールのコンソール表示するやり方で作っています。
307
+ suuji変数に入っている配列を全て消化したら完了いう変更しています。
32
-
33
- コードを下記のように変えて、F12キーでデベロッパーツールを起動してみてください。
34
-
35
- (タイマー機能は一旦潰しています。これも後で復活させますからね。)
36
308
 
37
309
 
38
310
 
@@ -40,43 +312,57 @@
40
312
 
41
313
  var isNabeatsu = function (num) {
42
314
 
43
- return num % 3 === 0;
315
+ return (num % 3 === 0) || Boolean(num.toString().match(/3/));
44
-
316
+
45
- }
317
+ }
46
-
318
+
47
- var start = function () {
319
+ var toNabeatsu = function (num) {
320
+
48
-
321
+ return isNabeatsu(num) ? 'AHO!' : num.toString();
322
+
323
+ }
324
+
49
- var suuji = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
325
+ var suuji = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
326
+
50
-
327
+ var aaa = 0;
328
+
329
+ var bbb = setInterval(start, 1000);
330
+
331
+
332
+
333
+ function start() {
334
+
335
+ // 表示したい文字列は今回はconsole.logを使って表示
336
+
337
+ console.log(toNabeatsu(suuji[aaa]));
338
+
339
+ aaa++;
340
+
51
- for (var it of suuji) {
341
+ if (aaa >= suuji.length) {
52
-
342
+
53
- var output = isNabeatsu(it) ? 'AHO!' : ''+it;
343
+ clearInterval(bbb); // タイマー停止
54
-
55
- console.log(output);
56
344
 
57
345
  }
58
346
 
59
347
  }
60
348
 
61
- start();
62
-
63
349
  // 1
64
350
 
65
351
  // 2
66
352
 
67
- // AHO
353
+ // AHO!
68
354
 
69
355
  // 4
70
356
 
71
357
  // 5
72
358
 
73
- // AHO
359
+ // AHO!
74
360
 
75
361
  // 7
76
362
 
77
363
  // 8
78
364
 
79
- // AHO
365
+ // AHO!
80
366
 
81
367
  // 10
82
368
 
@@ -84,9 +370,9 @@
84
370
 
85
371
 
86
372
 
87
- うん、上手く動いているようですね。
88
-
89
- では、次の章で3という文字含また時にアホになる対応も入れています。
373
+ 1秒おきにconsole.log実行さて、表示されていく様が分かります。
374
+
375
+ 10秒後に数字の値が全て表示されて、これで完了ですね。
90
376
 
91
377
 
92
378
 
@@ -94,335 +380,89 @@
94
380
 
95
381
 
96
382
 
97
- 文字を検索するには正規表現が便利です!
98
-
99
- ですが、今回は[String.prototype.split](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/split)という機能を使ってやってみましょう。
100
-
101
-
102
-
103
- まず、JavaScriptには型によって使えるメソッドが決まっています。
104
-
105
- 例えば文字列ならば`.split`といメソッドが利用可能になっていす。
106
-
107
- デベロッパーツールコンソール下記のコマンドを入力して動作を確かめてみましょう。
108
-
109
-
110
-
111
- ```JavaScript
112
-
113
- // の書き方エラー
114
-
115
- // 数字の直後に.があと、小数点とて認識しようとするアルファベットなので構文エラーとなる
116
-
117
- 30.split("3")
118
-
119
- // Uncaught SyntaxError: Invalid or unexpected token
120
-
121
-
122
-
123
- // カッコで包めば大丈夫…でもこ書き方もエラ
124
-
125
- // 数値型にはsplitというメソッド存在ない
126
-
127
- (30).split("3")
128
-
129
- // Uncaught TypeError: 30.split is not a function
130
-
131
-
132
-
133
- // 文字列ならOK
134
-
135
- "15".split("3")
136
-
137
- // ["15"]
138
-
139
-
140
-
141
- // 3の付く文字の場合、配列が分断される事を確認
142
-
143
- "130".split("3")
144
-
145
- // ["1", "0"]
146
-
147
-
148
-
149
- // 数値が文字列になってくれればなぁ…
150
-
151
- // JSではどんな型にも.toString()が用意されてて、文字列に変換できる!
152
-
153
- (130).toString().split("3")
154
-
155
- // ["1", "0"]
156
-
157
- ```
158
-
159
-
160
-
161
- 実現できそうな気がしてきましたね。
162
-
163
- `.split`を通った文字列は、`3`という文字列があろうがなかろうが必ず配列に変化します。
164
-
165
- そして、配列には`length`という要素数を調べるプロパティが存在します。
166
-
167
-
168
-
169
- ```JavaScript
170
-
171
- (15).toString().split("3").length
383
+ 【おまけ】
384
+
385
+
386
+
387
+ > タイマー機能を改良しよう
388
+
389
+
390
+
391
+ 見事動くようになしたが、
392
+
393
+ 関数はともかく、こ`suuji`、`aaa`、`bbb`がキモいんすよねぇ……
394
+
395
+ 何がキモいって、start関数の中から決め打ちで読みにいってるあたりです。
396
+
397
+
398
+
399
+ ういう変数「状態変数」といって、
400
+
401
+ 出来だけ減らた方かっこいいのです。
402
+
403
+
404
+
405
+ そのためのテクニックはいくつかありますが、今回は2つ加えます。
406
+
407
+
408
+
409
+ - ループ仕組みをsetTimeoutを使った再起ルプ化
410
+
411
+ - start関数引数の配列を監視て続けるかやめるか選ぶように
412
+
413
+
414
+
415
+ ```JavaScript
416
+
417
+ var isNabeatsu = function (num) {
418
+
419
+ return (num % 3 === 0) || Boolean(num.toString().match(/3/));
420
+
421
+ }
422
+
423
+ var toNabeatsu = function (num) {
424
+
425
+ return isNabeatsu(num) ? 'AHO!' : num.toString();
426
+
427
+ }
428
+
429
+ var start = function (numbers) {
430
+
431
+ if (numbers[0] == null) return;
432
+
433
+ console.log(toNabeatsu(numbers[0]));
434
+
435
+ setTimeout(start.bind(null, numbers.slice(1)), 1000);
436
+
437
+ }
438
+
439
+ start([1, 2, 3, 4, 5, 6, 7, 8, 9, 10,]);
172
440
 
173
441
  // 1
174
442
 
175
-
176
-
177
- (130).toString().split("3").length
178
-
179
443
  // 2
180
444
 
181
- ```
182
-
183
-
184
-
185
- そうなんです、130のように途中に3がある文字列はそこでぶった切られる為に配列の要素数が1を超えるんですね。
186
-
187
- 念のため13や31でも挙動を確認してみましょうか
188
-
189
-
190
-
191
- ```JavaScript
192
-
193
- (13).toString().split("3")
194
-
195
- // ["1", ""]
196
-
197
- (13).toString().split("3").length
198
-
199
- // 2
200
-
201
-
202
-
203
- (31).toString().split("3")
204
-
205
- // ["", "1"]
206
-
207
- (31).toString().split("3").length
208
-
209
- // 2
210
-
211
- ```
212
-
213
-
214
-
215
- これを`isNabeatsu`関数に反映してみましょうか。
216
-
217
-
218
-
219
- ```JavaScript
220
-
221
- var isNabeatsu = function (num) {
222
-
223
- if (num % 3 === 0) {
224
-
225
- return true;
226
-
227
- } else if (num.toString().split('3').length > 1) {
228
-
229
- return true;
230
-
231
- } else {
232
-
233
- return false;
234
-
235
- }
236
-
237
- }
238
-
239
- console.log(isNabeatsu(11)); // false
240
-
241
- console.log(isNabeatsu(12)); // true
242
-
243
- console.log(isNabeatsu(13)); // true
244
-
245
- ```
246
-
247
-
248
-
249
- うん、いい感じに動作していますね。
250
-
251
-
252
-
253
- ```JavaScript
254
-
255
- var isNabeatsu = function (num) {
256
-
257
- if (num % 3 === 0) {
258
-
259
- return true;
260
-
261
- } else if (num.toString().split('3').length > 1) {
262
-
263
- return true;
264
-
265
- } else {
266
-
267
- return false;
268
-
269
- }
270
-
271
- }
272
-
273
- var start = function () {
274
-
275
- var suuji = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
276
-
277
- for (var it of suuji) {
278
-
279
- var output = isNabeatsu(it) ? 'AHO!' : ''+it;
280
-
281
- console.log(output);
282
-
283
- }
284
-
285
- }
286
-
287
- start();
288
-
289
- ```
290
-
291
-
292
-
293
- ---
294
-
295
-
296
-
297
- 最後にタイマー機能を復活させて終わりましょうか。
298
-
299
- そのまえに`suuji`の手打ちが面倒なのでさくっと数値の配列作ってくれる関数でも用意しましょうかね。
300
-
301
- これはイディオムというおまじない的なものなので、興味が出たら調べてみてください。
302
-
303
-
304
-
305
- ```JavaScript
306
-
307
- var range = function (start, end) {
308
-
309
- return Array(end - start + 1).fill(0).map(function (it, i) {
310
-
311
- return i + start;
312
-
313
- })
314
-
315
- }
316
-
317
- console.log(range(1, 3)); // [1, 2, 3]
318
-
319
- console.log(range(5, 8)); // [5, 6, 7, 8]
320
-
321
- ```
322
-
323
-
324
-
325
- これを使ってstart時に外から埋め込むようにします。
326
-
327
- まずは完成形をドン!
328
-
329
-
330
-
331
- ```JavaScript
332
-
333
- var isNabeatsu = function (num) {
334
-
335
- if (num % 3 === 0) {
336
-
337
- return true;
338
-
339
- } else if (num.toString().split('3').length > 1) {
340
-
341
- return true;
342
-
343
- } else {
344
-
345
- return false;
346
-
347
- }
348
-
349
- }
350
-
351
- var range = function (start, end) {
352
-
353
- return Array(end - start + 1).fill(0).map(function (it, i) {
354
-
355
- return i + start;
356
-
357
- })
358
-
359
- }
360
-
361
- var start = function (numbers) {
362
-
363
- if (numbers.length < 1) return;
364
-
365
- var it = numbers[0];
366
-
367
- var output = isNabeatsu(it) ? 'AHO!' : ''+it;
368
-
369
- console.log(output);
370
-
371
- setTimeout(start.bind(null, numbers.slice(1)), 1000);
372
-
373
- }
374
-
375
- start(range(1, 13));
376
-
377
- // 1
378
-
379
- // 2
380
-
381
- // AHO
445
+ // AHO!
382
446
 
383
447
  // 4
384
448
 
385
449
  // 5
386
450
 
387
- // AHO
451
+ // AHO!
388
452
 
389
453
  // 7
390
454
 
391
455
  // 8
392
456
 
393
- // AHO
457
+ // AHO!
394
458
 
395
459
  // 10
396
460
 
397
- // 11
398
-
399
- // AHO!
400
-
401
- // AHO!
402
-
403
- ```
461
+ ```
404
-
405
-
406
-
407
- ちゃんと1秒おきに文字がコンソールに表示されましたね。
462
+
408
-
409
- setIntervalは1秒毎に時を刻んでくれるので便利ですが、
463
+
410
-
411
- 変数のハンドリングが超大変です。
464
+
412
-
413
-
414
-
415
- 今回はstart関数を一度実行したら、
416
-
417
- 2度目以降は毎回setTimeoutを使う仕様にしました。
418
-
419
-
420
-
421
- いやいや、start関数はnumbersという数字の配列寄越せって言ってるんだから、
422
-
423
- setTimeoutやsetIntervalで使えないでしょ!どうすんの!?
424
-
425
- そんなこともあろうかと、関数には[function.prototype.bind](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Function/bind)というメソッドが用意されています。
465
+ 全ての関数には[function.prototype.bind](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Function/bind)というメソッドが用意されています。
426
466
 
427
467
  まぁ、要するに関数に値を適用しつつ、実行しないで待ってくれるという必殺技があるんです。
428
468
 
@@ -448,9 +488,9 @@
448
488
 
449
489
 
450
490
 
451
- また、配列には[array.prototype.slice](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/slice)というメソッドが用意されてお
491
+ 更に配列には[array.prototype.slice](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/slice)という配列の一部を切り出すメソッドがます。
452
-
492
+
453
- 配列って減ら事が可能です。
493
+ これ利用して金太郎アメのようにnumbers変数の関数を頭から1個ずつ削り取って利用る作りにしています。
454
494
 
455
495
 
456
496
 
@@ -460,6 +500,12 @@
460
500
 
461
501
  // [2, 3]
462
502
 
503
+
504
+
505
+ [1, 2, 3].slice(1).slice(1)
506
+
507
+ // [3]
508
+
463
509
  ```
464
510
 
465
511