質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.79%

3と3の倍数はアホと叫ぶようにしたい

解決済

回答 5

投稿 編集

  • 評価
  • クリップ 7
  • VIEW 6,999

score 43

いつもお世話になっております。
javascript初心者です。

現在、3と3の倍数になったときに「AHO!」と叫ぶプログラミングを作成しております。
やりたいことは至ってシンプルでブラウザ上で1ずつカウントアップされていく中で
3と6と9に到達したときに「AHO!」と表示させたいです。
3と6と9に到達したときにイベント起こす為に条件分岐作成したのですが、上手く作動していないのが現状です。

ご教示の程、宜しくお願いします。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>3と3の倍数はアホと叫ぶ</title>
<script type="text/javascript">
    var suuji = ["1","2","3","4","5","6","7","8","9","10"];
    var aaa = 0;
    var bbb = setInterval(start, 1000);

    function start() {
        if (aaa < 10) {
            document.write(suuji[aaa] + " ");
            aaa++;
        }else{
            clearInterval(bbb); // タイマー停止
       document.write("AHO!");
        }
    }
</script>

</body>
</html>

試してみたこと①

var aaa = 0;の部分を3/0にしてみるも「AHO!」としか表示されず。


試したこと②

        if (aaa < 10) {
            document.write(suuji[aaa] + " ");
            aaa++;
        }else if(aaa == 3){
            document.write("AHO!");
        }else{
            clearInterval(bbb); 
    }

3に到達したときにイベント起こすように条件分岐追加してみるも動きが停止してしまいました。
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • kei344

    2018/03/22 09:32

    まだ質問が「受付中」になっていますが、いったん「解決済」にされてはいかがでしょうか。また、解決されていないなら状況を質問文に追記ください。

    キャンセル

回答 5

checkベストアンサー

+17

まず、今回の質問文の改良点的な所が幾つか見受けられます。

  • document.writeは非推奨なのでやめよう
  • 数字がアホになるか否かの判定ロジックは関数として切り分けよう
  • 数字を入れるとアホ、もしくは数字を返すロジックも関数として切り分けよう
  • タイマー機能を改良しよう

document.writeは非推奨なのでやめよう

document.writeはJavaScript(以下JS)黎明期の書き方ですが、
これは様々な箇所で不具合が出るダメな子です。
結果、どのJSの書籍でも「document.write」は使うな!と明記される程嫌われている機能なので忘れてください。

DOMツリー構造を動的に変更するやり方を覚えましょう。
jQueryというライブラリを扱うのが初心者にはオススメです。
jQueryのDOMを挿入を覚えてください。

この反映は質問者さんへの課題として残しておきます。

従って、今回の回答文ではconsole.logとデベロッパーツールによる簡易的な表示方法を利用していきます。
F12キーを押すと、デベロッパーツールが立ち上がります。
console.log(123)という風に実行すると、デベロッパーツールのconsoleタブに結果を出力してくれます。


数字がアホになるか否かの判定ロジックは関数として切り分けよう

3の倍数でアホになる判定ロジックは関数として外部に切り出してください。
関数にしてしまえば、結果の確認がとても楽になりますよ。

isナベアツです!
numという数値の引数を要求しており、アホになりそうだったらtrue
普通の回答で数字を教えてくれそうならfalseになります。

var isNabeatsu = function (num) {
  return num % 3 === 0;
}
console.log(isNabeatsu(1)); // false
console.log(isNabeatsu(2)); // false
console.log(isNabeatsu(3)); // true
console.log(isNabeatsu(4)); // false
console.log(isNabeatsu(5)); // false
console.log(isNabeatsu(6)); // true

よしよし、うまく動作しているようですね。
ですが、これはまだ3の倍数だけで判定するので、肝心の13や23等で反応しなさそうです。

var isNabeatsu = function (num) {
  return num % 3 === 0;
}
console.log(isNabeatsu(13)); // false
console.log(isNabeatsu(23)); // false
console.log(isNabeatsu(31)); // false

あらら、全滅です…
では、これを改良して完成させましょうか。
「数字の3が含まれている場合はfalseではなくtrueになる」が条件です。

この数字の3を探す…といった風な用途は「文字列検索」といいます。
JSには数値型や文字列型、配列、オブジェクト…など、様々な型が存在します。
それらの型には値を加工するのに使う便利な機能(メソッド)が用意されています。

先程3の倍数か否かを確認する為に、num % 3と入力しましたね。
数値型は数値計算に利用される型であり、他の型では数値計算は不可能です。
でも数字の3を探したい……こういう要望は文字列型が得意とする仕事なので数値型から文字列型へ変換(キャスト)する必要があります。

プログラマのテクニックの一つとして、数値として判断出来る値は基本的には数値型として所持しておき、
文字列検索がしたくなったらその時だけ、文字列型にキャストする使い方が分かりやすくオススメです。

