20190821のJavaScriptに関する記事は21件です。

動的なURLをブックマークする方法を紹介

こんにちは、プログラミングスクールのレビューサイト「スクールレポート」を運営しているアカネヤ(@ToshioAkaneya)です。

動的なURLをブックマークする方法を紹介

URLに日付が含まれている場合など、動的なURLブックマークしたい場合があるかと思います。

そのような場合は、ローカルにHTMLファイルを作成してそれをブックマークしましょう。
そして、<script>タグを作成して、window.locationプロパティを使用することで、目的のURLにリダイレクトするようにします。

このようにして動的なURLをブックマークすることができます。この記事が参考になれば幸いです。

終わりに

Ruby on RailsとVueで作成したプログラミングスクールのレビューサイトを運営しています。良ければご覧ください。https://school-report.com/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

配列をシャッフルするアルゴリズムを思いついたが既存だった話【JavaScript/Rubyサンプルコードあり】

0.前提

 アルゴリズムそのものは筆者自身がゼロから考えました。もし他の方の考えたアルゴリズムと同じだったとしてもごめんなさい。アルゴリズム界隈にそこまで詳しくないのでこれが有名なアルゴリズムだったとしても知らない
 →追記:どうやら、フィッシャー–イェーツのシャッフル - Wikipediaという有名なヤツらしいです。本当に何も知らなかったんです。信じてください。ここから先、サンプルコードを書いてる途中で気づくまでウッキウキで解説してる筆者の展望をご覧ください。書き直すのも億劫なのでそのままにしておきます。

1.アルゴリズム

 さて、このアルゴリズムはもともと前回【JavaScript】重複のない乱数配列を作ってみた【ES6(ES2015)】でアルゴリズムを考えていたときに生まれたものです。もし気になるならそっちも見てみてください。
 前回に続いて『アルゴリズムこうしん~♪』とかやろうと思いましたが今回はやめておきます。
 先にアルゴリズムを説明しておくことにしましょう。サンプルコードは私が書ける言語の範囲()でこの下に書いておきます。
 ひとまず、要素数5の配列で説明します。ほぼ前回のと同じですが笑。

initAry:

添字 0 1 2 3 4
要素 kurorin kotone hime hina tyanmari

 この配列をシャッフルするよ!

 0~4(initAryの添字の最大値)の乱数を発生させて、initAryからその乱数の位置にある要素を一つ取り出し新しい配列randAryに入れるよ。

randomNumber:3
取り出す要素はinitAry[3] = hime
initAry:

添字 0 1 2 3
要素 kurorin kotone hina tyanmari

randAry:

添字 0
要素 hime

 これを、initAryが空になるまで繰り返すよ。
 0~3(initAryの添字の最大値)の乱数を発生させて、initAryからその乱数の位置にある要素を一つ取り出し配列randAryに入れるよ。

randomNumber:2
取り出す要素はinitAry[2] = hina

initAry:

添字 0 1 2
要素 kurorin kotone tyanmari

randAry:

添字 0 1
要素 hime hina

 ・・・(略)・・・

randomNumber:0
取り出す要素はinitAry[0] = kotone
initAry:

添字
要素

randAry:

添字 0 1 2 3 4
要素 hime hina kurorin tyanmari kotone

 さて、要素数5の配列がシャッフルできたよ!
 これを要素数nの配列でやればシャッフルできるよ!すっごーい!

3.サンプルコード

 そこまで難しくないと思うので、解説は簡略化します。

JavaScript(ES6)

//min以上max未満の乱数を発生させる関数
const getRandomInt =(min, max) => {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random() * (max - min)) + min;
}

//配列をシャッフルする関数
const getRandomAry = (ary) => {
  const initAry = ary.slice();
  let randAry = [];
  while(initAry.length > 0){
    randAry.push(initAry.splice(getRandomInt(0,initAry.length), 1).pop());
  }
  return randAry;
}

 ES6(ES2015)のアロー関数を用いているので、非対応環境ではfunctionとか使って書いてください。
 わざわざconst initAry = ary.slice();と二度手間を踏んでいるのは値渡しをするためです。これをしないと、参照渡しとなり、関数呼び出し時に引数として渡した配列が空になってしまうためです。アロー関数だと引数が配列の場合、参照渡し参照の値渡しになりその配列を破壊的に操作されるようですね・・・。別で記事まとめました→【JavaScript】配列を引数渡しすると破壊的に動作する話【メモ書き】

Ruby

def get_random_array(initAry)
  randAry = Array.new
  while(initAry.length > 0)
    randAry.push(initAry.slice!(rand(initAry.length)))
  end
  return randAry
end

 コード書いてて何故か処理が終わらないな・・・と思ってたらslice!sliceと書いていて破壊的メソッドになっていないからでした。
 ってか、書いてみたけどArrayクラスにshuffleってメソッドあるじゃん。というわけでそれを使いましょう。

 C++ 

#include<vector>

~サンプルコード作成途中の話~
 ・・・ん? なんかstd::shuffleってのがあるな?

以下の実装では、フィッシャー - イェーツのシャッフルアルゴリズムが使用されている
shuffle - cpprefjp C++日本語リファレンス

 「フィッシャー - イェーツのシャッフル」・・・?
 ~Wikipediaを読む筆者~

 ・・・。

 ・・・・・・。

 一緒じゃねえか!!
 誰がねェ・・・誰がどう見ても、おんなじやおんなじや思てェ!!
 うわああああああああああああああああああああああああああん!!!!
 ・・・書きかけのサンプルコード、乗りかかった船だし
 やってやろうじゃねえか!まあゆくどりさんは天才ですから?!?!
 (何でも言うことを聞いてくれるアカネチャン風)
 ということで動的配列vectorを使って書こうとしました。

 ・・・・・・。

 が。乱数生成の段階で詰まりました。DxLibくらいでしか触れたことのない私ではまだ無理でした。ヘッダとか色々読みましたが、最近のC++ではC言語のrand()やsrand()を使うのは非推奨なようで、よく理解してない私が「これがサンプルコードです」と提示すると界隈の方々に叩かれそうなので止めました。また理解できるようになってから覚えていたら帰ってきてここに書くことにします。

4.参考文献

JavaScriptで配列のコピー(値渡し) - Qiita
るりまサーチ(サンプルコード作成時リファレンスの検索に使用)
shuffle - cpprefjp C++日本語リファレンス

5.さいごに

 結局、思いついたのは既存アルゴリズムでした。まあ、このアルゴリズムを自分で思いついたのでよく理解できていますし、損はしないと思っています。また何か思いついたりしたら書きます。
 ありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RaspberryPiで監視カメラ(カメラモジュール+USB Audioのデータをブラウザで表示+再生)

はじめに

前回、WebSocketで受信した画像をブラウザで表示する仕組みを調べたので、RaspberryPiのカメラモジュールと繋げてみました。

あと、USB Audioデバイスを買ったのでWebSocketでデータを流して、ブラウザで再生する処理も入れてみました。

簡易的な監視カメラができたので、記事としてまとめておきます。

ハードウェア

  • Raspberry Pi 3 Model B+

  • カメラモジュール

  • USB audio

    秋月で400円くらい。lsusbで認識された情報だとIntel製。ボリュームが小さくでちょっと不満。もっと高いのにすればよかった。

raspai.jpg

  • クライアント

    chromeで動作確認

仕様

  1. Imageサーバーは、ソケット接続されたらカメラの画像を送信し続ける
  2. Audioサーバーは、ソケット接続されたらAudio INから取得したデータを送信し続ける
  3. ブラウザで画像データを受信して、canvasに描画する
  4. ブラウザで音声データを受信して、db値をcanvasに描画する
  5. ボタンをクリックすると、WebAudioで音声を再生する

anime_monitor.gif

音声再生中はタブにスピーカーのアイコンが表示されます。

クライアント側

ブラウザで表示するためのhtmlとjsを用意します。

HTML

音声データ表示用のcanvasと再生ボタン、画像用のcanvasを用意します。

WebAudioはユーザー操作のイベントで開始する必要があるので、ボタンを用意する必要があります。

index.html
<html>
  <head>
    <script src="./client.js"></script>
  </head>
  <body onload="on_load();">
    <div>
      <canvas id="canvas_audio" width="500" height="40"></canvas>
    </div>
    <input type="button" value="play audio" onclick="on_button_play_audio();">
    <div>
      <canvas id="canvas_image" width="640" height="480"></canvas>
    </div>
  </body>
</html>

javascript

画像データ受信ハンドラでcanvasに描画、音声データ受信ハンドラでcanvasにグラフを表示する&音声再生します。

  • 画像データ

前回のサンプルのままだと大きい画像のbase64文字列の生成でエラーになりました。調査時はシンプルなPNGにしたので圧縮率が高かったけど、写真のjpegになったらサイズが小さくならなかったと推測。かっこ悪いですがループで文字列を結合してbase64変換しています。

  • 音声データ

WebAudioを使用して音声を再生します。受信データがfloat32なのでそのまま流せば再生できます。

ユーザー操作しないと音声再生できないので、クリックのイベントを用意します。

データが受信できているかを確認できるようにdb値をcanvasに表示します。db値の計算はネットで調べてて適当に実装したから合ってるか自信ないです。

client.js
var image_socket = null;

var audio_socket = null;
var audio_ctx = null;
var scheduled_time = 0;
var delay_sec = 1;

function on_load(){
  // ここではaudioの再生に関する処理は許可されないのでWebSock処理のみ行う

  // 画像通信用WebSocket接続
  img_url = "ws://" + location.hostname + ":60002"
  image_socket = new WebSocket(img_url);
  image_socket.binaryType = 'arraybuffer';
  image_socket.onmessage = on_image_message;

  // 音声通信用WebSocket接続
  audio_url = "ws://" + location.hostname + ":60003"
  audio_socket = new WebSocket(audio_url);
  audio_socket.binaryType = 'arraybuffer';
  audio_socket.onmessage = on_audio_message;

}

function on_button_play_audio(){
  // ユーザー操作イベントでAudioの再生操作を行う

  if(audio_ctx == null){
    audio_ctx = new (window.AudioContext||window.webkitAudioContext)
  }
}

function on_image_message(recv_data){
  // 受信したデータをbase64文字列に変換
  var recv_image_data = new Uint8Array(recv_data.data);
  var base64_data = ""
  for (var i=0; i < recv_image_data.length; i++) {
      base64_data += String.fromCharCode(recv_image_data[i]);
  }

  // 画像をcanvasに描画
  var canvas_image = document.getElementById('canvas_image');
  var ctx = canvas_image.getContext('2d');
  var image = new Image();
  image.onload = function() {
    ctx.drawImage(image, 0, 0);
  }
  image.src = 'data:image/jpeg;base64,' + window.btoa(base64_data);
}

function on_audio_message(recv_data){
  audio_f32 = new Float32Array(recv_data.data);
  if(audio_ctx != null){
    play_audio(audio_f32);
  }
  draw_audio_graph(audio_f32);
}

function play_audio(data){
  var audio_buffer = audio_ctx.createBuffer(1, data.length, 44100);
  var buffer_source = audio_ctx.createBufferSource();
  var current_time = audio_ctx.currentTime;

  audio_buffer.getChannelData(0).set(data);
  buffer_source.buffer = audio_buffer;
  buffer_source.connect(audio_ctx.destination);

  if (current_time < scheduled_time) {
    buffer_source.start(scheduled_time);
    scheduled_time += audio_buffer.duration;
  } else {
    buffer_source.start(current_time);
    scheduled_time = current_time + audio_buffer.duration + delay_sec;
  }
}

function draw_audio_graph(data){
  var canvas_image = document.getElementById('canvas_audio');
  var ctx_2d = canvas_image.getContext('2d');

  // db値計算(自信ない)
  sum_data = data.reduce((a,b)=>Math.abs(a)+Math.abs(b));
  mean_data = sum_data / data.length;
  dB = 20 * Math.log10(mean_data);

  // 前回描画をクリア
  ctx_2d.fillStyle = '#ffffff';
  ctx_2d.fillRect(0,0,500,40);

  var rate = 2;
  var graph_x = Math.abs(dB) * rate;
  ctx_2d.fillStyle = '#000000';
  ctx_2d.fillRect(0,0,graph_x,40);

  // 値表示
  ctx_2d.fillStyle = '#ff0000';
  ctx_2d.font = "10px serif";
  ctx_2d.fillText("db:" + dB , 5 , 10 );
}

サーバー側

画像用と音声用で2つのWebSoket通信を行うので、2つWebSoketのサーバーを用意します。

httpのサーバーも必要になるので、まとめて起動する処理も用意します。

Imageサーバー

PiCameraで取得した画像をブラウザに送信します。

imageServer.py
import asyncio
import websockets
import io
from picamera import PiCamera

