18664


数字の0から1msごとに+1していき、5になったらまた0からスタートするプログラムを書きたいです。考え方が浮かびません。。考え方のアルゴリズムを教えていただきたいです。


CC BY-SA 3.0
2015-11-11T04:36:27.570
18656
2015-11-16T14:19:37.297
13210
question
javascript
0から始まり5までいったらまた0からスタートする


だいたい次の様な感じでやればいいかと思います。  
実行目的が分からないのでそこら辺は適時変更で。

[code]

    <html>
    <head>
    <title>example</title>
    
    <script type="text/javascript">
    var intervalID;
    var counter = 0;
    var result;
    
    function increment(){//カウンタをインクリメントする関数、結果の表示は確認用
     result.innerHTML += counter;//実行結果(カウンタの現在値)を表示する
     if(++counter == 6){//カウンタを１足して６になったら
      counter = 0;   //リセットして０にする
      result.innerHTML += "<br/>";//改行する
     }
    }
    function start(){
     result = document.getElementById("out");
     intervalID = setInterval(increment, 1);//インターバル１ｍｓでincrement関数を呼び出す、1ms間隔で実際に呼び出せるかは多分無理
    }
    
    function end(){
     clearInterval(intervalID);
    }
    </script>
    </head>
    <body>
    <button onclick="start();">実行</button>
    <button onclick="end();">終了</button>
    <div id="out">
    </div>
    </body>
    </html>
[/code]


CC BY-SA 3.0
2015-11-11T05:13:38.450
18661
2015-11-11T05:13:38.450
5044
18656
answer


考え方としては  
・1ms毎に発動するタイマーをセットし、処理を行う  
・処理の中で1ずつ増やし、上限値に達したら初期値にリセットする  
以上の繰り返しで良いかと思います。

[code]

    var timer = null;
    var interval = 1;
    var limit = 6;
    var counter = 1;
    
    /**
     * 素直にカウンタを＋１して上限でリセット
     **/
    function sample1(){
      if(null!=timer){clearInterval(timer);}
      counter=0;
      timer = setInterval(
        function(){
          document.getElementById("console").innerText = counter;
          if(limit<=++counter){counter=0;}
        },
        interval
      );
    }
    
    /**
     * 上限でモジュロ(余りを出力)する
     **/
    function sample2(){
      if(null!=timer){clearInterval(timer);}
      counter=0;
      timer = setInterval(
        function(){
          document.getElementById("console").innerText = counter;
          counter=++counter%limit;
        },
        interval
      );
    }
    
    /**
     * 応用：現在秒をミリ秒で取得し、間隔で割り、モジュロし、1ズラす
     **/
    function sample3(){
      if(null!=timer){clearInterval(timer);}
      timer = setInterval(
        function(){
          var res = ((+new Date())/interval).toFixed(0)%limit;
          document.getElementById("console").innerText = res.toString();
        },
        interval
      );
    }
    
    /**
     * サンプル停止
     **/
    function stopSample(){
      if(null!=timer){clearInterval(timer);}
      document.getElementById("console").innerText = "";
    }
      
[/code]

[code]

    <div id="console"></div>
    <button onclick="sample1();">sample1</button>
    <button onclick="sample2();">sample2</button>
    <button onclick="sample3();">sample3</button>
    <button onclick="stopSample();">stop</button>
[/code]


CC BY-SA 3.0
2015-11-11T05:35:52.773
18663
2015-11-11T05:44:58.567
2015-11-11T05:44:58.567
7676
7676
18656
answer


  
アルゴリズムが分からないというよりは、コードにしていく過程でつまづかれているのかな、と思いました。  
できるだけ小さい単位でコードを追加し、テストするようにするとよいです。  
常に追加している機能の部分以外はテスト済みで、間違いが無い、と安心できるようにしておきたい所です。

以下は例です。追加部分は小く、追加部分以外のコードは動く、という形を保っています。  
もちろん、完成後にコードを整理する必要はあるかもしれません。  
(追記: ここで「テスト」としているのは「動かしてみる」という程度の意味です)

### 最低限のコードでテスト

まず、カウントを記憶させるcount変数を作ります。「数字の0から」なので、0を代入しておきます。

[code]

    var count = 0;
    
[/code]

次にカウントを表示するcount_show関数を作ります。  
HTMLに id="count" の要素を用意しておくものとします。

[code]

    var count_show_elm = document.getElementById("count");
    function count_show(){
        count_show_elm.textContent = count;
    }
    
[/code]

ここらへんでcount_show関数を一度呼んでみて、テストしておきす。  
カウント: 0 が表示されれば成功です。

[code]

    var count = 0;
    
    var count_show_elm = document.getElementById("count");
    function count_show(){
        count_show_elm.textContent = count;
    }
    
    count_show();
[/code]