今回はString.prototype.matchを利用することにしました。
正規表現を使って文字列を探す少々高難度の手法ですが、3という文字列を探すだけの正規表現は/3/とわりと簡単なので採用しました。
(他にも様々な方法で解決することが可能で、10人に頼めば10人共がちょっとずつ違う判定ロジックを持ってくると思います。探してみてね!!)

// 文字列検索を行うmatchメソッドは文字列型限定のメソッド
var num = 123;
num.match(/3/);
// Uncaught TypeError: num.match is not a function

// JSでは.toString()を使えば全ての値が文字列型にキャストされる
num.toString().match(/3/);
// ["3", index: 2, input: "123", groups: undefined]

// 値をカッコで包んだりしてそのまま.toString()で文字列型にして使うことも可能
(17).toString().match(/3/);
// null

// 論理型にキャストするにはBooleanを使う
Boolean((31).toString().match(/3/));
// true
Boolean((16).toString().match(/3/));
// false

これを元にisナベアツ関数を完成させましょう。
「3で割り切れる数字」もしくは「数字の3がある」場合ですので、
論理和であるor演算子||を利用します。

var isNabeatsu = function (num) {
  return (num % 3 === 0) || Boolean(num.toString().match(/3/));
}
console.log(isNabeatsu(1)); // false
console.log(isNabeatsu(2)); // false
console.log(isNabeatsu(3)); // true
console.log(isNabeatsu(13)); // true
console.log(isNabeatsu(23)); // true
console.log(isNabeatsu(31)); // true
console.log(isNabeatsu(25)); // false

いい感じに動作してますね。
isナベアツ関数完成です。


数字を入れるとアホ、もしくは数字を返すロジックも関数として切り分けよう

今度はtoナベアツ関数を作成しましょうか。
これも切り分ける事でテストが非常にしやすくなります。

もうアホになるべき数字か否かを判定する関数は出来ていますね?
今回は「特定条件でAHO!」か「そうでなければ数字をそのまま使う」ので、三項演算子を使ってみましょうか。
ついでに、AHO!という文字列が返ってくる可能性がある以上、もう片方の数字も文字列型にキャストして同じ文字列型に合わせておいたほうが使いやすそうですね。

var isNabeatsu = function (num) {
  return (num % 3 === 0) || Boolean(num.toString().match(/3/));
}
var toNabeatsu = function (num) {
  return isNabeatsu(num) ? 'AHO!' : num.toString();
}
console.log(toNabeatsu(1)); // 1
console.log(toNabeatsu(2)); // 2
console.log(toNabeatsu(3)); // AHO!
console.log(toNabeatsu(13)); // AHO!
console.log(toNabeatsu(23)); // AHO!
console.log(toNabeatsu(31)); // AHO!

はい完了。
このように同じ数字を入れると、必ず同じ値が返ってくるような処理は関数化しておくと、
このように動作確認が楽ですね。


早速これを質問文に組み込みましょう。
suuji変数に入っている配列を全て消化したら完了というロジックに変更しています。

var isNabeatsu = function (num) {
  return (num % 3 === 0) || Boolean(num.toString().match(/3/));
}
var toNabeatsu = function (num) {
  return isNabeatsu(num) ? 'AHO!' : num.toString();
}
var suuji = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var aaa = 0;
var bbb = setInterval(start, 1000);

function start() {
  // 表示したい文字列は今回はconsole.logを使って表示
  console.log(toNabeatsu(suuji[aaa]));
  aaa++;
  if (aaa >= suuji.length) {
    clearInterval(bbb); // タイマー停止
  }
}
// 1
// 2
// AHO!
// 4
// 5
// AHO!
// 7
// 8
// AHO!
// 10

1秒おきにconsole.logが実行されて、表示されていく様が分かります。
10秒後に数字の値が全て表示されて、これで完了ですね。


【おまけ】

タイマー機能を改良しよう

見事動くようになりましたが、
関数はともかく、このsuujiaaabbbがキモいんですよねぇ……
何がキモいって、start関数の中から決め打ちで読みにいってるあたりです。

こういう変数は「状態変数」といって、
出来るだけ減らした方がかっこいいのです。

そのためのテクニックはいくつかありますが、今回は2つ加えます。

  • ループの仕組みをsetTimeoutを使った再起ループ化
  • start関数は引数の配列を監視して続けるかやめるか選ぶように
var isNabeatsu = function (num) {
  return (num % 3 === 0) || Boolean(num.toString().match(/3/));
}
var toNabeatsu = function (num) {
  return isNabeatsu(num) ? 'AHO!' : num.toString();
}
var start = function (numbers) {
  if (numbers[0] == null) return;
  console.log(toNabeatsu(numbers[0]));
  setTimeout(start.bind(null, numbers.slice(1)), 1000);
}
start([1, 2, 3, 4, 5, 6, 7, 8, 9, 10,]);
// 1
// 2
// AHO!
// 4
// 5
// AHO!
// 7
// 8
// AHO!
// 10