class ImageServer:

    def __init__(self, loop, address , port):
        self.loop = loop
        self.address = address
        self.port = port
        self.camera = PiCamera()

    async def _handler(self, websocket, path):
        with io.BytesIO() as stream:
            for _ in self.camera.capture_continuous(stream, format='jpeg', use_video_port=True, resize=(640,480)):
                stream.seek(0)

                try:
                    await websocket.send(stream.read())
                except:
                    print('image send Error.')
                    break

                stream.seek(0)
                stream.truncate()

    def run(self):
        self._server = websockets.serve(self._handler, self.address, self.port)
        self.loop.run_until_complete(self._server)
        self.loop.run_forever()

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    ws_is = ImageServer(loop, '0.0.0.0', 60002)
    ws_is.run()


Audioサーバー

pyaudioで取得した音声データをブラウザに送信します。クライアントであるWebAudioにfloat32で渡すのでpaFloat32を指定します。

同一スレッドで読み込みと送信を行ったらバッファ不足のエラーが出たので読み込みと送信は別スレッドで行うようにしています。

複数接続は考慮していない実装です。

AudioServer()クラスに、デバイス名を指定するので環境に合わせた文字列を設定してください。

AudioServer.py
import asyncio
import websockets
import pyaudio
import threading
import queue

class AudioServer:

    def __init__(self, loop, address, port, device_name):
        self.loop = loop
        self.address = address
        self.port = port

        self.chunk = 1024 * 4
        self.device_index = self._find_device_index(device_name)

        self.rec_loop = False
        self.q = queue.Queue()

    def _rec_thread(self):

        audio = pyaudio.PyAudio()
        stream = audio.open(
                format = pyaudio.paFloat32,
                channels = 1,
                rate = 44100,
                input = True,
                frames_per_buffer = self.chunk,
                input_device_index=self.device_index)

        while self.rec_loop:
            audio_data = stream.read(self.chunk)
            self.q.put(audio_data)

        stream.close()
        audio.terminate()


    async def _handler(self, websocket, path):
        if self.rec_loop:
            print("_rec_thread is exists.")
            return

        # Audio INから読み出すスレッドを起動
        self.rec_loop = True
        send_thread = threading.Thread(target=self._rec_thread)
        send_thread.start()

        # スレッドで読み込んだデータを送信する
        send_loop = True
        while send_loop:
            try:
                send_data = self.q.get(timeout=1)
                await websocket.send(send_data)
            except:
                print('audio send Error.')
                send_loop = False
                break

        self.rec_loop = False
        send_thread.join()

    def run(self):
        self._server = websockets.serve(self._handler, self.address, self.port)
        self.loop.run_until_complete(self._server)
        self.loop.run_forever()

    @staticmethod
    def get_devices():
        '''使用可能なデバイスを列挙'''
        device_name_list = []
        audio = pyaudio.PyAudio()
        for i in range(audio.get_device_count()):
            device = audio.get_device_info_by_index(i)
            device_name_list.append(device['name'])

        return device_name_list


    @staticmethod
    def _find_device_index(find_name):
        '''引数のデバイス名に対応するindexを返す'''
        device_index = -1
        audio = pyaudio.PyAudio()
        for i in range(audio.get_device_count()):
            device = audio.get_device_info_by_index(i)
            if device['name'] == find_name:
                device_index = i
                break

        return device_index

if __name__ == '__main__':
    device_name = None
    devices = AudioServer.get_devices()

    print("devices len = {}".format(len(devices)))
    if len(devices) > 0:
        device_name = devices[0]
        print("device_name = {}".format(device_name))

    loop = asyncio.get_event_loop()
    ws_as = AudioServer(loop, '0.0.0.0', 60003, device_name)
    ws_as.run()

httpサーバー

まとめて起動する仕組みも用意しましたが終了時のことは考慮していないので、ちゃんと実装する人は他の方法がいいと思います。

以下のスクリプトを実行して http://localhost:60001/ にアクセスすれば動作確認できます。

monitor_run.py
import threading
import http.server
import socketserver
import asyncio
from ImageServer import ImageServer
from AudioServer import AudioServer


def _http_thread():
    _handler = http.server.SimpleHTTPRequestHandler
    httpd = socketserver.TCPServer(("", 60001), _handler)
    httpd.serve_forever()


def _image_thread():
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)

    ws_is = ImageServer(loop, '0.0.0.0', 60002)
    ws_is.run()


def _audio_thread():
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)

    device_name = None
    devices = AudioServer.get_devices()

    print("devices len = {}".format(len(devices)))
    if len(devices) > 0:
        device_name = devices[0]
        print("device_name = {}".format(device_name))

    loop = asyncio.get_event_loop()
    ws_as = AudioServer(loop, '0.0.0.0', 60003, device_name)
    ws_as.run()


if __name__ == '__main__':
    imageThread = threading.Thread(target=_image_thread)
    imageThread.start()

    audioThread = threading.Thread(target=_audio_thread)
    audioThread.start()

    httpThread = threading.Thread(target=_http_thread)
    httpThread.start()


    imageThread.join()
    audioThread.join()
    httpThread.join()

おわりに

WebAudio+pyaudioの組み合わせをやってる人がいなくて試行錯誤でした。

dB値の計算は昔やったことあるけど細かいこと忘れた。正しいdB値が欲しいわけじゃないから適当で。

激しく寝返りするようになった娘のベビーモニターとして利用するつもりですが、暗い部屋だと映らないので赤外線カメラに変更するか、USBライトの点灯制御するか悩むところ。
100均のUSBライトの点灯制御ができたら更新記事をアップしようと思います。

offshot.jpg

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Javascript】第一級関数と高階関数、関数を作る関数

概要

第一級関数・・・変数や配列(第一級オブジェクト)と同じように扱える関数
高階関数・・・第一級関数を引数として取る関数のこと

Javascriptは第一級関数な言語で使いやすいので以下Javascriptでやっていきます。
第一級関数な言語は以下に載ってます。
https://ja.wikipedia.org/wiki/第一級関数

例:マップ関数

function add2(n) {
  return n + 2;
}

const arr = [1,2,3];
const result = arr.map(add2);

console.log(arr);
console.log(result);
// > [1, 2, 3]
// > [3, 4, 5]

関数を作る関数

第一級関数と高階関数を使うことができれば、同じ処理を関数でまとめてしまうかのようにダブっている部分をまとめることができます。
例えば2つの配列の各要素を四則演算する関数は四則演算子以外すべて同じです。

function addArray(arrA, arrB) {
  return arrA.map(function(_, i) {
    return arrA[i] + arrB[i];
  });
}

function mulArray(arrA, arrB) {
  return arrA.map(function(_, i) {
    return arrA[i] * arrB[i];
  });
}

function subArray(arrA, arrB) {
  return arrA.map(function(_, i) {
    return arrA[i] - arrB[i];
  });
}

function subArray(arrA, arrB) {
  return arrA.map(function(_, i) {
    return arrA[i] / arrB[i];
  });
}

ダブっているところを高階関数でまとめてしまいましょう。

(残念なことにJavascriptでは演算子は第一級オブジェクトでは無いので関数で改めて定義してあります。)

function add(a, b) { return a + b; }
function mul(a, b) { return a * b; }
function sub(a, b) { return a - b; }
function div(a, b) { return a / b; }

function makeFunction(fn) {
  return function(arrA, arrB) {
    return arrA.map(function(_, i) {
      return fn(arrA[i], arrB[i]);
    });
  }
}

var addArray = makeFunction(add);
var mulArray = makeFunction(mul);
var subArray = makeFunction(sub);
var divArray = makeFunction(div);

makeFuncitonは関数(この場合は四則演算関数)を引数に取り、返り値として関数を返えす関数です。
内容は上のもののダブっていたとこはそのままに四則演算部分を関数で置き換えた関数を返しています。

最後に

この手の関数の利用はLisp(commonlispやscheme)で学ばせてもらいました。あちらにはもっと自由で抽象的なマクロもあるので関数型プログラミング言語をやってみたい方はぜひLispを使ってみてください。Lispの宣伝でした。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.jsでタスクキューっぽいものを実現する

はじめに

APIへのリクエストをするときに、連続でリクエストを送るようなシーンで連続送信を防止するために現在の処理が実行中の場合は処理を待つみたいなシーンがありました…

let isConnecting = false
let isRetry =false
api: function () {
  if (!isRetry) {
    if (!isConnecting) {
      isConnectiong = true
      axios.get('api')
      .then(function(res) {
        // 成功時の処理
        isConnecting = false
        api()
      })
    } else {
      isRetry = true
    }
  }
}

こうやって書くとネストが深くなって、イケてないですよね
※ 正確にはこれじゃ失敗したときにつみますが

そこで、タスクキュー的なもので処理を予約するみたいなことをやりたいと思いました。

JavaScriptのタスクキュー

いいライブラリが殆どない…

ということで、自分で頑張りました

やってみた

Vue.jsで書いていたので、ここではvue.jsで書いてます。
通常のjavascriptでやるには変数のwatch機能とかを頑張れば同じやり方でできると思います。

やりたいこと

  1. APIへリクエストを送る
  2. APIへのリクエストが完了していない場合は、タスクキューにためて、前の処理が終わってから実行する
  3. 貯める処理は2つまでで、最終待機しているタスクは最新の状態にしたい

コード

new Vue({
  el: "#app",
  data: function() {
    return {
      exQueue: []   // タスクを貯めておく用の配列
    }
  },
  watch: {
    // タスクを貯める配列の値に変化があったら呼ばれる関数
    exQueue: function(queue) { /*③*/
      // タスクが一つのときしか実行されないので、現在実行中の場合は無視される
      if (queue.length === 1) {
        queue[0].func(queue[0].params)
      }
    }
  },
  methods: {
    main: function () { /*①*/
      this.addQueue({ func: this.callAPI, params: params }) 
    },
    // タスクを追加する関数
    addQueue: function (job) {  /*②*/
      // 3の条件を満たすために貯めれるタスクは2つまで
      if (this.exQueue.length < 2) {
        this.exQueue.push(job)
      } else {
        // 2つを超えるときは後ろを最新のものに上書きする
        this.exQueue.pop()
        this.exQueue.push(job)
      }
    },
    // APIを呼び出す関数
    callAPI: function(params) { /*④*/
      axios.get('api')
      .then(function(res) {
        // 完了したタスク(配列の先頭を削除する)
        this.exQueue.shift()
      })
      .catch((error => {
        console.error('APIからの取得失敗')
      }))
    }
  }
})

解説

  • ① → ② → ③ → ④ の順番に実行されていく
  • ④ で exQueue が更新されるため、 再び ③ が実行される
  • タスクがある場合は再度実行
  • 残タスクがあるうちは繰り返し

といった形で、タスクキューを割と簡単に実装することができます。

さいごに

簡単にVue.jsでタスクキューを実装してみました。
この辺の処理効率化みたいな部分て、ゴリゴリのサーバーサイドよりなコアな言語とかでよく繰り広げられることを見るのですが、JavaScriptだと以外にないなと思い書いてて楽しかったです。※そもそも言語の思想と違う感じはしていますが
とはいえ、if文のネストをしてしまうと、視認性も悪くなったり、ストックしたいタスクを増やすってなると改修が大変になるってことを考えると配列を見てタスクキューだって分かるようになりだいぶシンプルにかけると思います。APIの通信とか非同期の処理をするときに参考になればと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React Native + React Navigation で、 Android の画面遷移アニメーションを iOS と同様にする

React Navigation の画面遷移のアニメーション、 Android だけよくわからない。

デフォルトだと、モーダルが重なるようになるが、 iOS のように左から右に進んでいくようにしたい。

めっちゃ簡単にできた。

import {
  createAppContainer,
  createStackNavigator,
  StackViewTransitionConfigs,
} from 'react-navigation'

