javascriptでpartition 関数を実装
こんにちは
javascriptでhaskellでいうところのpartition関数を実装してみたのでメモします。
partition関数
全然一般的ではないので説明です。僕がpartition関数って言っているのは、「配列を、その要素に対して真偽を判定する関数により、真なものと偽なものの2つのグループに分ける関数」です。
たとえば以下のように動くものです。
[0,1,2,3,4,5,6,7,8,9].partition(num => num > 4) // => [[5,6,7,8,9],[0,1,2,3,4]] [1,"2",3].partition(item => typeof item === 'string') // => [["2"],[1,3]]
いかにもありそうな関数ですが、探した限りではありませんでした。(altjsでは実装されている場合があるようです)
やっちゃだめみたいですがArrayのprototypeを汚染して関数を定義します。Arrayの他のコールバックを引数に取る関数を参考にしました。
Array.prototype.partition = function(callback, thisArg) { if (this === null) { throw new TypeError('Array.prototype.partition called on null or undefined'); } if (typeof callback !== 'function') { throw new TypeError('callback must be a function'); } var T; if (thisArg) { T = thisArg; } var list = Object(this); var length = list.length >>> 0; var value; var accepted = new Array(); var rejected = new Array(); for(var i = 0; i < length; i++) { value = list[i]; if (callback.call(T, value, i, list)) { accepted.push(value); } else { rejected.push(value); } } return [accepted, rejected]; }; [0,1,2,3,4,5,6,7,8,9].partition(num => num > 4) // => [[5,6,7,8,9],[0,1,2,3,4]] [1,"2",3].partition(item => typeof item === 'string') // => [["2"],[1,3]]
第二引数のthisArg
はArray.prototype.map
の仕様にあったため追記しました。使い道があまりわかりませんが例えば繰り返しの中で副作用を起こさせることができます(ちょっと意味のない例。。。)。
追記(2017/11/29): よく考えるとthisArg
は関数呼ぶ時に呼び元のthis
を渡したりする時に使うためでした。
[1,2,3,4,5].partition(function(num) { this.log(num) ; return num > 2; }, { log(num) { console.log(`${num}を振り分けました`) } });
最後にreduceを使って、配列とコールバックを引数に取る関数として書いてみます。
const partition = (list, callback) => { if (list === null) throw new TypeError('partition called on null or undefined'); if (typeof callback !== 'function') throw new TypeError('callback must be a function'); return list.reduce((state, item) => { if(callback(item)) { state[0].push(item); } else { state[1].push(item); } return state; }, [[],[]]); }; partition([0,1,2,3,4,5,6,7,8,9], num => num > 4) // => [[5,6,7,8,9],[0,1,2,3,4]] partition([1,"2",3], item => typeof item === 'string') // => [["2"],[1,3]]
感想
テストとか書いてしっかり確かめたいですね。小さいものは書いていて楽しいです。
browser-syncでブラウザ自動更新
Node.jsでコードを書いていると、ターミナルとブラウザの行き来が頻発して面倒です。自分のノートPCはモニタが小さいので、ウィンドウを切り替えていると埋もれてしまい、それを探すために集中力が切れてしまったりします。
browser-syncを使ってコードを書き換えるとブラウザが自動更新されるようにしてみました。expressと連携することもできるので、そのこともメモしておきます。
単純な使い方
npm install --save-dev browser-sync
監視対象のファイルを指定して起動します。
./node_modules/.bin/browser-sync start --server --files index.html
コマンドを実行するとlocalhost:3000
でサーバが起動します。ブラウザが立ち上がり、Connected to Browser Sync
という文字がページ右上に表示されます。この状態で、index.html
を編集するとブラウザが自動更新されます。
expressと連携
expressでサーバを立てている場合、connect-browser-sync
ミドルウェアを利用してサーバに自動更新の機能を組み込むことができます。まずはインストールします。
npm install express --save npm install browser-sync --save-dev npm install connect-browser-sync --save-dev
app.jsを作成します。
var express = require('express'); var app = express(); if ( app.get('env') === 'development' ) { var browserSync = require('browser-sync'); var connectBrowserSync = require('connect-browser-sync'); var browserSyncConfigurations = { "files": "static/*" }; app.use(connectBrowserSync(browserSync(browserSyncConfigurations))); } app.use(express.static(__dirname + '/static')); app.get('/', function (req, res) { res.sendFile(__dirname + '/static/index.html'); }); app.use(function(err, req, res, next) { console.error(err.stack); res.status(500).send('Something broke!'); }); app.listen(3000);
if
の部分がbrowserSyncの設定です。オプションをオブジェクトでbrowserSync
関数に渡し、connectBrowserSync
ミドルウェアをexpressに登録しています。オプションのfiles
は監視対象のファイルを設定します。ワイルドカードで指定したり、配列として複数のパスを渡すこともできます。ここではstatic
以下のファイルをすべて監視しています。ここをコンパイル後のjsの吐き出し先に設定したりすれば、webpackと協調して使うこともできますね。
ミドルウェアの登録のタイミング次第では動かなかったりして少しはまりました。静的コンテンツのディレクトリ設定をbrowserSyncの前に設定したら監視が始まりませんでした。
感想
browserSync
で検索すると大体はgulp
と一緒に使おうみたいな記事が見つかります。設定ファイルの記述方法を覚えるのが大変なので、gulp
はやらなくていいかなと思い、自動更新に絞った使い方を調べました。
上記二つのケースどちらでも、browserSyncを起動すると管理サーバも立ち上がるのですが、その使い方があまりわかっていません。また「snipet
を貼り付けてね」みたいなことをbrowserSyncから注意されるのですが、これが何に使われているかわからないです。自動更新できているから、ひとまず放置しています。
追記: ブラウザ同士のイベントを同期することもできるみたいです。同一ネットワーク内だと同期されるのかな?
圏, 関手, 自然変換
圏論の定義メモ
Def.圏(category)
圏 は次のデータからなります;
- 対象(objects)
- , , ...
- 射(arrows)
- , , ...
- ドメイン(domain), コドメイン(codomain)
- 任意の射 に対し, 対象 , が定まる. この時 と表す. (ただし 、)
- 合成(composite)
- 任意の射 、に対し、 という射が定まる.
- 恒等射(identity arrow)
- 任意の対象 に対し, という射が定まる.
これらのデータは次の法則を満たします;
- 結合律(associativity):
- 任意の射 , , で, を満たすものに対し, が成り立つ.
- 恒等律(unit):
- 任意の射 に対し,
Def.関手(functor)
圏 , に対し, 関手 とは, 圏 の対象を圏 の対象に, 圏 の射を圏 の射に写すもので, 次を満たします;
Def.自然変換(natural transformation)
圏 , , 関手 に対し, 自然変換 とは, 圏 の任意の対象 に対し, 圏 の射 を与えるもので, 次を満たします;
- 圏 の任意の射 に対し, が成り立つ.
感想
だからどうしたという感じですよね。写像だ、とか集合だ、とか限定すると間違ってしまうためぼんやりした定義になってしまいます。はてなの所為なのか可換図式書けませんでした...
PCのカメラを起動する
WebRTCで遊んでみたいので、手始めにPCの内臓カメラを起動するだけのページを作成します。
navigator
オブジェクトのgetUserMedia
メソッドを使用することでデバイスにアクセスできるみたいです。ただ、ブラウザ環境ごとにこのメソッドがあったりなかったりするため、代替となる関数を列挙してそのうちあったものを使用する、というコードを書きます。
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || window.navigator.mozGetUserMedia || navigator.msGetUserMedia;
メソッド自体はこんな形をしています。
navigator.getUserMedia(constraints,successCallback,errorCallback)
次にこのAPIを使用してメディアに接続します。
navigator.getUserMedia({video: true, audio: true}, stream => { video.src = window.URL.createObjectURL(stream); }, error => { console.error(error); return; });
あとは適当にhtmlをかいた、ら動くと思ったのですが、エラーも吐かずになぜか動かない。
そういえばwebサーバに置かないとダメ、みたいなことを聞いた気がしたので、nodeでexpressを使ってサーバを立てました。
mkdir webrtc cd webrtc npm init -y npm install express
二つのファイルを作成します。
server.js
var express = require('express'); var app = express(); app.get('/', function (req, res) { res.sendFile(__dirname + '/index.html'); }); app.use(function(err, req, res, next) { console.error(err.stack); res.status(500).send('Something broke!'); }); app.listen(3000);
index.html
<html> <head> <title>sample</title> </head> <body> <video id='video' autoplay></video> <script type="text/javascript"> let video = document.querySelector('video'); navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia; navigator.getUserMedia({video: true, audio: true}, stream => { video.src = window.URL.createObjectURL(stream); }, error => { console.error(error); return; }); </script> </body> </html>
あとは起動してlocalhostの3000ポートにアクセスします。
node server.js
なんか暗いですが映りました!
感想
getUserMediaがPromise化対応したそうですが、chromeはダメみたいです。
近々redux
のコンポーネントとして、動画の録画、再生、停止、保存機能を持った部品を作成したいです。
あと通信部分で、同一LAN内の携帯とPCとかならビデオチャットできないだろうか?と考えています。
オーダー
オーダーについて。適当に済ましていたので、おぼえがきします。アルゴリズムでも数学でも見ることがよくありますが、若干うろ覚えです。
定義
が のときオーダーである。
これを変形すると、 となることから、この定義は は より発散する速度が遅いということを意味しているのがわかります。この時 は で抑えられる、と言ったりするようです。
これに対し似た記法で、というものもあります。この時、以下が成り立ちます。
詳細な定義は省略しますが極限が0になることが違います。 は より発散する速度が、 の時より、十分遅いというイメージです。この時 は に比べ無視できる、と言ったりするようです。例えば、写像 が で全微分可能とは、
です。ここでは となっていて多少変化がありますが基本的に意味は変わっていません。つまり、 と は のノルムが十分小さいところでは限りなく等しいということが言えます。
アルゴリズムなどではよく、、 などと言ったりしますね。これらは「入力データ量 に対する実行時間は 」みたいな文脈だったと思います。これはつまり、
より、
この場合はアルゴリズムの実行時間が に比例する、ということですね。 なども同様です。
感想
わかったようなわかってないような微妙な気分です。当たり前のことを言っている気持ちにもなりました。
まあ少なくとも毎回ググらなくてよくなっただけ、進歩かと思います。
javascriptで実行時間を表示する
短いメモです。
console.time
、console.timeEnd
を使うとスクリプトの実行時間を表示することができます。
var loop = n => { console.time('timer'); for(i = 1; i <= n; i++) { for(j = 1; j <= n; j++) { console.log(`${i} x ${j} = ${i*j}`); } } console.timeEnd('timer'); }
引数で与えている文字列を使って、スタートとエンドを対応させます。
ここを参考にしました[JavaScript]使い分けるだけで今すぐデバッグ効率を上げる、consoleオブジェクトの関数 - Qiita。console
オブジェクトの関数がたくさん書かれています。
ReduxとExpressかElectronか
Reduxのチュートリアルを見終わったので、実際に手を動かしてみようと思い、リポジトリを作成してみました。中身はExample: Todo List | Reduxです。
express
とelectron
で動作確認ができます。
感想
写経しているとredux
に関して徐々にわかってきました。redux
は画面状態をStore
内のstate
オブジェクトとして持っていて、操作によって発行されるaction
オブジェクトとstate
をセットで、Reducer
に渡すことで、新たなstate
を取得します。
今回の登場人物は、
- Component(presentation, container)
- Action Creator
- Reducer & Store
処理の流れもこの順番です。
画面はコンポーネント(Component
)のツリー構造で表現されます。コンポーネントにはPresentation Component
とContainer Component
の二種類があり、Presentation Component
は構造を、Container Component
は状態を変化させる方法を定義します。単独で部品としても使えますが、Container Component
で定義した状態を変化させる関数を、react-redux
ライブラリに含まれるconnect
関数を使って、Presentation Component
に結びつけて使用したりもできます。また、このconnect
によってStore
とComponent
の結びつけも行なっているようです。(ちょっと調べないと、です)
また、コンポーネントのツリーの頂点にProvider
というreact-redux
ライブラリに含まれるコンポーネントを追加しているので、ここが状態をツリーの枝に伝える仕組みを提供しているのかもしれないです。
特定の動作に対して発行するAction
をAction Creator
で定義しておきます。これは最低限type
というプロパティを持ったオブジェクトを生成すれば問題ないようです。
Reducer
がStore
を定めているのだろうという認識で、この二つは同じ行に書いています。(実装的な視点、ですかね)Reducer
は古いstate
と、発行されたaction
を引数に新しいstate
を返します。redux
ライブラリのcreateStore
関数によってReducer
からStore
が生成されます。
いろいろ書きましたが、それぞれの部品はわかるものの、連結部分がわかりにくいという感想です。
知らなくてはいけないのはreact-redux
のProvider
、connect
によっていかにコンポーネントとStore
を結びつけているか、という部分ですかね。
Reduxの勉強3
引き続き、Reduxのレッスン動画を視聴した時のメモです。
- 21: ToDoアプリのリファクタリング。見た目と挙動を分けます。Main container componentからPresentational componentを分離しましょうというはなし。
- 22: container componentは振る舞いを定義するクラスとして作成した。
conponentDidMount
、conponentWillUnmont
メソッドを定義し、presentational componentは状態に対する見た目だけを定義する。のかな? - 23: Storeと結びついているコンポーネントは
forceUpdate
する?render
を全体のコンポーネントから分割したコンポーネントに行わせるように修正した。(しかしAddToDoコンポーネントにはrenderがないようだが)厳密にPresentationとContainerを分割する必要はないが、したほうがいいと言ったことを言っている気がする。。。
(だんだんきつくなってきました。。。)
- 24: StoreにアクセスするコンポーネントがContainerコンポーネントみたいだ。子のContainer ComponentにStoreを渡すために一旦storeをトップレベルの引数として渡す処理を書いた。
- 25:
Provider
を使うことでPresentation ComponentからStoreを取り除くことができるみたいだ。ContextによってContainer ComponentにStoreを渡すことができるようになった。Contextを使うにはchildContextTypesを指定しなければいけない。グローバルにアクセスできる感じか。ただしContextはあまり安定していないらしく、がっつり使っているのは良くないとのことです。 - 26:
Provider
はライブラリとして取得できるよ、という説明と記述方法でした。 - 27:
ReactRedux
ライブラリのconnect
メソッドを使うことでContainer Componentを作成することができる。最終的にどうなるか知れればいいかなという気分になってきました。 - 28, 29:
connect
メソッドを使って色々整理していきます。 - 30: ActionCreaterについて。
だいぶ聞き取れず、試すこともできず、停滞しています。 後日初めから、アプリを作成しつつ追っかけていこうと思います。。。
webpack、babel、React
Reactの環境構築です。できるだけ覚えることを減らしたいので、webpack
、babel
、react
を軸に必要最低限なものをインストールします。Node.js
の環境が整っている前提です。(npm
コマンドが使えればOKかと思います)
それぞれに関するメモです。
webpack
: jsxとかes6記法で書いたスクリプトとかをクライアント側に送るためにまとめるビルドツールという認識です。モジュール間の依存性を解決しつつビルドしてくれるそうです。チュートリアルやったらわかった気になれました。loader
というモジュールを追加することで、jsファイルだけでなくcssなどのまとめ方も指定できます。babel
: es6記法を一般的なブラウザで読めるようにコンパイルするもの。webpackでes6記法を含んだファイルをビルドする際に、エンジンとして使いました。インストールのみで特に何もしていないです。react
: jsのテンプレートエンジンです。jsxとして書くのが楽なので書くと、そのままではブラウザで動きません。なのでwebpackで普通のjsファイルにビルドさせます。
設定
パッケージファイルを作成し、必要なパッケージをインストールします。
npm init -y npm install webpack babel-loader babel-core babel-preset-es2015 babel-preset-react react react-dom --save-dev
次はwebpack
の設定です、設定ファイルを記述しない場合webpackにいろいろオプションを指定して実行することになりますので、ファイルwebpack.confing.js
を作成します。
webpack.config.js
module.exports = { entry: "./entry.js", output: { path: __dirname, filename: "bundle.js" }, module: { loaders: [ { test: /\.(js|jsx)$/, exclude: /node_modules/, loader: "babel", query: { presets: ['es2015', 'react'] } } ] } };
このファイルにwebpackコマンド実行時の設定をオブジェクトとして記述していきます。プロパティの説明をします。
entry
がビルドしたいスクリプトになります。この例ではentry.js
と設定しています。内部でrequire
とか呼び出ししているファイルは、webpackが勝手にまとめてくれます。output
には出力ファイルのパスと名前を記述します。ディレクトリ構造を考えていないので特にパスは指定してません。module
のloaders
という配列に、どのファイルをどのようにビルドするかの設定をオブジェクトで記述します。今回は一つだけです。test
にはどのファイルを対象にするかを正規表現で記述します。今回は拡張子がjs
またはjsx
のものを対象にします。exclude
には除外するもののパターンを記述します。loader
はビルドするエンジンを指定しているのだと思います。loader
に機能を追加するのがpreset
なんだろうか。presets
はes6記法とreactに関するものを指定しました。
次に、順番が前後していますが、entry.js
とReactのコンポーネントと動作確認用のindex.html
を作りましょう。
entry.js
window.onload = () => { require('./Hello.jsx'); }
Hello.jsx
const React = require('react'); const { render } = require('react-dom'); const Hello = React.createClass({ render: function() { return ( <div className="hello"> Hello, world! </div> ); } }); render( <Hello />, document.getElementById('root') );
index.html
<html> <head> <meta charset="utf-8"> </head> <body> <script type="text/javascript" src="bundle.js" charset="utf-8"></script> <div id="root"></div> </body> </html>
あとはコマンドを叩くだけです。
./node_modules/.bin/webpack
実行完了したらindex.html
をブラウザで確認してみてください。Reactっぽさはないですが、レンダリングされているのが確認できると思います。
必須ではないですがコマンドの実行はpackage.json
のscripts
に記述しちゃうと楽です。
package.json
{ "name": "webpack-tutorial", "version": "1.0.0", "description": "", "main": "bundle.js", "dependencies": { "webpack": "^1.13.1", "css-loader": "^0.23.1", "style-loader": "^0.13.1", "babel-core": "^6.10.4", "babel-loader": "^6.2.4", "babel-preset-es2015": "^6.9.0", "babel-preset-react": "^6.11.1", "react": "^15.2.0", "react-dom": "^15.2.0" }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "webpack": "webpack --progress --colors" }, "author": "", "license": "ISC" }
以下で起動できるようになります。
npm run webpack
以上です。コードはgithubに上げました。リポジトリ名がチュートリアルとなっていますが全然チュートリアルではないですが。。。
感想
ビルドツールのノリが少しわかりました。ビルドツールが自分にとって身近ではないのは、頻繁にビルドしないからに尽きるんでしょうね。
ともあれようやくReactが動いたので、Reduxのチュートリアルを試すことができます。
Reduxの勉強2
動画が30分で終わるかと思ったら30回あることに気がついて力尽きてしまったので、今日はその残りを見ていきます。
Webpackを使えるようになろうとか言っていましたが、いまの腰を据えて試せていないので停滞しています。残りの動画を放っておくとちょっと分かった気になっているので見なくなる可能性があります、こちらを優先させていただきます。
- 11: todos Reducerをテストファーストで書いた。actionにidがふってあるが、どこまでふったか、stateかどこかに数字を控えなければいけないのだろうか?(後の動画でグローバル変数として保持しているのがわかる)
- 12: todos Reducerに「ADD_TODO」だけではなく「TOGGLE_TODO」メソッド(case?すくなくともメソッドではない)を追加。副作用なくすためにしっかりテスト書かないとダメ。ちょっと性善説で悩む(でもみんな責任感あるし大丈夫かな)。
- 13: Reducerの分割。stateのリストを受け取るReducerの内部を、単一のstateを受け取るReducerに切り出す。どんどんReducer同士を結合させちゃえてきな英語を喋っていた気がする、この辺りは副作用のない関数であることが不安を取り去ってくれてますね。想定していないtypeのActionに関してはそのままstate返しましょう、という念押し。
- 14: Reducerの合成だが、ちょっと難しい。todosとは別のvisibilityFilter Reducerを追加し、それらを包含したtodoApp Reducerを作成。todoApp内部で、各Reducerへの振り分けを行なっているみたい。なんかStoreとReducerの関係性が曖昧になってきた。。。ActionをStoreに
dispatch
した際に、入れ子になったどのstateに対するActionか指定していないが、これは対象としていない方にundefinedが渡るからシカトしてOKということか?なんか違和感。 - 15: Reducerの合成は
combineReducers
を使うと楽と言っている。またes6の記法で定義した変数a, b, c
をobj = {a,b,c}
と書けばオブジェクトのkey valueが勝手に入るらしい。「object literal shorthand anotation」だそうです。 - 16:
combineReducers
の実装を書いて説明してくれる。reduce
で少し詰まった。多分、foldl
とかと同じでいいんですよね。配列の各要素を使って一つのものをどんどん作っていくイメージをしました。 - 17: Reactを使ってToDoの「ADD_TODO」機能のユーザインタフェースを作った。また、入力から出力までのReduxの(?)ライフサイクルを説明してくれているので、よくわからなくなったらこれを見るといいかも。
- 18: 「TOGGLE_TODO」機能をUIから使えるようにした。全体的に言えるのはどの関数も、基本的には全てのstateを総なめして一致したら処理を実行する感じみたいだ。
- 19: 「visibilityFilter」のUIを作成。画面側の機能とReducerは対応していないという当たり前のことに気がつく。filterをかける処理などは通常の関数で実装しているが、もし自分が作った時にその辺間違えそう。画面表示とstateの違いをちゃんと整理しておかないと危険ですね。
- 20: ToDoアプリのリファクタリング。Reactコンポーネントの分離について。分離の基準がよく理解できない。イベントハンドラのコールバック関数はどちらが持つべきか等。分離の途中で次回。
感想
本当は今日中に30まで見てしまって、手を動かす時間を取りたかったのですが、眠気が襲ってきてしまったのでこれで終了です。
動画の中で、コンポーネント内でinputの値を取得するために、react callback ref api
を使っていた。常識っぽいので知っておきたいです。
あと、markdownのlistはoffsetが効かないため困りました。今日は動画を11から見始めたので、
11. hogehoge 12. hogehoge ...
と書いていたのですが、画面上では
1. hogehoge 2. hogehoge ...
になってしまい、ちょっと面倒さを感じながら*
をかませて対応しました。
JavaScript...
ES6から、「Spread operater」というものが追加されました。「...」を記述することで、配列やオブジェクトを展開する事ができる演算子だそうです。 例えば、
var [x, ...xs] = [1,2,3,4,5,6,7,8,9]; console.log(x); //=> 1 console.log(xs); //=> [2, 3, 4, 5, 6, 7, 8, 9]
分割代入との合わせ技だと思いますが、このように余った部分を配列で受け取ってくれます。
可変長引数等で使えますね。
サンプルコードを書いてみました。 比較方法が数値しか対応していなくて残念ですが、比較の優先順位を可変長で受け取るソート関数を書いてみました。
function mySort(array, ...properties) { if(properties.length <= 0) return array; let comp = (a, b) => { let res = 0; for(i = 0; i < properties.length; i++){ let property = properties[i]; if(match = /^-(.*)$/g.exec(property)) property = match[1]; let sign = (match) ? -1 : 1; if(!(property in a)) throw new Error(`There is no property named ${property}`); res = sign * (a[property] - b[property]); if(res !== 0) break; } return res; } return [...array].sort(comp); } var objs = [ {id: 0, a:1, b:1, c:1, d:1}, {id: 1, a:1, b:1, c:1, d:2}, {id: 2, a:1, b:1, c:2, d:1}, {id: 3, a:1, b:1, c:2, d:2}, {id: 4, a:1, b:2, c:1, d:1}, {id: 5, a:1, b:2, c:1, d:2}, {id: 6, a:1, b:2, c:2, d:1}, {id: 7, a:1, b:2, c:2, d:2}, {id: 8, a:2, b:1, c:1, d:1}, {id: 9, a:2, b:1, c:1, d:2}, {id:10, a:2, b:1, c:2, d:1}, {id:11, a:2, b:1, c:2, d:2}, {id:12, a:2, b:2, c:1, d:1}, {id:13, a:2, b:2, c:1, d:2}, {id:14, a:2, b:2, c:2, d:1}, {id:15, a:2, b:2, c:2, d:2} ]; console.table( mySort(objs, '-a', 'b','c') );
配列への要素の追加も副作用なく簡単にかけます。
function mutableExclamate(list) { list.push('!'); return list; } function immutableExclamate(list) { return [...list, '!']; } args = ['y','a','y']; console.log(args); //=> ["y", "a", "y"] console.log(mutableExclamate(args)); //=> ["y", "a", "y", "!"] console.log(args); //=> ["y", "a", "y", "!"] args = ['y','a','y']; console.log(args); //=> ["y", "a", "y"] console.log(immutableExclamate(args)); //=> ["y", "a", "y", "!"] console.log(args); //=> ["y", "a", "y"]
そんなに想像力が働かないですね。 ただArray.prototypeを触らなければならない場面は減ると思います。
Reduxの勉強
Redux
ここを一通り読めばわかりそうだ!と思って読み始めたら、いきなりプレディクタブルステートコンテナとか言われてよく分かりません。
Redux is a predictable state container for JavaScript apps. Read Me | Redux
ステートは普通に状態でいいのだろうか?あまり知らないため、用語を使っていたらわけわからなくなると思いながら、とりあえず読み進めることにしました。
30分の動画があるためそれを見る。なんか10個くらいあるようです。(間違ってました、後述)
- ToDoアプリを用いて、画面の状態が一つのオブジェクト(State Tree)に対応していることの実演。
- State Treeはリードオンリーで、操作(type)とデータを持ったオブジェクト(Action)によって変更される。
- Pure FunctionとImpure Functionの違い。副作用がないこと(戻り値を返す以外の方法で出力を行わないことかな?)
- StateとActionを受け取り新しいStateを返すのがReducer、これはPure Functionになっている。だから同じStateとActionを入れれば同じStateが必ず得られることが保証される。(ちょっと英語怪しくなってきました...)
- 数字を増やしたり減らしたりするだけのCounter Reducerを例に、ReducerとTestの説明をしている。
expect(...).toEqual(...)
って感じで書いている。またReducerの実装上の注意など説明されていた(未定義なtypeのAction、stateのデフォルト値など) - Reduxの導入と、Storeのもつメソッドの説明。ReduxのcreateStoreメソッドによってReducerを引数にStoreというものが作られている。StoreはsubscribeメソッドによってViewの書き換えを行なったり、stateを保持しているようだ。StoreにActionをdispatchメソッドによって食べさせることで保持しているstateが更新されている。こいつは副作用を担っているっぽいです。
- createStoreの実装に関する説明。getState、dispatch、subscribeの三つを提供してくれるがこれらが何をしているかを説明してくれた。subscribeがあまりわからない...内部に持つlisteners配列を更新するが、filter処理はなにをしている?
- renderにはReactを使おう!その際はコールバックを渡すのがいいよ、という説明。おそらく先ほどのsubscribeのおかげでStoreが変更を反映してくれているんだろうという程度の理解。
- テストにdeepFreezeを使ってイミュータブルな処理をちゃんと書こうという話かな?es6の記法(配列
list
に対して...list
を使うことで中身を展開できる)をつかった小粋な書き方とか勉強になりました。 - 引き続きテストの話。
Object.assign
とes6...object
の記法の説明。自分の英語力だと3点リーダの記法がいいように聞こえたんだけどどうなんだ?
いまさら気がついたのですが、この動画30分ではなく30個の動画だったのですね...
30見ていると何も達成感がないのでとりあえずまとめとしてカウンターアプリを作ってみよう。
と思ったのですが、環境構築がうまくいかずに断念。webpackを勉強しないといけない。
感想
ビルドツールになぜか苦手意識があることが判明しました。なぜ嫌なのだろうか?webpackを調べつつ、Redux触っていきたいです、あわよくばElectronでアプリ作りたいです。
javascriptで波括弧を使ってconst値に分割代入
こんにちは。
クロスプラットフォームのデスクトップアプリがhtml、css、javascriptで作れてしまうフレームワーク「Electron」をちょっと触ってみようと思ったのですが、「Hello, world」する前に気になったことをメモします。 (Electronとはあまり関係ない内容になっています)
疑問
とりあえずElectronの環境を整えました。
mkdir myapp npm init -y cd myapp npm install electron-prebuilt --save-dev
あとは、Quick Start - Electronを参考に進めていきました。
ウィンドウを表示したり、閉じた時の挙動を制御するシステム系の設定をindex.js
(package.json
のmain
プロパティで指定したファイル)に記述するのですが、いざコピペしてやろうと思ってサイトの内容を読んでいるといきなり3行目に以下の記述を見かけました。
const electron = require('electron'); // Module to control application life. const {app} = electron; ...
const
は定数定義で、ES6から使えるらしい、ということは知っていたのですが、
この波括弧{app}
はなんなんだ?と思いました。
知らないと恥ずかしいのかもしれないので、いろいろ試してみました。
(以下ごちゃごちゃしています。結論はタイトルに書いてあります)
スコープ
とりあえずconst
は関係ない、スコープの問題だろうと当たりをつけて以下のコードをchromeのコンソールで実行しました。
{hoge} = {hoge:'hogehoge'}; console.log(hoge);
確かにこれでもhogehoge
がコンソールに出力されます。
const
がどうとかではなく、左辺に波括弧の中にオブジェクトのプロパティを記述すると、右辺のオブジェクトが持っているプロパティの値を取り出すことができるという動きです。
左辺の波括弧の中のhoge
が外側から参照できること、左辺のhoge
に右辺の値が入っていることについて考えなければと思い、javascriptのオブジェクトの代入に関して調べてみました。
javascriptにおいてはオブジェクト型の代入は参照渡しになる、ということを聞いてもしかすると、と考え以下のコードを試してみました。
obj1 = {p1:{c:1}}; p1 = obj1.p1; p1.cc = 2; console.info(obj1); console.info(p1); obj2 = {p2:{c:1}}; var {p2} = obj2; p2.cc = 2; console.info(obj2); console.info(p2);
二種類の方法を試したのですが違いはありませんでした。 もしかすると波括弧で囲えば値渡しになるとか考えたのですが、それは間違っていました。。。
感想
そもそもなんでこう書かれているかを探るために、Electronのドキュメントのgithubリポジトリをみてみました。
この記法に変更された時のプルリクエストを見ると、Destructing assignment
というコミットメッセージを発見しました!
ググってみると次のページが見つかります。
分割代入、という構文だったんですね、初めて知りました。
今日はここにたどり着くまでで消耗してしましました...
gnuplotとFFTW
勉強のため、gnuplot(gnuplot homepage)とc言語のFFTW(Fastest Fourier Transform in the West.)ライブラリの環境を作ってみます。
gnuplot
gnuplotはコマンドラインから使えるグラフィックユーティリティです。科学者や学生向けに数学の関数をヴィジュアル化する目的で作られました。
homebrewでインストールします。こちらを参考にしました。(gnuplotをHomebrewからインストールするときの手順)
$ brew install gnuplot --with-aquaterm
aquatermはGUIです。gnuplotの出力を表示するために、インストール時点でオプションを追加します。
インストール完了後gnuplot
コマンドで対話環境が起動します。まずはaquatermでsin(x)
を表示してみましょう。
gnuplot> set terminal aqua gnuplot> plot sin(x)
出力方式、出力先を設定することで、画像ファイルやhtmlのcanvasとして吐き出すことができます。
gnuplot> set terminal png gnuplot> set output 'sin_x.png' gnuplot> plot sin(x)
関数をデータとして吐き出したい場合は、
gnuplot> set table "gnuplot.sample" gnuplot> set yrange [0:1] gnuplot> set xrange [-5:5] gnuplot> plot exp(- x * x / 2) / sqrt(2 * pi) gnuplot> unset table
とすると、gnuplot.sample
ファイルに関数の値が書き込まれます。
# Curve 0 of 1, 100 points # Curve title: "exp(- x * x / 2) / sqrt(2 * pi)" # x y type -5 1.48672e-06 i -4.89899 2.45106e-06 i -4.79798 3.99989e-06 i -4.69697 6.46117e-06 i -4.59596 1.0331e-05 i -4.49495 1.6351e-05 i -4.39394 2.56161e-05 i -4.29293 3.97238e-05 i -4.19192 6.09759e-05 i -4.09091 9.26476e-05 i -3.9899 0.000139341 i -3.88889 0.00020744 i -3.78788 0.000305686 i -3.68687 0.00044589 i -3.58586 0.000643795 i -3.48485 0.000920105 i -3.38384 0.00130165 i -3.28283 0.00182273 i -3.18182 0.0025265 i -3.08081 0.00346644 i -2.9798 0.00470779 i -2.87879 0.00632878 i -2.77778 0.00842153 i -2.67677 0.0110926 i -2.57576 0.0144624 i -2.47475 0.0186646 i -2.37374 0.0238433 i -2.27273 0.0301496 i -2.17172 0.0377369 i -2.07071 0.0467541 i -1.9697 0.057338 i -1.86869 0.069604 i -1.76768 0.0836362 i -1.66667 0.0994771 i -1.56566 0.117117 i -1.46465 0.136486 i -1.36364 0.157443 i -1.26263 0.179775 i -1.16162 0.20319 i -1.06061 0.227324 i -0.959596 0.251742 i -0.858586 0.275953 i -0.757576 0.299423 i -0.656566 0.32159 i -0.555556 0.341892 i -0.454545 0.359787 i -0.353535 0.374774 i -0.252525 0.386423 i -0.151515 0.394389 i -0.0505051 0.398434 i 0.0505051 0.398434 i 0.151515 0.394389 i 0.252525 0.386423 i 0.353535 0.374774 i 0.454545 0.359787 i 0.555556 0.341892 i 0.656566 0.32159 i 0.757576 0.299423 i 0.858586 0.275953 i 0.959596 0.251742 i 1.06061 0.227324 i 1.16162 0.20319 i 1.26263 0.179775 i 1.36364 0.157443 i 1.46465 0.136486 i 1.56566 0.117117 i 1.66667 0.0994771 i 1.76768 0.0836362 i 1.86869 0.069604 i 1.9697 0.057338 i 2.07071 0.0467541 i 2.17172 0.0377369 i 2.27273 0.0301496 i 2.37374 0.0238433 i 2.47475 0.0186646 i 2.57576 0.0144624 i 2.67677 0.0110926 i 2.77778 0.00842153 i 2.87879 0.00632878 i 2.9798 0.00470779 i 3.08081 0.00346644 i 3.18182 0.0025265 i 3.28283 0.00182273 i 3.38384 0.00130165 i 3.48485 0.000920105 i 3.58586 0.000643795 i 3.68687 0.00044589 i 3.78788 0.000305686 i 3.88889 0.00020744 i 3.9899 0.000139341 i 4.09091 9.26476e-05 i 4.19192 6.09759e-05 i 4.29293 3.97238e-05 i 4.39394 2.56161e-05 i 4.49495 1.6351e-05 i 4.59596 1.0331e-05 i 4.69697 6.46117e-06 i 4.79798 3.99989e-06 i 4.89899 2.45106e-06 i 5 1.48672e-06 i
ここでは一列目がx軸、二列目がy軸になっています。三列目はちょっと謎...
このデータを使ってグラフを表示するのも簡単にできます。
gnuplot> plot "gnuplot.sample"
FFTW
FFTWは離散フーリエ変換を計算するためのc言語のライブラリです。変換する関数は次元、入力サイズ、実数or虚数別が選べます。
$ brew install fftw
FFTWには離散フーリエ変換を扱うための関数や複素数データ型が用意されています。
FFTW does not use a fixed algorithm for computing the transform, but instead it adapts the DFT algorithm to details of the underlying hardware in order to maximize performance.
FFTWでは入力データに対して、毎回決まった変換アルゴリズムを適応するのではなく、最適化した変換アルゴリズムを用いて離散フーリエ変換を行う仕組みになっています。そのため、変換実行前に「プラン」というものを作成します。複素一次元離散フーリエ変換のプランを作成する関数を見てみましょう。
fftw_plan fftw_plan_dft_1d(int n, fftw_complex *in, fftw_complex *out, int sign, unsigned flags);
fftw_plan_dft_1d
はfftw_plan
を戻り値にもつ関数です。引数として次のものをとります;
int n
: データサイズfftw_complex *in
:入力データ配列fftw_complex *out
:出力データ配列int sign
:フーリエ変換(-1)、逆フーリエ変換(+1)かの設定。定数が用意されています。unsigned flags
:通常はFFTW_ESTIMATE
をつかい、サイズが同じ変換を何回もし、初期化コスト(?)を気にしないならばFFTW_MEASURE
を使うみたいです。
感想
力が尽きてしまい、フーリエ変換前後のグラフを出力することは断念しました。sinをフーリエ変換して、一本だけ立つことを確認するとか、関数の和がどんどん短形関数に近づいていく様をみるとか、やりたいです。
フーリエ変換の練習(前半)
フーリエ変換について知識がないため、ちょっと勉強してみます。証明的なものを載せていますが、考え方を学ぶためのメモなため正確なことを知りたい場合参考書を当たってください。フーリエ変換の雰囲気がわからなくて困っている場合はちょっとは役に立つと思います。(私も同じ状況なので)
フーリエ変換は関数を周波数で分解した表示に変換できるものです。分解の際に現れる各項は連続関数と定数の積ですが、これらの無限の和を考えることで、不連続な関数に関しても分解が得られるということが面白と思いました。通信関係をはじめとして、広い範囲での応用がされています。
元となった理論は、フーリエ級数の研究なのかと思います。フーリエ級数は周期関数に関する表示を与えるものでしたが、フーリエ変換では周期を無限と考えることで、その対象を広げたところが違いです。
考え方の核は、周期を無限と考えることと、関数空間に対して基底による分解を与える感じでしょうか?一部フーリエ級数と変わらないですが。
前提条件としては、微分積分、線形代数(基底とかの話)、複素数(というかオイラーの公式)を知っていれば理解できそうです。
離散フーリエ変換など応用の方面でまだまだ研究されているようで2012年には従来の10倍の変換速度を誇るSFFTといったものも発表されたみたいですね。。。
ということで、数学の勉強方法ってどうするんだろう、としばらく悩んだのですが、ああ、証明を考えてみるのが一番理解できるな、と思い出しました。理解のためにまずはフーリエ級数を証明してみます。なので周期関数の空間について考えます。ちゃんとした参考書みないので記号は適当です、うまくいかなかったらすみません。
周期関数
周期 の関数の空間を と定義します。
内積は、 で定義します。(内積であることは積分の性質からいけるはず)
の基底が、1(定値関数)、、 になることを示せれば、任意の周期関数 は、
という形で表せることがわかります。
証明
前半
基底であることを示すには、一次独立と生成系であることを言わなければいけません。
まずは一次独立を示すために、それぞれの基底候補が直交していることを示します。
より がわかりました。 も同様です。
次に、 と と、 ですが、これはオイラーの公式を使うといけそうです(、 の積の公式を忘れてしまいました...)。
以下の等式が成立します。(記述が面倒なので、 とします。)
一方、
なので、
が成り立ちます。さらにの符号を変えた、 から、
よってこれを使うと、
つまり、、、 は直行することがわかります。
以上のことから、
に対して、、、 をそれぞれかけて積分したものを考えれば、
、 、 がわかります。
感想
ちょっと元気が無くなってしまい、今回は前半だけです。いろいろ抜けがあると思います、後半の生成系であることを示す方針は全然思いついてないです。ただ関数空間の条件に可積分が必要になりそうですね(多分仮定されてたはず)。
あと、はてなの環境では[tex: ... ]
の内部で&が使えないため等式を複数段並べる時とか面倒です。なにか方法があるのでしょうか?