全ての関数にはfunction.prototype.bindというメソッドが用意されています。
まぁ、要するに関数に値を適用しつつ、実行しないで待ってくれるという必殺技があるんです。

var add = function (a, b) { return a + b; }
add(2, 3);
// 5

var add10 = add.bind(null, 10);
add10(5);
// 15

更に配列にはarray.prototype.sliceという配列の一部を切り出すメソッドがあります。
これを利用して金太郎アメのようにnumbers変数の関数を頭から1個ずつ削り取って利用する作りにしています。

[1, 2, 3].slice(1)
// [2, 3]

[1, 2, 3].slice(1).slice(1)
// [3]

全体的な流れとしてはこうです。

  • [1, 2, 3 ...]という配列がnumbersに入って実行
  • 先頭の1を取り出す
  • [1, 2, 3 ...].slice(1)を実行して、得た結果[2, 3 ...]を次のstartに格納しておく
  • 1秒後、次のstartが実行、今度は先頭の2を取り出す
  • これを繰り返し、最終的に空の配列[]を次のstartに格納
  • 1秒後、次のstartが実行、先頭のアイテムが無いので終了

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/03/22 09:39

    > 3と3の倍数になったとき
    ホントだ!騙された!!!

    > think49さん、defghi1977さん
    ありがとうございました。
    騙されてました(´・ω・`)

    キャンセル

  • 2018/03/22 13:42

    To: defghi1977 さん、miyabi-sun さん
    当初は質問者がコメント等で条件に言及している可能性を考えていました。
    誤解が解けて良かったです。

    キャンセル

  • 2018/03/22 14:09

    コードを載せた後に気が付いて, どうしようか迷っていたところにmiyabi-sunさんが後に続いて回答なさったために, おおぅどうすべえ…でも十分条件は満たしているから良いかな―と.(私の方で一言コメント残しとけばよかったですね)

    キャンセル

+3

例えばこんな

<output id="out" style="white-space:pre;"></output>
<script>
"use strict";
{
    let i = 0;
    const max = 100, h = setInterval(() => {
        let result = ++i % 3 == 0 || i.toString().match(/3/) ? "aho!" : i;
        out.value += `${result}\n\r`;
        if(i >= max){
            clearInterval(h);
        }
    }, 1000);
}
</script>


NOTE:
当初はdocument.writeを使っていましたが, FireFoxにおいてsetIntervalによる非同期処理と相性が悪かったため, 出力先を変更しています.

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/03/19 00:13

    紛れもないJavaSceiptですね。
    この回答文では、ES2015という下位互換を残しながら、
    他の人気言語の構文をいくつも取り入れた新しいバージョンの書き方を利用しています。

    初めての人は面食らうかと思いますが、IE11以外のブラウザの殆どは対応しており普通に使えます。
    実際に試してみて下さい。

    キャンセル

  • 2018/03/19 00:32

    この手の質問だと大抵回答がコードの大喜利になってしまうので, このような記述の方法もあるよ程度に色々調べてみて下さい.

    キャンセル

  • 2018/03/19 00:48

    回答ありがとうございます。
    そうだったんですね、ごめんなさい(>_<)
    仰る通りに色々調べてみます。

    キャンセル

+3

余計なお世話かもしれませんが、文字列比較を使わないバージョンも載せておきます。
ご参考にしてください。

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
        <script type="text/javascript">
window.onload = function() {
    function aho(x) {
        if (x % 3 == 0)
            return true;
        while (x > 0) {
            if (x % 10 == 3)
                return true;
            x = Math.floor(x / 10);
        }
        return false;
    }
    var p = document.getElementById('number');
    var x = 1;
    function update() {
        p.innerHTML += aho(x) ? 'AHO!' : x;
        p.innerHTML += '<br>';
        x++;
    }
    setInterval(update, 100);
};
        </script>
    </head>
    <body>
        <p id="number"></p>
    </body>
</html>

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+2

注: document.write は文書ストリームに書き込みを行うため、閉じられた(ロード済みの)文書で document.write を呼び出すと、自動的に document.open が呼ばれ、文書はクリアされます
document.write - Web API インターフェイス | MDN

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

for (var i = 1; i < 11) {
    if (i % 3 === 0) {
        document.write("AHO!");
    } else {
        document.write(i);
    }
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/03/19 07:06

    to harunikuさん
    「カウントアップ」が意図するところ(何をどうしたいのか)が明らかとなっていない点と「イベント」の言葉の使い方がJavaScript本来の意味と異なる点でこれ以上答えることが出来ない(答える意味がない)のですよ.

    キャンセル

  • 2018/03/20 13:19

    要件を半分しか満たしていないとはいえ、これで複数の低評価がつく理由を知りたい。

    キャンセル

  • 2018/07/11 17:53

    > x_x さん
    要件を満たしていないとは実は考えていません。
    「3と3の倍数」って=「3の倍数」でしょ?
    「3がつくとき」という「ナベアツ」と要件が異なってますから。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.79%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る