const Navigation = createStackNavigator({
  screenA: ComponentA,
  screenB: ComponentB,
}, {
  mode: 'card',
  transitionConfig: () => StackViewTransitionConfigs.SlideFromRightIOS,
}

export const AppNavigation = createAppContainer(Navigation)

なんでこれがデフォルトじゃないんだ・・・笑

https://stackoverflow.com/questions/48018666/how-to-change-the-direction-of-the-animation-in-stacknavigator

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javascriptを用いたランダムなクジ

はじめに

未来電子テクノロジーでインターンをしている<小栗>です。
「プログラミング初心者であるため、内容に誤りがあるかもしれません。
もし、誤りがあれば修正するのでどんどん指摘してください。

Javascriptを用いたランダムなクジ

'use strict';

{
  const btn = document.getElementById('btn');

  btn.addEventListener('click', () => {

    const n = Math.random();
    if (n < 0.15) {
      btn.textContent = '$100'; 
    } else if (n < 0.35) {
      btn.textContent = '$20'; 
    } else {
      btn.textContent = '$1'; 
    }
  });
  btn.addEventListener('mousedown', () => {
    btn.classList.add('pressed');
  });
  btn.addEventListener('mouseup', () => {
    btn.classList.remove('pressed');
  });
}

if文のnの数値を変えれば、結果の割合を変えることができます。
自分は$100が15%、$20が35%、$1が残りの50%で出るようになっています。

一応自分のHTMLも記しておきます。

<div class='lottery'>
        <div id='btn'>Lottery</div>
        <script src="js/main.js"></script>
</div>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

micro:bitでLEDと温度センサーを試してみた

マルツ電波でmicro:bitのスターターキットが安売りしていたので購入。
地元のお祭りも終わったのでちょっといじってみた。

ブロックエディタ、JavaScript、Pythonで開発できるとのこと。
Windows10なので、ストアからMakeCode for micro:bitをダウンロード。

エディタはブロックとJavaScriptと切り替え可能。
JavaScriptでコードを書いてブロックに切り替える、その逆も可能。
ただし、JavaScript→ブロック→JavaScriptと切り替えると、コードがブロックから再生成しているのか、整形しなおしているのか、元に戻りませんね。

とりあえず、こんかいはLEDと温度センサーを使って、温度を簡易的に表示してみました。
エディタが良くできていて、パレットというのか基本や入力などを選ぶと利用できるメソッドなどが選べるのがよい。

で、書いたコードは以下の通り。
大したことはしていませんが、input.temperature()で取得した温度を文字列に変えて、1文字づつ表示しています。

let leds: number[][][] = []
let tmp = 0
let num = 0
basic.forever(function () {
    leds = [
        [
              [0, 0, 1, 0, 0]
            , [0, 1, 0, 1, 0]
            , [0, 1, 0, 1, 0]
            , [0, 1, 0, 1, 0]
            , [0, 0, 1, 0, 0]
        ], [
              [0, 0, 1, 0, 0]
            , [0, 1, 1, 0, 0]
            , [0, 0, 1, 0, 0]
            , [0, 0, 1, 0, 0]
            , [0, 0, 1, 0, 0]
        ], [
              [0, 1, 1, 1, 0]
            , [0, 0, 0, 1, 0]
            , [0, 0, 1, 1, 0]
            , [0, 1, 0, 0, 0]
            , [0, 1, 1, 1, 0]
        ], [
              [0, 1, 1, 1, 0]
            , [0, 0, 0, 1, 0]
            , [0, 0, 1, 1, 0]
            , [0, 0, 0, 1, 0]
            , [0, 1, 1, 1, 0]
        ], [
              [1, 0, 0, 0, 0]
            , [1, 0, 1, 0, 0]
            , [1, 0, 1, 0, 0]
            , [1, 1, 1, 1, 0]
            , [0, 0, 1, 0, 0]
        ], [
              [0, 1, 1, 1, 0]
            , [0, 1, 0, 0, 0]
            , [0, 1, 1, 0, 0]
            , [0, 0, 0, 1, 0]
            , [0, 1, 1, 0, 0]
        ], [
              [0, 0, 1, 1, 0]
            , [0, 1, 0, 0, 0]
            , [0, 1, 1, 0, 0]
            , [0, 1, 0, 1, 0]
            , [0, 1, 1, 0, 0]
        ], [
              [0, 1, 1, 1, 0]
            , [0, 0, 0, 1, 0]
            , [0, 0, 1, 0, 0]
            , [0, 0, 1, 0, 0]
            , [0, 0, 1, 0, 0]
        ], [
              [0, 0, 1, 0, 0]
            , [0, 1, 0, 1, 0]
            , [0, 1, 1, 1, 0]
            , [0, 1, 0, 1, 0]
            , [0, 0, 1, 0, 0]
        ], [
              [0, 1, 1, 1, 0]
            , [0, 1, 0, 1, 0]
            , [0, 1, 1, 1, 0]
            , [0, 0, 0, 1, 0]
            , [0, 1, 1, 1, 0]
        ]
    ]
    let cnt;
    cnt = 0
    while (true) {
        tmp = input.temperature()
        let strTmp = tmp.toString()
        console.log(strTmp)
        num = parseInt(strTmp.charAt(cnt))
        for (let y = 0; y <= 5 - 1; y++) {
            for (let x = 0; x <= 5 - 1; x++) {
                if (leds[num][y][x] == 0) {
                    led.plotBrightness(x, y, 0)
                } else {
                    led.plotBrightness(x, y, 255)
                }
            }
        }
        cnt += 1
        if (cnt >= strTmp.length) {
            cnt = 0
        }
        basic.pause(200)
    }
})
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】重複のない乱数配列を作ってみた【ES6(ES2015)】

0.前提

 「俺はJavaScriptで重複なし乱数配列(整数)が作りたいんや!」ってことでJavaScriptで書いてますが、アルゴリズム自体は多言語に応用できます。尚、アルゴリズムそのものは筆者自身がゼロから考えました。もし他の方の考えたアルゴリズムと同じだったとしてもごめんなさい。

1.はじめに

 ES6(ES2015)に対応した環境でないと動作しません。筆者は、Repl.itのJavaScript環境(Node.js v10.16.0)にて動作を確認しています。最新のメジャーブラウザでは動作しますが、IE11以前では動作しません。
 また、筆者が以前書いた記事JavaScript基礎文法を軸にコード書いているので、初心者の方は先にそちらに目を通すこと推奨。

2.アルゴリズム

 『アルゴリズムたいそう~♪』テッテテッテテッテテッテテー チャン♪
 『こっちむいて一人で・・・ってなんでやね~ん』
 はい、これ以上やると叩かれそうなのでやめておきます。
 先にアルゴリズムを説明しておくことにしましょう。コードだけ欲しい人は読み飛ばしてください。目標とするのは、重複なしの乱数配列、例えば1~5の乱数配列ならこんな感じ。

添字 0 1 2 3 4
要素 3 4 5 2 1

 こういう乱数が入った配列を作っていきます。いつ使うのかって?
 ちょっと話が外れるので最後に書くね(この記事を読むってことはそれが必要な場面があるでしょうから・・・)。
 →初めから配列をシャッフルすることを目的にしていたのですが、ここで考えたアルゴリズム自体が「配列をシャッフルする」ものだったので別で記事を書きました:配列をシャッフルするアルゴリズムを思いついたが既存だった話【JavaScript/Rubyサンプルコードあり】

 まずはじめに、候補となる配列を作るよ。

initAry:

添字 0 1 2 3 4
要素 1 2 3 4 5

 0~4(initAryの添字の最大値)の乱数を発生させて、initAryからその乱数の位置にある要素を一つ取り出し新しい配列aryに入れるよ。

randomNumber:3
取り出す要素はinitAry[3] = 4
initAry:

添字 0 1 2 3
要素 1 2 3 5

ary:

添字 0
要素 4

 これを、initAryが空になるまで繰り返すよ。

randomNumber:2
取り出す要素はinitAry[2] = 3
initAry:

添字 0 1 2
要素 1 2 5

ary:

添字 0 1
要素 4 3

 ・・・(略)・・・

randomNumber:0
取り出す要素はinitAry[0] = 2
initAry:

添字
要素

ary:

添字 0 1 2 3 4
要素 4 3 5 1 2

 おしまい。

3.実装

 さて、実際にこれを実装していこう。今回、class宣言を利用している。先に全貌を提示する。

RandomNumber.js
class RandomNumber{
  constructor(num, min = 0){
    this.num = num;
    this.min = min;
    this.init();
  }

  /*
   * getRandomIntメソッド
   * 処理:min以上max未満の乱数を返す。 
   * 引数
   *  min:最小値(整数)
   *  max:最大値(整数)
   * 返り値:生成された乱数
   */
  getRandomInt(min, max){
    min = Math.ceil(min);
    max = Math.floor(max);
    return Math.floor(Math.random() * (max - min)) + min;
  }

  /* 
   * initメソッド
   * 処理:minを最小とするnum個の被りなしの乱数が入った配列this.aryを生成。
   * 引数
   *  num:生成個数(整数)、初期値はthis.num
   *  min:最小値(整数)、初期値はthis.min
   * 返り値:なし
   */
  init(num = this.num, min = this.min){
    this.index = 0;
    this.ary = [];
    let initAry = [];

    for(let i = 0; i < num; i++){
      initAry.push(min + i);
    }

    while(initAry.length > 0){
      this.ary.push(initAry.splice(this.getRandomInt(0,initAry.length), 1).pop());
    }
  }

  /* 
   * nextメソッド
   * 処理:実行するたびに、this.aryの要素を順に返す。
   *    配列の最後までいったときははじめの要素に戻る。
   * 引数:なし
   * 返り値:this.index位置のthis.aryの要素
   */
  next(){
    if(this.index >= this.ary.length){
      this.index = 0;
    }
    return this.ary[this.index++];
  }
}

 初期化も行いたいため、initメソッドをconstructorと別に定義している。これによって、例えばinit()とすると乱数配列を再生成できる。

constructor

処理
numとminをそれぞれ同名のクラスメンバにする。
その後、initメソッドを引数無しで実行する。
引数
num:生成する乱数の個数
min:生成する乱数の最小値、初期値は0
  constructor(num, min = 0){
    this.num = num;
    this.min = min;
    this.init();
  }

 constructor(num, min = 0)のように書いてminの初期値を0にすることで、第2引数を指定せずにクラス生成すると最小値0num個の整数乱数配列が生成されます。生成過程は後述のinitメソッドで。

getRandomIntメソッド

処理
min以上max未満の乱数を返す。
引数
min:最小値(整数)
max:最大値(整数)
返り値
生成された乱数
  getRandomInt(min, max){
    min = Math.ceil(min);
    max = Math.floor(max);
    return Math.floor(Math.random() * (max - min)) + min;
  }

コードはMath.random() - JavaScript | MDNのサンプルコードを転用したもの。
MDNのライセンスに従い掲載。

initメソッド

処理
minを最小とするnum個の重複なしの乱数が入った配列this.aryを生成。
引数
num:生成個数(整数)、初期値はthis.num
min:最小値(整数)、初期値はthis.min
返り値
なし
  init(num = this.num, min = this.min){
    this.index = 0;
    this.ary = [];
    let initAry = [];

    for(let i = 0; i < num; i++){
      initAry.push(min + i);
    }

    while(initAry.length > 0){
      this.ary.push(initAry.splice(this.getRandomInt(0,initAry.length), 1).pop());
    }
  }

引数省略時、this.minを最小とするthis.num個の整数乱数配列を生成。

this.index = 0;:nextメソッドで使うものなので後述。

this.ary = [];let initAry = [];:この2つが配列であることを宣言

for文:候補となる配列initAryを生成
 initAry.push(min + i);で最小値+iの値をinitAryに入れていくことで生成している

while文:乱数配列this.aryを生成
 initAry.splice(this.getRandomInt(0,initAry.length), 1).pop()initAryの要素からランダムで一つ取り出している。
 最後に.pop()をつけているのは、Arrayクラスのspliceメソッドは取り出す要素が一つでも要素数1の配列を返してしまうためである。
 initAry.splice(this.getRandomInt(0,initAry.length), 1)とすると、this.aryの中身が「要素数1の配列」をnum個格納した配列になってしまう。

nextメソッド

処理
実行するたびに、this.aryの要素を順に返す。配列の最後までいったときははじめの要素に戻る。
引数
なし
返り値
this.index位置のthis.aryの要素
  next(){
    if(this.index >= this.ary.length){
      this.index = 0;
    }
    return this.ary[this.index++];
  }

なにかに使えそうと思って作成したメソッド。forEachなどのループを使うことなく、生成された重複なしの整数乱数配列の中身を順に取り出すことができる。

4.使用例

const r = new RandomNumber(5,1);
console.log(r.ary);
r.init();
console.log(r.ary);
for(let i= 0; i < r.ary.length*2; i++){
  console.log(r.next());
}

実行結果

[ 4, 1, 3, 5, 2 ]
[ 5, 3, 2, 4, 1 ]
5
3
2
4
1
5
3
2
4
1

5.参考文献

(初心者向け) JavaScript のクラス (ES6 対応) - Qiita
JavaScriptのclass - Qiita
Math.random() - JavaScript | MDN
Array.prototype.splice() - JavaScript | MDN

6.さいごに

 重複なしの乱数配列を作って配列をシャッフルしようとアルゴリズム考えたら最終的に配列をシャッフルするアルゴリズムになってた。なんだこれ。
 ちなみに当初考えていたものは、乱数作って配列に入れて、その全要素と被らない乱数が生成されるまで繰り返すものでした。処理時間めっちゃ長い。
 class使う必要あるの?って思った人はわざわざclass使う必要ないです。でも、どうやらモジュール化するならclassのほうがいい?
 今の所、生のJavaScriptとjQueryをHTML、CSSと併用してしか使ってないので詳しくは知りませんが。
 ではまた。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptのエキサイティングな新機能7選

以下はMostafa Gaafarによる記事、7 New Exciting JavaScript Features You Need to Knowの日本語訳です。

7 New Exciting JavaScript Features You Need to Know

JavaScript ( ECMA Script ) は進化する言語であり、たくさんのproposalやアイデアが出番を待ち受けています。
TC39 (Technical Committee 39) という委員会がJavaScript標準と新機能の定義を担当しています。
そして今年は彼らの活動が活発になっています。
以下は、現在ステージ3にある提案の一部の紹介です。
ステージ3は完成する直前の段階です。
これはつまり、この機能がブラウザやその他のJavaScriptエンジンにすぐに実装されることを表しています。
実際、以下のいくつかは既にブラウザに実装されています。

1. Private fields

ChromeとNodeJS12に実装済。

はい、そうです。
JavaScriptはようやくprivateを手に入れました。
もうprivateプロパティを作るためにthis._doPrivateStuff()したりクロージャに閉じ込めたりWeakMapを使ったりといった小細工を弄する必要はありません。

01.jpg

構文は以下のようになります。

// private修飾子は`#`
class Counter {
  #x = 0;

  #increment() {
    this.#x++;
  }

  onClick() {
    this.#increment();
  }

}