[code]

    <p> カウント:
         <span id="count"></span>
    </p>
[/code]

### 最小限の機能追加でテスト

次にカウントを変化させるcount_change関数を作ります。  
count変数に1を足した後にcount_show関数を呼び出し、表示を更新する事にします。  
今は最小限の追加なので、5を超えた場合の事は後で考えます。

[code]

    function count_change(){
        count += 1;
        count_show();
    }
    
[/code]

これもすぐテストします。  
カウント: 1 になれば成功です。

[code]

    var count = 0;
    
    var count_show_elm = document.getElementById("count");
    function count_show(){
        count_show_elm.textContent = count;
    }
    
    function count_change(){
        count += 1;
        count_show();
    }
    
    count_show();
    count_change();
[/code]

[code]

    <p> カウント:
         <span id="count"></span>
    </p>
[/code]

### 動きのある物は観察しやすい形でテスト

次にcount_change関数を呼ぶのを一定時間おきに繰り返すようにします。  
観察しやすいように、とりあえず一秒毎にしておきます。

[code]

    setInterval(count_change, 1000);
    
[/code]

これもすぐにテストします。

[code]

    var count = 0;
    
    var count_show_elm = document.getElementById("count");
    function count_show(){
        count_show_elm.textContent = count;
    }
    
    function count_change(){
        count += 1;
        count_show();
    }
    
    count_show();
    setInterval(count_change, 1000);
[/code]

[code]

    <p> カウント:
         <span id="count"></span>
    </p>
[/code]

### つまづきがちな機能は、他が完全に動いている事が確信できる環境で追加

count_change関数に5を超えた場合はcount変数を0に戻す処理を入れて完成です。

[code]

    function count_change(){
        count += 1;
        if(count > 5){
            count = 0;
        }
        count_show();
    }
    
[/code]

[code]

    var count = 0;
    
    var count_show_elm = document.getElementById("count");
    function count_show(){
        count_show_elm.textContent = count;
    }
    
    function count_change(){
        count += 1;
        if(count > 5){
            count = 0;
        }
        count_show();
    }
    
    count_show();
    setInterval(count_change, 1000);
[/code]

[code]

    <p> カウント:
         <span id="count"></span>
    </p>
[/code]


CC BY-SA 3.0
2015-11-11T05:55:18.737
18664
2015-11-11T06:06:26.250
2015-11-11T06:06:26.250
3054
3054
18656
answer


# 一番簡単な考え方

一番簡単な考え方は次の通りです。

  * for文でループカウンタ`i`について以下の処理を繰り返す 
    * `i % 6`を表示する
    * 1ms停止する

[code]

    co(function*() {
      for (var i = 0;; i++) {                                       // 以下をfor文で繰り返す
        document.getElementById("counter").textContent = i % 6;     // 1 % 6 を表示する
        yield new Promise(resolve => setTimeout(resolve, 1));       // 1ms停止する
      }
    });
[/code]

[code]

    <script src="https://cdnjs.cloudflare.com/ajax/libs/co/4.1.0/index.js"></script>
    <div id="counter"></div>
[/code]

# 精度が重要な場合

`setTimeout`や`setInterval`の間隔は最小でも数ミリ秒になるため、1ミリ秒間隔で操作することはできません。また、`setTimeout(func,
delay)`では正確に`delay`ミリ秒後に呼び出されるわけではないので繰り返すと誤差が蓄積していきますし、`setInterval`では誤差の扱いは定められていません。ブラウザの状態によっては、自動的に頻度を低下させられる可能性もあります。このため、`setTimeout`や`setInterval`ではまったく要求を達成できません。

もっとも信頼できる方法はおそらく、`window.postMessage`を利用してなるべく高速に非同期処理を繰り返し、`performance.now`で開始時刻との差を測ってそれに応じて表示を変更する方法です。これは`onmessage`のたびに正確な時刻が表示されるので、基本的にはマシンが早ければ早いほど精度が増します。ただし`performance.now`の精度の上限1マイクロ秒までです。

以下のコードなら長時間動かし続けても誤差は広がっていきませんし、一時的にマシンの負荷が大きくなって誤差が広がっても、あとで精度を取り戻すことができます。`window.postMessage`のコールバックは自分の環境だと毎秒10000回ほど呼べているようなので、誤差の大きさは平均で0.1ミリ秒程度だと思われます。

[code]

    var counter = document.getElementById("counter");
    var start = performance.now();
    window.addEventListener("message", () => {
      counter.textContent = (performance.now() - start | 0) % 6;    
      window.postMessage(null, "*");
    });
    window.postMessage(null, "*");
[/code]

[code]

    <div id="counter"></div>
[/code]


CC BY-SA 3.0
2015-11-15T16:59:26.690
18857
2015-11-16T14:19:37.297
2015-11-16T14:19:37.297
10330
10330
18656
answer