const c = new Counter();
c.onClick(); // OK
c.#increment(); // エラー

Proposal:https://github.com/tc39/proposal-class-fields

2. Optional Chaining ?.

オブジェクト内のネストの奥深くにあるプロパティにアクセスしようとして、悪名高いCannot read property 'stop' of undefinedエラーに遭遇した人は多いでしょう。
そうなったら次は、チェーン内の未定義オブジェクトに対応できるようにコードを変更します。

const stop = please && please.make && please.make.it && please.make.it.stop;

// あるいは'object-path'みたいなライブラリを使う
const stop = objectPath.get(please, "make.it.stop");

Optional Chainingがやってくると、同じことを簡単に書けるようになります。

const stop = please?.make?.it?.stop;

Proposal:https://github.com/tc39/proposal-optional-chaining

3. Nullish Coalescing ??

オプション値が存在しない場合にデフォルト値を使用するという実装はとても一般的です。

const duration = input.duration || 500;

この書き方の問題点は、||を使うと、有効な入力値である可能性のある0や''、falseといったfalseっぽい値が全て500になってしまうことです。

そんなわけでundefinedおよびnullのみを識別するNull合体演算子を新設します。

const duration = input.duration ?? 500;

Proposal:https://github.com/tc39/proposal-nullish-coalescing

4. BigInt 1n

ChromeとNodeJS12に実装済。

JavaScriptの数値演算がひどいものだった理由のひとつは、2^53を超える数値を扱うことができないということです。
幸いなことに、BigIntがこの問題を解決します。

02.jpg

理屈抜きで使い方を示すとこう。

// 数値リテラルにnを付けるとBigIntになる
const theBiggestInt = 9007199254740991n;

// constructorでもよい
const alsoHuge = BigInt(9007199254740991);

// constructorには文字列も渡せる
const hugeButString = BigInt('9007199254740991');

BigIntには通常の数値と同じ演算子、+-/*%などを使うことができます。
ただし、ほとんどの操作ではBigIntと通常の数値リテラルを混ぜることはできません。
BigIntとNumberを比較することはできますが、加算はできません。

1n < 2 // true

1n + 2 // Uncaught TypeError: Cannot mix BigInt and other types

Proposal:https://github.com/tc39/proposal-bigint

5. static Fields

ChromeとNodeJS12に実装済。

これはとても簡単です。
多くのOOP言語同様、クラスにstaticフィールドを設定できます。
これは列挙型の代替にも使用可能で、privateとも共存可能です。

class Colors {
  // public static
  static red = '#ff0000';
  static green = '#00ff00';

  // private static
  static #secretColor = '#f0f0f0';
}

font.color = Colors.red;
font.color = Colors.#secretColor; // Error

Proposal:https://github.com/tc39/proposal-static-class-features

6. Top Level await

Chromeに実装済。

コードのトップレベルでawaitを使用できます。
いちいち非同期関数でラップせずに済むようになるので、コンソール上でfetchのような非同期処理をデバッグするときなど非常に便利です。

03.jpg

Async/Awaitの復習が必要なら、私の過去記事を参照ください。

もうひとつの便利な使用例は、非同期で初期化が行われるESモジュールをトップレベルで使えるようになることです。
たとえば接続を確立する必要があるデータベースなどです。
このような非同期モジュールをawaitでインポートすると、そのモジュールは初期化が終わるまで待機し、その後そのモジュールに依存する次のモジュールを実行します。
これによって、返ってきたPromiseが解決されるまで待つという現在のスタンダードよりも、ずっと簡潔に非同期を処理することができます。
モジュールは、依存先モジュールが非同期なのか同期なのかを知る必要はありません。

db.mjs
export const connection = await createConnection();
server.mjs
import { connection } from './db.mjs';
server.start();

この例では、db.mjsの処理が完了するまでserver.mjsは待機します。

Proposal:https://github.com/tc39/proposal-top-level-await

7. WeakRef

ChromeとNodeJS12に実装済。

弱い参照は、いつまでも存続しているとはかぎらない参照です。

const、let、varで生成した変数は、その変数がアクセス可能であるかぎり決してメモリから削除されることはありません。
これらは全て強い参照です。
弱い参照で生成されたオブジェクトは、GCによって削除される可能性があります。
WeakRefインスタンスには、参照された元オブジェクトを返すメソッドderefがあります。
元オブジェクトが削除されていたらundefinedになります。

これは、メモリにずっと保存しておきたい程ではない重要性の低いオブジェクトをキャッシュしておきたいときに役立ちます。

const cache = new Map();

const setValue =  (key, obj) => {
  cache.set(key, new WeakRef(obj));
};

const getValue = (key) => {
  const ref = cache.get(key);
  if (ref) {
    return ref.deref();
  }
};

// キャッシュにあればそれを返す、なければ再計算する
const fibonacciCached = (number) => {
  const cached = getValue(number);
  if (cached) return cached;
  const sum = calculateFibonacci(number);
  setValue(number, sum);
  return sum;
};

不規則に削除される可能性があるため、リモートデータなどをキャッシュするために使うのは良い考えではありません。
それらにはLRUキャッシュなどを使った方がよいでしょう。

Proposal:https://github.com/tc39/proposal-weakrefs

これらのクールな新機能を使ってみて、私と同じように興奮してください。
また、ここで取り上げなかった他のプロポーザルもTC39のGitHubに公開されています。

コメント欄

「privateの#は頭おかしい」「完全に同意」「唖然とするわ」「privateキーワード追加しろよ」「いやまあ、色々と事情があるんだよ、きっと」
「Null合体演算子が楽しみ。Optional ChainingはKyle Simpsonから聞いただけだけど便利そう。」
「BigInt大歓迎。」
「弱参照知らなかった。うまく使えばWebアプリのパフォーマンスを大幅上昇できるかも。」
「TS使ってる身としてはそう目新しいものでもない。だが良いことだ。」
「Chromeで使えるのいいね。」

感想

初見のときから思ってたんだけど、privateアクセス修飾子の#はあまりに気持ち悪いですね。
staticはキーワードとして導入したのに、どうしてprivateは入れなかったのでしょうか。

もちろん理由はproposalに書かれてるのですが、主な理由としては『privateフィールドが存在していることすら外部に出したくない』だそうです。
いや、そこまでする必要あるのか?
PHPどころかJavaですら『privateフィールドが存在する』こと自体は外部からわかるぞ。

そんなわけでprivateフィールド#aのあるクラスに外からa=1とすると、privateフィールド#aとは別のpublicフィールドaが生えます。

class A{
  #a = 0;
}

a = new A();
a.a = 1; // エラー出ない

privateフィールドというより、外部と完全に切り離された内部変数みたいなかんじですね。
まあ、このあたりの仕様や文法を決めるにあたっては何年もの議論があったらしいので、後からちょっと覗いた程度では窺い知れぬ何か深い事情があるのでしょう。きっと。

結果としてJavaScriptのprivateフィールドは、リフレクションのような手段をもってしてもアクセスできない、それどころか存在するかどうかすら検出できないという、非常に本気のステルス性を手に入れました。
でもChromeのコンソールとかでは普通に見えるんですけどね。

BigIntはNumberと混ぜられないのが面倒ですね。
GMPを見習ってどうぞ。

PHP
$n = gmp_init('9007199254740991');
var_dump($n+1); // GMP(9007199254740992)

PHPの場合、GMPオブジェクトに数値演算すると結果もGMPオブジェクトとなり、透過的に計算できるので便利です。
どうしてこうしなかったのでしょうか?

とまあ少々疑問に感じる点もないでもないものの、いずれの機能も、導入されれば役に立つであろうことは間違いないはずです。
また、proposalでは、他にも多くの新機能や構文が自分の登場する順番を待っています。
色々見てみると楽しいかもしれません。

個人的には追加よりむしろ末尾;補完を削除してほしいのですが、これはどう考えても無理だろうな。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ES6 class 構文のパフォーマンスについて

概要

この記事は「class 構文」と「クロージャによるカプセル化」のパフォーマンス比較記事です。
検証ブラウザーは Chrome のみになります。

何か1つの処理を作るのに態々 class 化しなくても、クロージャ(関数)を作成しカプセル化するなどの方法もある。しかしパフォーマンスの観点で考えると、どちらで実装していくのが良いのだろうか...という疑問があった。本記事では、その検証結果をまとめる。
検証前に「class 構文」と「クロージャによるカプセル化」に対する考えを述べる。

class 構文に対する考え(検証前)

class 構文は実質 prototype の糖衣構文なので、同じようなオブジェクトを幾つも生成する場合は class の方が良さそうではある。しかし、class から instance(オブジェクト)を生成する new はそこそこのコストを孕んでいそうなので、オブジェクトの生成数が1〜2つ程度なら態々 class 構文で書かなくても良さそう。
また、プライベートなプロパティーを作成できない点が気に掛かる。

class構文の例
class Sample {
    constructor(val) {
        this.value = val; // value には外部からもアクセスできる
    }
    getValue() {
        return this.value.toUpperCase();
    }
}
const test = new Sample('hello world');
test.getValue(); // HELLO WORLD
test.value // hello world(アクセスできちゃう)

クロージャによるカプセル化に対する考え(検証前)

class で実現できないプロパティーのプライベート化(外部からアクセス不可)を実現することができる。class と異なり、実行の都度毎回新しいオブジェクト(prototypeではない)を返すので、オブジェクトの生成数が多ければ多いほどメモリの消費が気に掛かる。

クロージャによるカプセル化の例
const sample = (val) => {
    const value = val; // value には外部から直接アクセスできない
    return {
        getValue() {
            return value.toUpperCase();
        }
    };
};
const test = sample('hello world');
test.getValue(); // HELLO WORLD

パフォーマンス検証

「class 構文」と「クロージャによるカプセル化」の2通りについて同一の処理を用意し、「メモリの使用量」と「オブジェクト生成速度」を比較する。なお、下記コードが比較対象のコードである。

比較コード(class構文)
class Shape {
    constructor(option) {
        this.shape = document.createElement('div');
        this._text = document.createTextNode(option.text);
        this._width = option.width;
        this._height = option.height;
        this._backgroundColor = option.backgroundColor;
        this.parent = option.parent;
    }
    add() {
        this.width().height().backgroundColor();
        this.shape.appendChild(this._text);
        this.parent.appendChild(this.shape);
    }
    text(t) {
        this._text.nodeValue = t;
        return this;
    }
    width(w = this._width) {
        if (w !== this._width) this._width = w;
        this.shape.style.width = `${this._width}px`;
        return this;
    }
    ...略(コードが長いため)
}
比較コード(クロージャによるカプセル化)
const shape = (opiton) => {
    const el = document.createElement('div');
    const _text = document.createTextNode(option.text);
    const _width = option.width;
    const _height = option.height;
    const _backgroundColor = option.backgroundColor;
    const parent = option.parent;
    return {
        add() {
            this.width().height().backgroundColor();
            el.appendChild(_text);
            parent.appendChild(el);
        },
        text(t) {
            _text.nodeValue = t;
            return this;
        },
        width(w = _width) {
            if (w !== _width) _width = w;
            el.style.width = `${_width}px`;
            return this;
        },
        ...略(コードが長いため)
    }
};

メモリの使用量とオブジェクト生成速度の計測

class Shape と関数 shape で生成したオブジェクトを savingVariable に for 文で任意数追加し終わる速度と、その時のメモリ使用量を計測する。
メモリの使用量に関しては Chrome の「タスクマネージャ」を利用して「JavaScript メモリ」の値を計測する(タスクマネージャによるメモリ使用量のリアルタイム監視)。

  • 計測は片方ずつ行う。片方計測時はもう片方はコメントアウトしておく。
  • 10 回ループと 100000 回ループで数値を取る
検証コード
// 作成したオブジェクトを保存する(メモリ使用量の計測用)
window.savingVariable = [];

console.time("no new");
for (let i = 0; i < 100000; i++) {
    savingVariable.push(shape(option));
}
console.timeEnd("no new");

console.time("new");
for (let i = 0; i < 100000; i++) {
    savingVariable.push(new Shape(option));
}
console.timeEnd("new");

オブジェクト生成速度のみの計測

「メモリの使用量とオブジェクト生成速度の計測」では、メモリの圧迫による速度低下が考えられるので、こちらでは毎回作成したオブジェクトを永続的に参照可能な変数には保存しないようする(GC(メモリ解放)させるようにする)。
※ なお、メモリの圧迫による速度低下は 10回ループ程度場合では考えられないため、こちらでは 100000 回ループのみ計測。

  • 計測は片方ずつ行う。片方計測時はもう片方はコメントアウトしておく。
  • 100000 回ループで数値を取る。
検証コード
console.time("no new");
for (let i = 0; i < 100000; i++) {
    const box = shape(option);
}
console.timeEnd("no new");

console.time("new");
for (let i = 0; i < 100000; i++) {
    const box = new Shape(option);
}
console.timeEnd("new");

検証結果

検証の結果は下記のようになった。

メモリの使用量とオブジェクト生成速度の計測(結果)

コード ループ回数 メモリ使用量 速度(3回計測)
関数 shape 10 4.594MB 0.198ms, 0.171ms, 0.175ms
class Shape 10 4.590MB 0.148ms, 0.172ms, 0.161ms
関数 shape 100000 62.518MB 288.227ms, 232.893ms, 244.277ms
class Shape 100000 20.369MB 107.515ms, 93.580ms, 131.524ms

class Shape は関数 shape に比べて圧倒的にメモリ使用量が少ない。大体予測は付いていたが、実際数値で見ると prototype の使用は圧倒的に効率がいいなと再認識させられた。
また、速度についてもメモリの圧迫に引っ張られてなのか class Shape の方が圧倒的に早い。
10 回ループについては、大した差は出なかった。

オブジェクト生成速度のみの計測(結果)

コード ループ回数 速度(3回計測)
関数 shape 100000 86.072ms, 83.144ms, 92.013ms
class Shape 100000 79.072ms, 77.062ms, 85.856ms

やはり、メモリ使用量の計測と同時に行った結果については速度に差が出たが、純粋にオブジェクト生成速度の計測のみを行うコチラでは大きく速度の差は開かなかった。
その結果、new による instance の生成はコストというほどパフォーマンスに影響を与えるものではない事が分かった。

追加・補足検証

一番最初の例(概要の章)で出した class Sample と関数 sample についても計測してみた。
理由としては、class Shape や関数 shape のように多くの機能を持ったオブジェクトの生成比較とは別に、小さなオブジェクト生成の比較結果を得るため。

追加・補足検証結果

コード ループ回数 メモリ使用量 速度(3回計測)
関数 sample 10 4.713MB 0.198ms, 0.171ms, 0.175ms
class Sample 10 4.713MB 0.148ms, 0.172ms, 0.161ms
関数 sample 100000 22.388MB 7.521ms, 7.259ms, 6.287ms
class Sample 100000 11.488MB 10.986ms, 8.089ms, 14.462ms

class Sample / 関数 sample 自体、メンバ変数 / 関数 の数が少ないので、100000 回 for 文で処理してもあまり差は無かった。メモリ使用量を見るとやはり class Sample の方が少なくはあるが、メモリの圧迫も然程無い為かそれによる速度差は特に見られなかった(誤差の範囲)。その為、class Shape / 関数 shape で行なった速度のみの計測は行わなかった。

結果としてやはりオブジェクトが大きいものであるほど、それが多数生成された際に class 構文による恩恵が大きく見られることが分かった。

パフォーマンス検証を終えて

第一に class 構文優秀じゃん!と思いました。個人的に new ってコストのかかるイメージを固定概念的で持っていたので、それが払拭されたのが1つと、検証結果の数値を見るに prototype (class) を使用することの効率の良さ(無駄にメモリを喰わない)を改めて再認識できた点が大きいです。

プロパティーのプライベート化については現状 class 構文で使用できない(Chrome74以降のみ可)ですが、将来的に実装もされそうですし、この検証結果の元もっと class 構文を積極的に使っていこう!という考えを自分の中で促進することができました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

IntelliJ・WebStrom・PhpStorm等のJetBrains製IDEで、文字列の直前に「language=JSON」と書くと、その文字列にJSONのシンタックスハイライトが効いて便利だった。

IntelliJやWebStrom、PhpStormなどのJetBrains製IDEで、文字列の前に// language=JSONというコメントをつけると、IDEが文字列をJSONとして認識してくれるため、

  • JSONとしてのシンタックスハイライト
  • JSON構文エラーの警告
  • JSONのコード補完
  • コード整形

といった、地の文でJSONを書いたときにIDEがやってくれるような恩恵を享受できるようになる。

language-json.gif

この機能はLanguage Injectionと呼ばれるもの。コメントが書ける大抵の言語なら、JavaでPHPでもJavaScriptでもScalaでも使えるようだ。

この機能はJSONに限ったものでなく、language=SQLlanguage=HTMLなどのlanguage_IDを指定することで他の言語にも対応可能。

// language=<language_ID>

Screenshot_2019_08_21_12_44.png

PHPは@langが使える

PHPではPhpDocの@langでもLanguage Injectionすることができる。

Screenshot_2019_08_21_12_32.png

Javaなどでは@Languageアノテーションが使える

JavaやGroovy、Kotlinではorg.jetbrains:annotationsをMavenなどの依存に追加することで、@Languageアノテーションを使うことができる。Scalaには対応していない様子。

Screenshot_2019_08_21_12_31.png

参考文献

所感

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

2.2 Node JSのモジュールシステムとパターン

2.2.1 公開モジュールパターン

JavaScriptは「ネームスペース」が存在しないので、グローバルな変更が容易です。これを回避するために使うパターンが「Living Module Pattern」です。

test.js
const module = (() => {
  const privateFoo = () => {...}
  const privateBar = () => []

  const exported = {
    publicFoo: () => {...},
    publicBar: () => {...}
  }
})()
console.log(module)

JavaScriptがプライベートなスコープを形成するという性質を利用して、必要なものだけ公開します。

2.2.2 Node JSモジュールシステムの詳細

2.2.2.1 自作のモジュールローダ

test.js
function loadModule(filename, module, require) {
  const wrappedSrc = `(function(module, exports, require) {
    ${fs.readFileSync(filename, 'utf8')}
  })(module, module.exports, require)`
  eval(wrappedSrc)
}

const requireMine = (moduleName) => {
  console.log(`RequireMine invoked for module: ${moduleName}`)
  const id = requireMine.resolve(moduleName)

  if (requireMine.cache[id]) {
    return requireMine.cache[id].exports
  }

  const module = {
    exports: {},
    id: id
  }

  requireMine.cache[id] = module

  loadModule(id, module, requireMine)

  return module.exports
}
requireMine.cache = {}
requireMine.resolve = (moduleName) => {
  // モジュール名を完全な識別子に変換する。
}

※evalとは、引数に指定した文字列をJavaScriptプログラムコードとして評価・実行する機能をもつ関数です。
security holeを作ってしまうので、気をつけてください。

2.2.2.2 モジュールの定義

test.js
const dependency = require('./anotherModule')

function log() {
  console.log('Well done ${dependency.username}')
}

module.exports.run = () => {
  log()
}

module.exportに代入されない限りモジュールないの全ての変数が非公開になります。

2.2.2.3 グローバル変数の定義

globalオブジェクトに定義されたプロパティは、自動的にグローバル変数/関数として参照可能となります。しかし、モジュールシステムのカプセル化の利点を損なうので、一般的には使わないです。

2.2.2.4 module.exportsとexportsの使い分け

単純に新しいプロパティ追加

test.js
exports.hello = () => {
  console.log('Hello')
}

誤ったコード

test.js
exports = () => {
  console.log('Hello')
}

exportsに何か入れたい時には

test.js
module.exports = () => {
  console.log('Hello')
}

2.2.2.5 requireは同期関数

require()関数は同期関数なので、module.exportsオブジェクトへの操作も同期的に行われる必要があります。
→requireで呼ばれるモジュールの定義は同期処理として実装するしかありません。

2.2.2.6 依存解決

myApp/
  foo.js
  node_modules
     depA
        index.js
     depB
        bar.js
        node_modules
          depA
            index.js
     depC
        foobar.js
        node_modules
          depA
            index.js

・/myApp/foo.jsからrequire('depA')を呼び出した場合
→/myApp/node_modules/depA/index.jsをロードする
・/myApp/node_modules/depB/bar.jsからrequire('depA')を呼び出した場合
→/myApp/node_modules/depB/node_modules/depA/index.jsをロードする
・/myApp/node_modules/depC/foobar.jsからrequire('depA')を呼び出した場合
→/myApp/node_modules/depC/node_modules/depA/index.jsをロードする

このような呼び方で依存地獄を解決します。

2.2.2.7 モジュールのキャッシュ

パフォーマンス上不可欠ですが、次の天に注意してください。
・キャッシュによりモジュールの循環参照が可能になる。
・あるパッケージ内で同じモジュールが複数回requireされた場合、それらは同じインスタンスを参照する。
・require.cacheのキーをdeleteして、キャッシュを削除することも可能だが、しないほうがいい。

2.2.2.8 モジュールの循環参照

a.js
exports.loaded = false;
const b = require('./b')
module.exports = {
  bWasLoaded: b.loaded,
  loaded: true
}
b.js
exports.loaded = false;
const a = require('./a')
module.exports = {
  aWasLoaded: a.loaded,
  loaded: true
}
main.js
const a = require('./a')
const b = require('./b')
console.log(a)
console.log(b)

結果

{ bWasLoaded: true, loaded: true }
{ aWasLoaded: true, loaded: true }

→キャッシュの影響

2.2.3 モジュール定義におけるパターン

参考文献
Node.jsデザインパターン 第2版 - Mario Casciaro (著), Luciano Mammino (著), 武舎 広幸 (翻訳), 阿部 和也 (翻訳)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

mouseleave などで消えてしまう要素を setTimeout + debugger で無理やり捕まえる

  1. Google Chrome Dev Tool のコンソールで setTimeout + debugger を仕込む
  2. 時間が切れないうちに表示したい要素を表示させる
  3. setTimeout -> debugger が発火して要素選択できるようになる (mouseleave しても停止中なので消えない)
setTimeout(function() {
  debugger;
}, 3000);

CSS Trick にも同じ内容のものがあった

Set a Timed Debugger To Web Inspect Hard-To-Grab Elements
https://css-tricks.com/set-timed-debugger-web-inspect-hard-grab-elements/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javascriptでnew 演算子でも普通の関数としても呼び出せるコンストラクタの作り方

JavascriptのちょっとしたHackyなコードです。
Person(name, age)でも呼べるし new Person(name, age)とも呼べるような関数の作成方法です。

コード

function Person(name, age) {
  if(!(this instanceof Person)) return new Person(name, age);
  this.name = name
  this.age = age
}

考え方

コンストラクタとして呼び出されたら普通に処理。
関数として呼び出されたらコンストラクタ呼び出しとして呼びだし直す。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React Hooks でページネーションを実装する

やりたいこと

Hooks がリリースされてから、コンポーネントのシンプルな状態管理に関してはuseStateなどで済ませられるようになったが、やや複雑なコンポーネントの状態管理+アルファを Hooks でやってみたかった。

つくったもの

今回はuseReducerを使って市町村名をリストで表示するページネーションを実装してみた。
サンプルコード:https://codesandbox.io/s/paginator-demo-mnxvc
UIフレームワークは Material UI でサクッと用意。

useReducer を使った例としては公式のリファレンスでもカウンターなどがありますが、もう少し実用的で、複雑な状態管理と副作用をうまく利用することが要求されるコンポーネントを作って試してみたいと思っていたので今回それをやりました。

ページネーションを題材とした理由は、主に

  • コンポーネントの操作で非同期にデータをGETする必要性がある
  • ページの始点・終点・中間点等でコンポーネント(「進む」「戻る」などのボタン、ページ位置の表示)の制御を切り替える必要がある
  • 実際のWebアプリケーションでも使用される機会が多い

という点。その他に1ページあたりの表示件数とか、データの表示スタイルの切り替えといったアレンジも加えやすいので、思いつく限り今回は機能を盛り込んでみました。
※今回はサンプルコードを全て公開している都合上、公開APIを叩いて非同期でデータをGETするかわりに静的なデータ(市町村名とIDの一覧)を用意し擬似的に何件かずつarray.sliceで切り出して表示するという実装に変えてあります。

ソースコードとコンポーネントについて

詳しくは上記の CodeSandbox の中身をみていただくとして、今回はページネーションのコンポーネントとReducerの動きについて軽く説明します。

<Paginator />コンポーネント

初期Stateは以下のように設定しています。

index.js
function App() {
  return (
    <div className="App">
      <Container fixed>
        <Paginator
          sum={cities.length} //データの総件数
          per={10} //1ページあたりの表示件数
          initialData={cities.slice(0, 10)} //読み込み時に表示するデータ
          component={ListComponent} //データを渡すPresentaionコンポーネント
        />
      </Container>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

<Button> 前へ・次へ

ページの操作を行います。
「前へ」ボタンがクリックされたら前n件のデータとともにviewPreviewという関数を、「次へ」ボタンがクリックされたらviewNextという関数を発行します。

Paginator.js
const reducer = (state, action) => {
  switch (action.type) {
    //[前へ]ボタンをクリック時に発行。
    //ページ数のデクリメント
    case "viewPreview":
      return {
        ...state,
        currentPage: state.currentPage - 1,
        resourceData: action.data
      };
    //[次へ]ボタンをクリック時に発行。
    //ページ数のデクリメント
    case "viewNext":
      return {
        ...state,
        currentPage: state.currentPage + 1,
        resourceData: action.data
      };
    .
    .
    .
  }
};

reducer は発行される直前のstatedispatch()の中身(≒変更するstate)をactionとして引数にとります。変更を加えないstateは...stateとスプレッド演算子を使って return しないと上手くコンポーネントが機能しないため忘れずに。

<Select>1ページあたりの表示件数の切り替え

Selectボックスを変更した場合、setPerAmountという関数を発行します。

Paginator.js
//reducer
    //表示件数の切り替え時に発行。
    //現在のページを1ページ目にリセット。
    case "setPerAmount":
      return {
        ...state,
        currentPage: 1,
        per: action.per,
        pageAmount: Math.ceil(state.sum / action.per),
        resourceData: action.data
      };

表示件数を切り替えた場合は一番先頭のデータからまた表示し直すことが良い?気がするのでactionに入っているデータは先頭からn件の内容です。

<Switch>表示形式(コンポーネントのスタイル)を切り替え

初期状態では市町村名とIDが2段組になったリスト形式で表示していますが、スイッチを切り替えることでシンプルな1行のコンポーネントで現在のページの内容を表示し直します。
スイッチの切り替え時にデータを1行ずつ表示するための子コンポーネントとswitchComponentという関数を発行します。

Paginator.js
//reducer
    //表示形式の切り替え時に発行。
    case "switchComponent":
      return {
        ...state,
        component: action.component
      };

最後に

公式リファレンスを読むと使い方がなんとなくわかった気になりますが、実務ではカウンターやTodoリスト以上に複雑な機能を扱うことの方が多いためどういう場面で Hooks の力を最大限利用できるのかはまだ手探り中です。今回はuseContext抜きで実装しましたが、扱うコンポーネントの数が増えてきたら避けては通れなくなるかも。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

mathjsで桁あふれする規模の二項分布の計算をしてみた

mathjsで桁あふれする規模の二項分布の計算をしてみた

やってみたら、小規模なら簡単でも大規模だと工夫が必要であった。
別に言語にはこだわりは無いのだけど、今回は JavaScript を使用した。

実装的な結論から言うと BigNumber を使うだけの話です。
数式からプログラムを実装することに慣れていない人は参考になるかもしれない。

環境

Ubuntu 18.04
node: v10.16.2

Step 1. 数式のおさらい

$n$枚のコインを投げて、$x$枚が表になるような確率は、次のように表される。
Xは確率変数である。具体的には X={0枚のコインが表(すべて裏)の事象, 1枚のコインが表になる事象, ...} である。

P_{X}(x) = {}_{n}C_{k} p^{x}(1-p)^{n-x} 

答え合わせしやすいように、次のサイトと同じパラメーターで数式、およびプログラムの実装例を示す。

具体的に、コインを10回投げて、表が3回となる確率を考える。
根元事象は 3枚のコインが表になる事象 で、確率変数$x$の値は 3 である。
なお、理想的なコインを考えて、$p=0.5$とする。

\begin{aligned}
P_{X}(3) &= {}_{10}C_{3} 0.5^{3}(1-0.5)^{10-3} \\
&= \dfrac{10 \cdot 9 \cdot 8}{3 \cdot 2 \cdot 1} \cdot 0.125 \cdot 0.0078125 \\
&= 0.1171875
\end{aligned}

mathjs (Node.js)を用いた実装例
数式そのままです。特に解説はいりませんね。

const math = require('mathjs')

const n = 10
const k = 3
const p = 0.5

p_n_k = math.combinations(n, k) * math.pow(p, k) * math.pow(1 - p, n - k)
console.log('P(n=%d,x=%d)=%f', n, k, p_n_k)

実行例
結果も手計算で求めたものに一致します。

$ node throw_coin_10_times_3_head.js
P(n=10,x=3)=0.1171875
$

Step 2. コインを10回投げて、表が3回以上となる確率

コインを10回投げて、表が3回 以上 となる確率を考える。
根元事象の集合は 3枚のコインが表になる事象4枚のコインが表になる事象 ∪…∪ 10枚のコインが表になる事象 で、確率変数$x$={3, 4, ..., 10} である。離散型の確率分布であることに注意(3.5とかは存在していない)。この確率、どのくらいになると思いますか?

数式はこんな感じです。

\begin{aligned}
P_{X}(3 \cup 4 \cup \cdots \cup 10)
&= P_{X}(3) +  P_{X}(4)  +  \cdots + P_{X}(10) \\
&= \sum_{x=3}^{10} {}_{10}C_{x} 0.5^{x}(1-0.5)^{10-x} \\
&= {}_{10}C_{3} 0.5^{3}(1-0.5)^{10-3} + \cdots + {}_{10}C_{10} 0.5^{10}(1-0.5)^{10-10} \\
&\simeq 0.945
\end{aligned}

mathjs (Node.js)を用いた実装例
for文でぐるぐる回しているだけです。まだ、簡単なはず。特に解説はいりませんね。

const math = require('mathjs')

const k = 3
const p = 0.5

for (let n = 10; n <= 10; n++) {
    let sum = 0.0;
    for (let i = k; i <= n; i++) {
        p_n_k = math.combinations(n, i) * math.pow(p, i) * math.pow(1 - p, n - i)
        console.log('P(n=%d,x=%d)=%f', n, i, p_n_k)
        sum += p_n_k
    }
    console.log('P(n=%d,3<=x)=%f', n, sum)
}

実行例
結果も手計算で合計したものに大体一致します。ちなみに、約95%ですね。

$ node throw_coin_10_times_3to10_head.js
P(n=10,x=3)=0.1171875
P(n=10,x=4)=0.205078125
P(n=10,x=5)=0.24609375
P(n=10,x=6)=0.205078125
P(n=10,x=7)=0.1171875
P(n=10,x=8)=0.0439453125
P(n=10,x=9)=0.009765625
P(n=10,x=10)=0.0009765625
P(n=10,3<=x)=0.9453125
$

Step 3. : 5%のあたりくじを400本引いて、あたりの本数が20回以上となる確率

Step 2.の数値を置き換えができれば、数式は簡単です。
$p= 0.05, n=400, k=\{20, 21, \cdots, 400\}$

数式はこんな感じです。

\begin{aligned}
P_{X}(20 \cup 21 \cup \cdots \cup 400) &= P_{X}(20) +  P_{X}(21)  +  \cdots + P_{X}(21) \\
&= \sum_{x=20}^{400} {}_{400}C_{x} \cdot 0.05^{x}\cdot(1-0.05)^{400-x} \\
&= \text{???}
\end{aligned}

ただ、 400Cx をこれまでのやり方で数値計算してみようとすると、桁あふれします。

mathjs (Node.js)を用いた実装例(順列の計算だけ)
順列の計算だけを抜き出したコードです。

const { combinations } = require('mathjs')
let n = 400;
for (let k = 20; k <= 400; k++) {
    console.log('(n=%d, k=%d) -> %d', n, k, combinations(n, k))
}

実行結果
Number型で桁あふれしてしまっています。Σの合計どころではありません。

$ node mathjs_node_combinations_sample.js
(n=400, k=20) -> 2.788360983670897e+33
(n=400, k=21) -> 5.045605589499718e+34
(n=400, k=22) -> 8.692202356456333e+35
(n=400, k=23) -> 1.4285445611915188e+37
...snip
(n=400, k=120) -> 5.7072682234758346e+104
(n=400, k=121) -> 1.3206901674158961e+105
(n=400, k=122) -> Infinity
(n=400, k=123) -> Infinity
(n=400, k=124) -> Infinity
(n=400, k=125) -> Infinity
(n=400, k=126) -> Infinity
...snip

Final Step. :プログラムの実装例

実は、桁あふれ以外にも、加算時の情報落ちなど、もとのコードは色々問題があります。

mathjs は BigNumber を使えるので、全面的にこれを使います。これで、BigNumberで扱える範囲の問題なら数値計算ができます。

mathjs (Node.js)を用いた実装例(BigNumer使用)

const math = require('mathjs')

const n = 400
const k = 20
const p = 0.05

let sum = math.bignumber(0.0);
for (let i = k; i <= n; i++) {
    let c_n_k = math.bignumber(math.combinations(math.bignumber(n), math.bignumber(i)))
    let pow_p = math.pow(math.bignumber(p), math.bignumber(i))
    let pow_np = math.pow(math.bignumber(1 - p), math.bignumber(n - i))
    p_n_k = math.bignumber(c_n_k * pow_p * pow_np)
    console.log('P(n=%d,k=%d)=%s * %s * %s -> %s', n, i, c_n_k.toPrecision(8), pow_p.toPrecision(4), pow_np.toPrecision(4), p_n_k.toPrecision(4))
    sum = math.add(sum, p_n_k)
}

console.log('P(n=%d, %d<=x) = %s', n, k, sum.toFixed(4))

実行結果
表示桁が長い部分(4桁以上)は、削っています。Infinity扱いとなっていた計算に成功しています。

$ node lottery_400_hit_over_20.js
P(n=400,k=20)=2.7883610e+33 * 9.537e-27 * 3.427e-9 -> 0.09114
P(n=400,k=21)=5.0456056e+34 * 4.768e-28 * 3.608e-9 -> 0.08680
P(n=400,k=22)=8.6922024e+35 * 2.384e-29 * 3.798e-9 -> 0.07870
P(n=400,k=23)=1.4285446e+37 * 1.192e-30 * 3.998e-9 -> 0.06808
...snip
P(n=400,k=120)=5.7072682e+104 * 7.523e-157 * 5.789e-7 -> 2.486e-58
P(n=400,k=121)=1.3206902e+105 * 3.762e-158 * 6.094e-7 -> 3.027e-59
P(n=400,k=122)=3.0202669e+105 * 1.881e-159 * 6.414e-7 -> 3.644e-60
P(n=400,k=123)=6.8262942e+105 * 9.404e-161 * 6.752e-7 -> 4.334e-61
P(n=400,k=124)=1.5249060e+106 * 4.702e-162 * 7.107e-7 -> 5.096e-62
P(n=400,k=125)=3.3669925e+106 * 2.351e-163 * 7.482e-7 -> 5.922e-63
P(n=400,k=126)=7.3485948e+106 * 1.175e-164 * 7.875e-7 -> 6.803e-64
...snip
P(n=400,k=247)=1.5240999e+114 * 4.422e-322 * 0.0003906 -> 2.618e-211
P(n=400,k=248)=9.4027133e+113 * 2.211e-323 * 0.0004111 -> 7.640e-213
P(n=400,k=249)=5.7398089e+113 * 1.105e-324 * 0.0004328 -> 0.000
P(n=400,k=250)=3.4668446e+113 * 5.527e-326 * 0.0004556 -> 0.000
...snip
P(n=400,k=397)=10586800 * 3.098e-517 * 0.8574 -> 0.000
P(n=400,k=398)=79800.000 * 1.549e-518 * 0.9025 -> 0.000
P(n=400,k=399)=400.00000 * 7.745e-520 * 0.9500 -> 0.000
P(n=400,k=400)=1.0000000 * 3.873e-521 * 1.000 -> 0.000
P(n=400, 20<=x) = 0.5320
$

所感

数値計算は奥深いですね。
実際、BigNumer の範囲では計算はできるのですが、逆に言えばできることはその範囲までです。
BigNumberを使用すると整数型や浮動小数点の計算に比べてパフォーマンス上の問題もあって、ループ回数を増やしたりすると時間がかかり、時間的にできることも縮小します。
(最近流行のDeep Learningが流行っているのは、数値計算ライブラリのアルゴリズム、計算時間、ハードウェアなどを活用して、計算が可能になったことが一つのピースとなったと言えます。)

こういったAPIなんて使用せず、自前で実装するなら、計算順序などを工夫して情報落ちなど避けることはできます。
今回で言うと、 $0.05^{122}$ とかですね。 1.880e-159 とかほぼ0です。今回は (デカイ数) × (小さい数) × (小さい数) という構造をしていて、400Cx が math.combinations(n,k) と math.pow(p, k) などと、数式が単位にAPIがあるのは、とても分かりやすい。ただ、分かりやすくAPIが意味的(数式的に)に分離されていることとは、必ずしも数値計算に有利だとは限らない。

こういうのは、素朴に数式をハードウェアにあわせて最適化するアプローチもあるが一方で、シミュレーテッド・アニーリングのアルゴリズムのように意味的な関連付けは困難だが、入力とそれに対する解の性質は分かっていて使い物になる……そういうアプローチもある。今回のは、実装は綺麗に見えて、計算機科学的にダサいそういう感じのものであろう。

参考

数学・計算機科学関連

プログラミング関連

その他

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Nuxt.js 2.9 がリリースされたぞ!

Image
Logo from Nuxt TypeScript https://typescript.nuxtjs.org/

Vue を活用したユニバーサルアプリフレームワークの Nuxt.js の最新バージョン v2.9 がリリースされました。

以下リリースノートを翻訳していきます。あまりなれてないので誤訳があったらごめんなさい ?

https://github.com/nuxt/nuxt.js/releases/tag/v2.9.0

⚠️ 重要事項

  • Node.js のサポートバージョンが v8.9.0 以降となりました
  • TypeScript のサポートが外部化されました
  • vue-meta2.0.0 に更新されました。対応するリリースノートを参照してください。
  • scrollBehavior オプションが非推奨となりました PR#6055
    • app/router.scrollBehavior.js を代わりに使用してください。
  • devModules オプションが非推奨になりました。 PR#6203
    • buildModules を代わりに使用してください。

? バグフィックス

  • General
    • modulepreload 警告の修正
    • QuietMode の際のビルドエラーで例外を投げる
    • ModernMode のための Babel CoreJS のサポート
    • Babel Pollyfill が無効化できない件
  • Renderer
    • Safari 10 における nomodule スクリプトの完全な読み込み
  • CLI
    • nuxtnuxt-edge が両方インストールされているときの対策
  • vue-app
    • Hash Navigation での triggerScroll の発火
  • vue-renderer
    • createRenderer の実行前に render:resourcesLoaded を呼び出す機能の追加
    • キャッシュの変更を防ぐための SPAメタ情報の複製
  • Webpack
    • CSSの展開が HMR と Source-map を破壊する問題の対策

? 新機能

  • CLI
    • Option が Export されるようになった
  • server
    • renderAndGetWindowloadingTimeout と他のパラメータも渡せるようになった
  • Webpack
    • build.transpile に関数をエントリを追加できる様になった
  • vue-app
    • $nuxt.refresh が追加
    • ビルドインジケータの表示に WebSocket の代わりに EventSource を使用するようになった
    • scrollBehavior から app/router.scrollBehavior.js への移行
    • <no-ssr> のエイリアスとして <client-only> の追加
    • ローディングインジケータのカスタマイズができるようになった
    • asyncData と fetch で $nuxt.refresh() が利用できるようになった
    • Option が Export されるようになった
    • watchQuery をサポート
  • vue-renderer
    • V1系の互換性のためCSPオプションを追加
    • SSR時点でのbodyタグの前後へのタグの差し込みのサポート
  • typescript
    • TypeScript サポートのための仕組みの外部パッケージ化
    • 後述します

? リファクタリング

  • config
    • devModulesbuildModules にリネームされました
  • babel-preset-app
    • babel-plugin-dynamic-import-node の削除
  • Webpack
    • トランスパイルの正規化がシンプルになった
  • General
    • @nuxt/eslint-config v1 への依存のリファクタ

?その他

  • 省略

TypeScript サポートの外部パッケージ化について

TypeScript 大好きクラブ会員として気になったので少し紹介します。

今回 Nuxt.js 2.5 からサポートされた TypeScript のサポートのパッケージ分割がすすみ、@nuxt/typescript で提供されていた機能が Nuxtのビルド時に効果を発揮する @nuxt/typescript-build、 型定義を提供する @nuxt/types、そして、実行時に TypeScript を評価できるようにする @nuxt/typescript-runtime に分割されました。

これは Nuxt.js 2.9 のリファクタリングの一環によるもので、主にモジュールが Nuxt.Builder を拡張できる機能や、WebpackのWarningFix プラグインのフィルタをカスタマイズすること、あるいは Nuxt が内包する BabelLoader の設定を拡張したり、ts-loader で tsx をハンドリングしたりするシーンに置いてより柔軟に便利にする目的があるようです。(https://github.com/nuxt/nuxt.js/pull/5854))

おそらく、モジュールが分割されたことで、ゴリゴリカスタムしたいシーンなどでもより便利になったということでしょう。

また以降は Nuxt.js の TypeScript サポートは Nuxt TypeScript という nuxtjs.org 配下の公式コンテンツとして取り扱われ、TypeScript の コード補完やコードの品質、型安全 を意識したより手堅い Nuxt.js による開発手法をサポートしていくようです。

現在確認できる情報として、分割されたパッケージについての説明や、詳しい設定方法の他、サンプルとして Object APIClass APIFunction API それぞれでの実装方法が例示されています。(なんて呼んであげたらいいかわからなかったのでオブジェクト記法とかクラス記法とか個人的には呼んでたのですが、正式な呼び方ができたようで一安心。)

移行ガイド

今回のリファクタリングによりパッケージの分割と廃止が行われたため、package.json に記録されている依存のパッケージの入れ替え、および設定の変更追従が必要となります。下記はマイグレーションガイドを簡単に翻訳したものです。

1. 依存パッケージの切り替え
  • @nuxt/typescript 非推奨となったので代替として @nuxt/typescript-build @nuxt/types をインストールします。
npm uninstall @nuxt/typescript
npm install -D @nuxt/typescript-build @nuxt/types
2. 型定義参照元の切り替え
  • @nuxt/vue-app@nuxt/config から参照していた型定義は @nuxt/types から参照するように変更します。
"compilerOptions": {
  "types": [
     "@nuxt/types"
  ]
}
3. @nuxt/typescript-build を nuxt.config.js のモジュール定義に追加
export default {
  modules: ['@nuxt/typescript-build']
}
4. @nuxt/typescript の設定は build.typescript に移動
export default {
  typescript: {
    typeCheck: true,
    ignoreNotFoundWarnings: true
  }
}

下記のように書くこともできます

export default {
  modules: [
    ['@nuxt/typescript-build', {
      typeCheck: true,
      ignoreNotFoundWarnings: true
    }]
  ]
}
5. 直接TypeScript実行したい場合 (オプション)

すべてをTypeScriptで書きたい皆様向けの設定として、@nuxt/typescript-runtime が用意されています。これをインストールすると nuxt-ts もしくは nuxts がコマンドとして使用できるようになり、設定ファイルは nuxt.config.ts とできる他、モジュールや serverMiddleware も TypeScript で配置し実行できるようになります。

npm install @nuxt/typescript-runtime
"scripts": {
  "dev": "nuxt-ts",
  "build": "nuxt-ts build",
  "start": "nuxt-ts start"
  "generate": "nuxt-ts generate"
}

まとめ

リリースノートを見る限り、scrollBehaviordevModules などの廃止と移行がある以外はあまり大きな変更はなさそうでした。TypeScriptでNuxt.jsを実装している場合は、移行ガイドに従ってパッケージの入れ替えが必要になりますのでサクッと追従してあげると良さそうです。最新と手元のバージョンの差が大きくならないように頑張っていきましょう! ?

from: Scrapbox

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reactでメモアプリを作る(React基礎講座9)

はじめに

今回は、Reactを使って、メモを追加・削除ができる簡単なメモアプリを作成していきます。

挙動は以下のような感じです。

116d349e1defb23e1458f5359e3a85c6.gif

2a9d4baca00d88f7bf4ef1ab914e5438.gif

シリーズ

本記事はReact基礎講座のための連載になっています。気になる方のために、前の章は以下です。

React開発で見かける配列処理系のメソッド map , filter について(React基礎講座8) - Qiita

最初の記事は、以下です。

Reactを使ってJSXの内容をレンダリングする(create-react-app)(React基礎講座1) - Qiita

メモアプリとは

仕様は以下のようなシンプルなReactアプリケーションです。

  • テキストエリアにテキストを入力して、入力ボタンを押したらその内容が一覧で表示される
  • メモの一覧にはそれぞれ、削除ボタンがあり、それを押したらそのアプリが消える
  • メモはDBには格納されない

ファイル構成

root/
 ├ public/
 └ src/
   └ components/
   |  └ Form.js
   |  └ List.js
   └ memoApp.js
   └ index.js

  • index.js
    • React.ComponentであるmemoAppをimportする
  • MemoApp.js
    • stateとして以下の情報を初期設定します
      • memos: 具体的なメモの情報
      • nextId: 次に追加するメモのidの情報
        • stateを持たせるので、クラスコンポーネントで実装します
    • メモを保存する機能 addMemo と、削除する機能 deleteMemoを作る
      • これら2つの機能がstateを変更させます
    • コンポーネント Form.jsList.js をimportする
      • これらも今回はクラスコンポーネントで実装しましょう
  • /components/Form.js
    • メモのフォーム部分の見た目を作る
  • /components/List.js
    • メモ一覧の見た目を作る

コンポーネントの配置

まずは、ファイル構成どおりにコンポーネントを配置していきましょう。

まずは、index.js

index.js
import React from "react";
import { render } from "react-dom";

import MemoApp from "./MemoApp";

render(<MemoApp />, document.getElementById("root"));

次に、MemoApp.js

このコンポーネントから、<Form /><List />をimportして呼び出していることが分かります。

MemoApp.js
import React from "react";
import { render } from "react-dom";

import Form from "./components/Form";
import List from "./components/List";

class MemoApp extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div>
        <h2>MemoApp</h2>
        <Form />
        <List />
      </div>
    );
  }
}

export default MemoApp;

続いて、FormListコンポーネントを作成していきます。

Form.js
import React from "react";
import { render } from "react-dom";

class Form extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return <h3>Form</h3>;
  }
}

export default Form;
List.js
import React from "react";
import { render } from "react-dom";

class List extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return <h3>List</h3>;
  }
}

export default List;

スクリーンショット 2019-08-19 00.05.46.png

すると、こんな感じで作成できましたかね?これで、下準備というか、コンポーネントの配置は完了です。

MemoAppにStateを定義してListで表示

MemoAppにStateを定義していきます。

前出しましたが、定義するStateは2種類です。

  • memos: 具体的なメモの情報
    • 配列の中に連想配列を持たせましょう(keyは、idcontentにしましょう)
    • これ、最終的にはテキストボックスからcontentを生成しますが、今回はコンポーネントの見た目確認のため、Stateに定義します
  • nextId: 次に追加するメモのidの情報

MemoAppでは、<List />コンポーネント部分にstateのmemosを渡して、List.jsで表示させるようにしましょう。

List.jsでは、propsで、MemoAppにStateとして定義したmemosを要素全て表示させましょう。
その時に、使用するメソッドは....mapでしたね。

それでは、実装していきましょう。まずは、MemoApp.jsから。

MemoApp.js
import React from "react";
import { render } from "react-dom";

import Form from "./components/Form";
import List from "./components/List";

class MemoApp extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      memos: [
        { id: 1, content: "one" },
        { id: 2, content: "two" },
        { id: 3, content: "three" },
        { id: 4, content: "four" },
        { id: 5, content: "five" }
      ],
      nextId: 0
    };
  }

  render() {
    return (
      <div>
        <h2>MemoApp</h2>
        <Form />
        <List memos={this.state.memos} />
      </div>
    );
  }
}

export default MemoApp;

こちらは、Stateを定義して、それをStateとして<List />コンポーネントにmemosとして渡します。

List.jsでは、MemoApp.jsからStateに乗って渡ってきたmemosmapで要素ごとに処理した結果を変数listコンポーネントに入れて、その下のrenderメソッドで{list}として受け取ります。

List.js
import React from "react";
import { render } from "react-dom";

class List extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    const list = this.props.memos.map(memo => {
      return (
        <li>
          #{memo.id} - {memo.content}
        </li>
      );
    });
    return (
      <div>
        <h2>List</h2>
        {list}
      </div>
    );
  }
}

export default List;

こんな感じですね。見た目は、こんな感じになります。

スクリーンショット 2019-08-19 22.56.31.png

Form.jsを作成する

今度は、メモを作成するFormのコンポーネントを作成します。

Form.js
import React from "react";
import { render } from "react-dom";

class Form extends React.Component {
  constructor(props) {
    super(props);

    this.state = { content: "content" };
  }

  render() {
    return (
      <div>
        <h2>Form</h2>
        <input value={this.state.content} />
        <input type="submit" value="Add Memo" />
      </div>
    );
  }
}

export default Form;

ただ、このままだと、テキストエリアの値がいつまでも変わらないので、テキストエリア内の値が更新された場合、その変更後の値をテキストエリアに表示させます。テキストエリア内の値のstateが変更されたことを検知するイベントハンドラといえば、onChangeですね。これは、フォーム内の要素の内容が変更された時に起こるイベントハンドラです。

これをハンドラにして、メソッドhandleChangeを呼び出すような処理を書きましょう。handleChange関数の引数にeventをようして、関数内でevent.target.valueと記述すると、イベントの結果更新された値を取得することができます。それを変数contentとして格納する。

その変数(content)を使って、setStateしてフォーム内の値(state)を更新します。つまり、handleChange関数を使って自分自身の値を更新させていることがわかります。

それでは、実装していきましょう。

Form.js
import React from "react";
import { render } from "react-dom";

class Form extends React.Component {
  constructor(props) {
    super(props);

    this.state = { content: "content" };
  }

  render() {
    return (
      <div>
        <h2>Form</h2>
        <input value={this.state.content} onChange={this.handleChange} />
        <input type="submit" value="Add Memo" />
      </div>
    );
  }

  handleChange = event => {
    const content = event.target.value;
    this.setState({ content: content });
  };
}

export default Form;

次に、submitした際の挙動を書いていきましょう。inputタグをformタグで囲って、<input type="submit" value="Add Memo" />をクリックした際の処理(関数を)formの開始タグに記述します。その際のハンドラは、onSubmitですね。onSubmitの詳細は、以下の記事を見て見てください。

参考

https://www.sejuku.net/blog/28720

onSubmitで検知した際に、処理させる関数名はhandleSubmitとでもしましょう。今回も引数eventを受け取るようにしましょう。

処理内容は、一旦以下のようにします。

  • まずは、submitをクリックした際のデフォルトの挙動をしないようにする
  • 次に、アラートにて、現状のthis.state.contentの値を表示させるようにする
  • アラートで、現状のcontentを表示させたら、現状のテキストエリアの値は何も無いことにします

こんな感じです。では、実装していきましょう。

Form.js
import React from "react";
import { render } from "react-dom";

class Form extends React.Component {
  constructor(props) {
    super(props);

    this.state = { content: "content" };
  }

  render() {
    return (
      <div>
        <h2>Form</h2>
        <form onSubmit={this.hamdleSubmit}>
          <input value={this.state.content} onChange={this.handleChange} />
          <input type="submit" value="Add Memo" />
        </form>
      </div>
    );
  }

  handleChange = event => {
    const content = event.target.value;
    this.setState({ content: content });
  };

  hamdleSubmit = event => {
    event.preventDefault();
    alert(this.state.content);
    this.setState({ content: "" });
  };
}

export default Form;

こんな感じですね。挙動は、こんな感じになります。

b044a7af7e18e4f869ff43618be34f3e.gif

ここまでできれば一旦OKです。

なので、Form.jsのコンストラク内のstateのcontentで定義している文字列は空白でもいいかもですね。

次は、いよいよ、アラートを出すのではなく、実際に、stateを変更して、Listコンポーネントを変更するような機能を作成していきます。

stateを変更する機能を作成する

今度は、テキストフォームにメモの内容を記述したら、Listコンポーネントにその値が反映されるようにしましょう。

手順は以下のような感じです。

  • (色々なやり方がありますが今回は)MemoApp.jsでテキストを入力してボタンを押したら
    • 関数(addMemo)が走るようにしましょう
      • 関数(addMemo)自体は、
        • setStateが走るようにして
        • その中でstate(memosの配列にSpread operatorで既存のものを残しつつ、新たな要素を追加します)
        • contentの内容はForm.js内のhamdleSubmit関数の中で引数として渡します
        • 宣言元のaddMemoは引数としてhamdleSubmit関数から渡ってきたメモ内容(this.state.content)を引数(content)として受け取って、Spread operator(3点)の後にカンマして、あらたなオブジェクトに渡す
        • そして、idもthis.state.nextIdもセットして、その後のsetState内で 1たす項目を設けます
  • 既存のstate(memos)にsubmitした際、要素が加われば、コンストラクタで定義した既存のstate(memos)は消しましょう

JSでは、Spread operator(スプレッド構文)は頻出シンタックスなのでぜひ覚えてください。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Spread_syntax

また、今回実装したstateを変更するメソッド(addMemo)を親(MemoApp.js)に持たせて、それを子(Form.js)に渡して実行する方法はよくあるので難しいですが手に馴染ませてください。

子に渡っているのはhamdleSubmit関数の中のthis.props.addMemo(this.state.content); ← この部分ですね。

それでは、実装していきましょう。

MemoApp.js
import React from "react";
import { render } from "react-dom";

import Form from "./components/Form";
import List from "./components/List";

class MemoApp extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      memos: [],
      nextId: 0
    };
  }

  addMemo = content => {
    this.setState({
      memos: [...this.state.memos, { id: this.state.nextId, content: content }],
      nextId: this.state.nextId + 1
    });
  };

  render() {
    return (
      <div>
        <h2>MemoApp</h2>
        <Form addMemo={this.addMemo} />
        <List memos={this.state.memos} />
      </div>
    );
  }
}

export default MemoApp;
Form.js
import React from "react";
import { render } from "react-dom";

class Form extends React.Component {
  constructor(props) {
    super(props);

    this.state = { content: "" };
  }

  render() {
    return (
      <div>
        <h2>Form</h2>
        <form onSubmit={this.hamdleSubmit}>
          <input value={this.state.content} onChange={this.handleChange} />
          <input type="submit" value="Add Memo" />
        </form>
      </div>
    );
  }

  handleChange = event => {
    const content = event.target.value;
    this.setState({ content: content });
  };

  hamdleSubmit = event => {
    event.preventDefault();
    this.props.addMemo(this.state.content);
    this.setState({ content: "" });
  };
}

export default Form;

こんな感じですね。挙動は、こんな感じになります。

116d349e1defb23e1458f5359e3a85c6.gif

stateを削除する機能を作成する

次は、個々のメモを削除するような機能を作成していきます。

手順は以下のような感じです。

  • List.jsのコンポーネントにタグを設置
  • 機能自体はMemoApp.jsaddMemoの下にdeleteMemoメソッドを作成していきましょう
    • deleteMemoメソッドは引数にmemosのidを取れるようにしてください
  • では、上記の呼び出しをList.jsに定義したbuttonタグにハンドラはonClickとして、アロー関数をdeleteMemoメソッドのコールバックとして実行してください
  • コール先の関数の中の処理ですが
    • 配列(memos)をfilterで処理して、引数として受け取ったid以外をTRUEとして返すような条件でにして、結果を変数filteredArrayに格納し、それをmemosのsetStateとして渡す

それでは、実装していきましょう。

MemoApp.js
import React from "react";
import { render } from "react-dom";

import Form from "./components/Form";
import List from "./components/List";

class MemoApp extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      memos: [],
      nextId: 0
    };
  }

  addMemo = content => {
    this.setState({
      memos: [...this.state.memos, { id: this.state.nextId, content: content }],
      nextId: this.state.nextId + 1
    });
  };

  deleteMemo = id => {
    const filteredArray = this.state.memos.filter(memo => {
      return memo.id !== id;
    });
    this.setState({ memos: filteredArray });
  };

  render() {
    return (
      <div>
        <h2>MemoApp</h2>
        <Form addMemo={this.addMemo} />
        <List memos={this.state.memos} deleteMemo={this.deleteMemo} />
      </div>
    );
  }
}

export default MemoApp;
react.List.js
import React from "react";
import { render } from "react-dom";

class List extends React.Component {
  render() {
    const list = this.props.memos.map(memo => {
      return (
        <li>
          #{memo.id} - {memo.content}{" "}
          <button onClick={() => this.props.deleteMemo(memo.id)}>delete</button>
        </li>
      );
    });
    return (
      <div>
        <h2>List</h2>
        <ul>{list}</ul>
      </div>
    );
  }
}

export default List;

こんな感じですね。挙動は、こんな感じになります。

2a9d4baca00d88f7bf4ef1ab914e5438.gif

冗長な部分をリファクタ

冗長な書き方になってしまっている部分をconst { ... } = this.state;propsでも同様)と定義してリファクタしてあげましょう。

また、この記述import { render } from "react-dom";が不要なファイルはあるので、不要であれば消します。

そして、

Warning: Each child in a list should have a unique "key" prop.

というエラー対応をします。具体的には、Listコンポーネント’にある

タグにidを追加します。
<li key={memo.id}>

こんな感じですね。

それでは、実装していきましょう。

MemoApp.js
import React from "react";

import Form from "./components/Form";
import List from "./components/List";

class MemoApp extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      memos: [],
      nextId: 0
    };
  }

  addMemo = content => {
    const { memos, nextId } = this.state;
    this.setState({
      memos: [...memos, { id: nextId, content: content }],
      nextId: this.state.nextId + 1
    });
  };

  deleteMemo = id => {
    const { memos } = this.state;
    const filteredArray = memos.filter(memo => {
      return memo.id !== id;
    });
    this.setState({ memos: filteredArray });
  };

  render() {
    const { memos } = this.state;
    return (
      <div>
        <h2>MemoApp</h2>
        <Form addMemo={this.addMemo} />
        <List memos={memos} deleteMemo={this.deleteMemo} />
      </div>
    );
  }
}

export default MemoApp;
List.js
import React from "react";

class List extends React.Component {
  render() {
    const { memos, deleteMemo } = this.props;
    const list = memos.map(memo => {
      return (
        <li key={memo.id}>
          #{memo.id} - {memo.content}{" "}
          <button onClick={() => deleteMemo(memo.id)}>delete</button>
        </li>
      );
    });
    return (
      <div>
        <h2>List</h2>
        <ul>{list}</ul>
      </div>
    );
  }
}

export default List;

長くなりましたが、Reactを使ったメモアプリ作成は以上です。参考にしてみてください。

参考

  • 改訂新版JavaScript本格入門 ~モダンスタイルによる基礎から現場での応用まで | 山田 祥寛
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

jestでパラメータ化テストをする

はじめに

境界値分析など、引数を変えて試験をしたいことはよくあるとおもいます。
Javaではspockなどが有名ですが、jestでもパラメータ化テストは可能です!

試験対象

以下の現在時刻が昼間かどうかを判定するモジュールを試験することにします。

isDaytime.js
module.exports = () => {
  const hours = new Date().getHours();
  return ((6 < hours) && (hours <= 18));
}

書き方

書き方には2種類あります。
テストの性質や好みで書き分けられます。

配列を使った書き方

const isDaytime = require('./isDaytime');

beforeEach(() => {
  jest.clearAllMocks();
});
const OriginalDate = Date; // 退避

test.each([
    [0, false],
    [5, false],
    [6, false],
    [7, true],
    [17, true],
    [18, true],
    [19, false],
  ])('%i時のとき、%pを返す', (hours, expected) => {

  jest.spyOn(global, 'Date').mockImplementation(() => {
    return new OriginalDate(2019, 8, 20, hours)
  });

  expect(isDaytime()).toBe(expected);
});

image.png

https://jestjs.io/docs/ja/api#1-testeachtable-name-fn-timeout

test.each([[パラメータ1], [パラメータ2]])(テストケース名, 関数, タイムアウト)
の形式で記載します。
テストケースのタイトルにはprintf書式を使うことができ、パラメータで指定した順に参照されます。
以下のように、書き方を工夫するとタイトルをより読みやすくすることも可能です :ok_woman:

const isDaytime = require('./isDaytime');

beforeEach(() => {
  jest.clearAllMocks();
});
const OriginalDate = Date; // 退避

test.each([
    [0, '夜中', false],
    [5, '夜中', false],
    [6, '夜中', false],
    [7, '昼間', true],
    [17, '昼間', true],
    [18, '昼間', true],
    [19, '夜中', false],
  ])('%i時のときは、%s', (hours, _, expected) => {

  jest.spyOn(global, 'Date').mockImplementation(() => {
    return new OriginalDate(2019, 8, 20, hours)
  });

  expect(isDaytime()).toBe(expected);
});

image.png

テンプレートリテラルを使った書き方

const isDaytime = require('./isDaytime');

beforeEach(() => {
  jest.clearAllMocks();
});
const OriginalDate = Date; // 退避

test.each`
    hours | expected
    ${0}  | ${false}
    ${5}  | ${false}
    ${6}  | ${false}
    ${7}  | ${true}
    ${17} | ${true}
    ${18} | ${true}
    ${19} | ${false}
  `('$hours時のとき、$expectedを返す', ({hours, expected}) => {

  jest.spyOn(global, 'Date').mockImplementation(() => {
    return new OriginalDate(2019, 8, 20, hours)
  });

  expect(isDaytime()).toBe(expected);
});

image.png

https://jestjs.io/docs/ja/api#2-testeach-table-name-fn-timeout

テンプレートリテラルを使用してパラメータを書けます。
変数が多い時などにこの書き方にすると、見やすくなると思います。
文字列をパラメータに使いたい場合、 ${"文字列"} の形式で指定できます。

便利な設定

書き方をよく忘れてしまうので、VSCodeのスニペットなどに、パラメータテストを呼び出せるような設定をしておくと便利です :ok_hand:

javascript.json
{
    // Place your snippets for javascript here. Each snippet is defined under a snippet name and has a prefix, body and 
    // description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
    // $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. Placeholders with the 
    // same ids are connected.
    // Example:
    // "Print to console": {
    //  "prefix": "log",
    //  "body": [
    //      "console.log('$1');",
    //      "$2"
    //  ],
    //  "description": "Log output to console"
    // }
    "jestEach":{
        "prefix": "jest.each",
        "body": [
                "test.each([",
                "  [0, false],",
                "])('%iのとき、%pを返す', (hoge, expected) => {",
                "})"
        ],
        "description": "パラメータテスト"
    }
}

image.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Mermaid.js Live Editorのテーマを変更する

はじめに

最近フローチャートを描くために使っている、Mermaid.jsのLive Editor。
何もインストールせずにつかえて、書き終わったらリンクを送るだけでいいので仕事で重宝しています。

https://mermaidjs.github.io/mermaid-live-editor

テーマ変更方法

Mermaid configurationを変更します。

{
  "theme": "neutral"
}

テーマの種類

themeはいまのところ4種類あります。

theme
dark mermaidjs-dark-theme.png
default mermaidjs-default-theme.png
forest mermaidjs-forest-theme.png
neutral mermaidjs-neutral-theme.png

参照

mermaid.jsのドキュメント
Live Editorのソースコード

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む