dockerに関するメモ
golang
を少しだけ試して見たくなったけれど、環境を作りたくなかったのでdocker
を使って見ました。特に目新しい内容ではないですが、自分用にメモします。
構成というほどでもないですが、
- コーディング: 自分のマシン
- 実行環境: コンテナ
とするために、まずはディレクトリを作成し、そのディレクトリをコンテナと共有して起動します。
mkdir ~/my_golang_src cd ~/my_golang_src
docker run -i -t --rm -v `pwd`:/my_golang_src golang /bin/bash
シェルが開くので、/my_golang_src
に移動してgo run
などでソースを実行できるようになります。
redisのレプリケーション
redis
のレプリケーションがとても簡単だったのでメモです。勉強も兼ねてdocker
を無駄に使っています。環境は以下です。
とりあえずredis
が入ったAlpine Linux
のイメージを作成します。my-alpine
ディレクトリを作りその中にDockerfile
を以下の内容で作成します;
FROM alpine:latest MAINTAINER ushumpei RUN apk --no-cache add redis CMD ["/bin/sh"]
$ docker build -t my-alpine . $ docker run -it --rm my-alpine
redis
がインストールされた状態でAlpine Linux
が起動しました。
単一マシン上で
先ほどのmy-alpine
を使って、同一ホストで複数のredis
を起動し、master、slaveのレプリケーション構成を行ってみます;
/ # redis-server --port 3679 & / # redis-server --port 3680 --slaveof localhost 3679 &
本当はちゃんと設定ファイルを書いたほうがいいんだと思います。レプリケーションを確認すると;
/ # redis-cli -p 3679 INFO ... # Replication role:master connected_slaves:1 slave0:ip=::1,port=3680,state=online,offset=519,lag=1 ... / # redis-cli -p 3680 INFO ... # Replication role:slave master_host:localhost master_port:3679 ... / # redis-cli -p 3679 set hoge fuga OK / # redis-cli -p 3680 get hoge "fuga"
とちゃんとなっているようです。
docker
docker-compose
でmaster: 1台、slave: 2台の構成を作ってみようと思います。新たにディレクトリ(docker-redis
にしました)を作成し、その中にdocker-compose.yml
を以下のように作成しました。
version: '2' services: master: image: redis:latest restart: always ports: - 3679:3679 slave_1: image: redis:latest restart: always ports: - 3680:3679 command: redis-server --slaveof master 6379 slave_2: image: redis:latest restart: always ports: - 3681:3679 command: redis-server --slaveof master 6379
slave_1
、slave_2
からはmaster
のhost
がmaster
で参照できるようになっているようで、分かるまで悩みました(参考: Compose のネットワーク機能)。docker-compose up
で起動します。
動作確認を先ほどのmy-alpine
から行います。--net
オプションで先ほどのdocker-compose up
で起動したコンテナたちのネットワークに参加します。こうすることで、master
などのホストを参照することができるようになります。
(ネットワーク名はデフォルトではディレクトリ名_default
になるらしく、docker network ls
でも確認できます。ディレクトリのハイフンが無視されています、どういうルールでしょう?)
$ docker run -it --rm --net dockerredis_default my-alpine / # redis-cli -h master INFO ... # Replication role:master connected_slaves:2 slave0:ip=172.19.0.4,port=6379,state=online,offset=2367,lag=1 slave1:ip=172.19.0.2,port=6379,state=online,offset=2367,lag=1 ... / # redis-cli -h slave_1 INFO ... # Replication role:slave master_host:master master_port:6379 ... / # redis-cli -h slave_2 INFO # Replication role:slave master_host:master master_port:6379 ... / # redis-cli -h master set hoge fuga OK / # redis-cli -h slave_1 get hoge "fuga" / # redis-cli -h slave_2 get hoge "fuga"
感想
redis
のレプリケーションってすごく簡単にできるように思えました。pub/sub
使って何か作ってみたいです。
¥eあるいはedit
mysqlの対話環境でちょっと長い処理を実行したくなった時のメモです。
mysqlコマンドを実行すると対話環境が起動します。ただ改行を含んだクエリを書こうとすると、どこか間違えた時に書き直すのが非常に面倒です。
そういった時は¥e
またはedit
コマンドを使うと便利です。コマンドを実行するとエディタが起動し直近に実行したクエリが表示されます。編集を行い保存して閉じるとコマンドを抜けて対話環境に戻ります。あとは;
を入力してエンターキーを押せば内容が実行されます。毎回エンターキーを押すのも面倒な場合は、¥e;
で実行します、この場合はエディタを閉じたら即時実行されます。
開くエディタは選べるようです。私の環境ではデフォルトはviでした。これは環境変数EDITOR
で設定できます。(export EDITOR=vim
とか設定しときました)
調べたいクエリがあった時に、
mysql> ¥e;
実行vim
が開くので挿入コマンド:a!
でコピーしてきたクエリをペーストする- 適宜修正して
:wq
でvim
を終了する - 結果を確認し、必要なら
1
に戻る
という感じで使っていました。
ちなみに¥e
を実行した時に編集しているファイルは/tmp
以下に作成されていることが確認できました、しかしこれはエディタを閉じると削除されるようです。ちゃんと取っておきたいなら保存した方がいいと思います。(vi
なら:w ~/hogehoge
など)
感想
(rails console
でもedit
コマンドが使えるみたいですirb
はダメか、コマンドが違うようです?)。他の対話環境ではどうなんでしょうか?
追記: 2017/01/13 - 今手元にあるrails 5
で試してみると、edit
コマンドは使えないみたいです。動作が確認できていた環境はすでに手を離れてしまったので再確認できません。。。何かわかり次第追記します。
git logの折り返し
gitのlog
やdiff
表示時の、文字列の折り返しの切り替え方をメモします。
自分の環境(mac 10.12
、terminal
、git version 2.8.4 (Apple Git-73)
)ではデフォルトで折り返しがされていたのですが、以下の指定で折り返しが無効になりました。
折り返し無効
$ git config --global core.pager "less -S"
折り返し有効
$ git config --global core.pager "less -r"
(デフォルトで有効だったので、折り返し有効のコマンドは未確認です。。。)
git log --graph --decorate
などでログを見たい場合は、折り返さない方が見やすい気がします。
ちなみにcore.pager
はgit log
やgit diff
で表示するときの出力コマンドを指定しているそうで、less
にS
オプションをつけて折り返しの制御が行われているようです。cat
とかも指定できるようです。more
は私の環境では文字化けしました。
アロー関数(Arrow function)の書き方色々
アロー関数は引数や戻り値の種類によって色々な書き方ができます。分割代入を使うことで柔軟な関数をシンプルに宣言できてとても気持ちいです。
基本的な書き方から、やや直感的ではない書き方まで、メモしていこうと思います。
ざっくり
アロー関数の記述方法は、引数に関しては、引数なし、引数1、引数N、引数オブジェクトリテラルによる分割代入、の4種類があります。関数の処理部分に関しては波括弧、括弧なし、丸括弧の3種類があります。
- 引数1、括弧なし
- 引数N、丸括弧
- 引数オブジェクトリテラルによる分割代入、丸括弧
- 引数なし、波括弧
を並べてみます。
引数1、括弧なし
引数が1のときは、引数の括弧は省略できて、右側が評価され戻り値が返ります。Promiseのcatchとかすっきりします。
const factorial = n => (n > 0) ? n * factorial(n-1) : 1;
引数N、丸括弧
引数N個は単純にfunctionのときと同じように並べるだけです。戻り値にオブジェクトリテラルを返したいときに丸括弧を使うとすっきりです。
let projection = (a, b) => ({ x: (2 * a) / (1 + a * a + b * b), y: (2 * b) / (1 + a * a + b * b), z: (-1 + a * a + b * b) / (1 + a * a + b * b), });
引数オブジェクトリテラルによる分割代入、丸括弧
上の例と近いです。ただ引数個数が頻繁に変わる可能性がある場合この記法がいいようです。ですが、個人的に好きなので何でもかんでもこれで書きたいです。Reactの純粋な関数スタイルのコンポーネントを書いたりするとき便利です。
const Deco = ({ deco, children }) => ( <div> {deco + children + deco} </div> );
呼び出すときは引数で代入しているプロパティをもったオブジェクトを渡せば大丈夫です。引数いっぱいなのは嫌だけど、オブジェクトまるまる渡したら何されるかわからない、といった不安を綺麗に取り去ってくれます。
引数なし、波括弧
グローバルなオブジェクトに対して何かしたいときに使う、気がします。波括弧なので複数行書くことができます。
const values = () => { const inputs = [...document.querySelectorAll('input')]; const values = inputs.map(input => input.value) return values; };
感想
戻り値に関して補足です。1行で済む場合は括弧なしか丸括弧、複数行必要なときは波括弧でいいと思います。
引数オブジェクトリテラルによる分割代入は、修正による影響を少なくできるし、オプションのようなものを渡すことが簡単にできるのでかなり好きです。
説明の中で複数行とか言っていますが、宣言とか式とか、ちゃんとした言葉をいい加減覚えなければいけないなと思います。
Reactコンポーネントを純粋な関数で書こう
Reactコンポーネントの書き方は色々あり、es6を使う場合の選択肢は以下の2つがあると思います;
- クラス
- 純粋な関数
クラス
classの構文を使う場合、React.Component
クラスを継承してコンポーネントを定義します。
class App extends React.Component { render() { return ( <div className="app-component"> {this.props.hello} </div> ); } }
純粋な関数
純粋な関数としてコンポーネントを定義する場合、JSX記法で書いたDOMを返すような関数を記述します。なかなか直感的に書くことができます、親コンポーネントから渡されるprops
も引数として宣言します。
const App = ({ text }) => ( <div className="app-component"> {text} </div> );
違いは?
ざっくり言うと状態(state
)を持つか持たないかのようです。クラス構文であればstate
を持ったコンポーネントを作ることができますが、純粋な関数のコンポーネントではできません。純粋な関数のコンポーネントは引数(props
)のみ使用し、実際それはコード上で関数の引数として表現されます。(2つの例でのtext
の扱いの違いのような感じです)
また純粋な関数のコンポーネントではref
が使えないそうです。その理由は説明によるとbacking instance
を持っていないから、だそうです。 静的 なコンポーネント、というイメージでしょうか。申し訳ないですが詳しくはこちらを参照くださいStateless functions
例:コンストラクタ内でstate
を定義する。
class App extends React.Component { constructor(props) { super(props); this.state = { ... }; } ... render() { ... } }
感想
redux
のチュートリアルで初めてStateless function
で書かれたReactコンポーネントを見ましたが、状態管理をredux
に任せる立場からするとそれは当然なんだろうな、と今になって思いました。
ReactのStatelessなコンポーネントを作ることは、新しいhtmlのタグを定義することに近い気がしました。
オブジェクトリテラル内でのスプレッド演算子
こんにちは。
スプレッド演算子(spread operator)がオブジェクトリテラル内で使えませんでした。babel-preset-es2015
入れとけばなんとかなるだろうと思っていたのですが、babel-preset-stage-2
が必要みたいです。ECMAScriptの仕様策定方法を知らないのでstage-2
の意味はわからないですが、なんかよさそうなものがあったので読もうと思います。
ざっくりとした設定方法をメモしておきます。
インストール
npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-stage-2 webpack
webpack.config.js
はこんな感じになりました。
module.exports = { entry: './index.js', output: { path: __dirname, filename: 'bundle.js', }, module: { loaders: [ { test: /\.js[x]?$/, exclude: /node_modules/, loader: 'babel', query: { presets: [ 'es2015', 'stage-2', ], }, }, ], }, }
以下動くかどうか確認のためindex.js
とindex.html
を用意。
index.js
let obj1 = { 'before': '...oh' }; alert(JSON.stringify(obj1,null,2)); let obj2 = { ...obj1 , 'after': 'oh!' }; alert(JSON.stringify(obj2,null,2)); // ひどいサンプル
index.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>Spread</title> </head> <body> <div id="root"></div> <script type="text/javascript" src="./bundle.js"></script> </body> </html>
webpack
でビルド実行
./node_modules/.bin/webpack --colors --progress
index.html
をブラウザで開くとアラートが2回表示されます。オブジェクトリテラルが再代入なしで更新されているのが、なんとなくわかるかと思います。もっとプロパティが多いと、魅力が伝わる気がします。
感想
ちゃんとした例を書かないとダメですね。あとECMAScriptに関しても知っておきたいです。
Ctags と vim と Git
概要
vimでタグジャンプを楽にする方法です。以下適当な翻訳です。
Ctags はコードのインデックスを作成し, 関数, 変数, クラス, その他の識別子への Vim でのジャンプを容易にします. Gitのフックはリポジトリ単位です(Git hooks). あるリポジトリに対して, 指定されたフックをインストールするスクリプトを使用するのが推奨されていますが, 手作業で面倒です. テンプレを作成して楽しましょう.
テンプレートのフック作成
git全体で使用するテンプレートディレクトリを設定, 作成します.
git config --global init.templatedir '~/.git_template' mkdir -p ~/.git_template/hooks
ctagsファイルを作成します.
vim .git_template/hooks/ctags
#!/bin/sh set -e PATH="/usr/local/bin:$PATH" dir="`git rev-parse --git-dir`" trap 'rm -f "$dir/$$.tags"' EXIT git ls-files | \ ctags --tag-relative -L - -f"$dir/$$.tags" --languages=-javascript,sql mv "$dir/$$.tags" "$dir/tags"
上の内容は,
set -e
: スクリプト内の各コマンドの実行でエラーが出たら処理を中止するPATH..
: 環境変数に/usr/local/bin
を追加dir...
: gitリポジトリのパスをdir変数に格納trap..
: EXITシグナルを検知したら, 古いタグ.git/tags
を削除する?git...
: git管理下のファイルに対しctagsを生成, jsとsqlは除くmv ...
: 生成されたタグをtagsディレクトリに移動
という感じです.
各フックファイルを作成します。pre-commit
は以下です。
#!/bin/sh .git/hooks/ctags >/dev/null 2>&1 &
post-checkout
, post-merge
も内容は一緒です.
最後に rebase
, commit --amend
の対応として post-rewrite
を作成します.
#!/bin/sh case "$1" in rebase) exec .git/hooks/post-merge ;; esac
これでいけるはずです。
結局5つのファイルを作成しました。
/Users/ushumpei/.git_template |--hooks | |--ctags | |--post-checkout | |--post-commit | |--post-merge | |--post-rewrite
感想
Ctagsについて昔書いた文章が見つかったので載せてみました。
trap
とgit rev-parse
がわかっていない...
Promiseのthenが素敵
javascriptではes2015からPromise
が使えるようになります。Promise
で非同期処理を包むことで、非同期処理が終わったタイミングで次の処理を開始できて、コールバック地獄をなくす、等と言われていたりします。
詳しい使い方は検索するとたくさん出てくるので割愛させていただいて、ちょっと気になったことを書いていきます。
以下、サーバへ非同期で通信し、json
データをもらって、表示するコードです。主な動きとしては;
- 2000ms待つ。
- サーバへ非同期なGETリクエストを送信。
- レスポンスのJSON文字列をオブジェクトに変換。
- オブジェクトの情報を加工して画面に描画。
です。
app/index.js
const wait = ms => { return new Promise((resolve, reject) => { console.log(`wait ${ms}ms...`); setInterval(_ => { resolve('OK!'); }, ms) }); } const fetchData = url => { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.onload = () => { if (xhr.readyState === 4 && xhr.status === 200) { resolve(xhr.response); return; } reject(new Error(xhr.statusText)); }; xhr.onerror = () => { reject(new Error(xhr.statusText)); }; xhr.send(null); }); }; const renderHoges = data => { const root = document.getElementById('root'); root.innerHTML = ''; data.forEach(item => { let node = document.createElement('div'); node.innerText = `${item.id}: ${item.hoge}`; root.appendChild(node); }); return data.length; }; wait(2000) .then(_ => fetchData('/api/hoges')) .then(JSON.parse) .then(renderHoges) .then(length => console.log(`${length} hoges rendered!`)) .catch(error => console.error(error));
app/static/index.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>Promise</title> </head> <body> <div id="root">loading...</div> <script type="text/javascript" src="bundle.js"></script> </body> </html>
サーバ側はここから見れます。
嬉しいなと思ったのは、then
を使うことで、もともとPromise
を考慮していないJSON.parse
やrenderHoges
などの同期処理をチェインできることです。
つまり明確に非同期な処理だけPromise
で書けばよく、チェインさせたいがために同期処理のPromise
版を書く必要がないということです。
感想
何当たり前のこと言ってんの?と思われるかもしれないですね。
困ったのは、Promise
から値を取り出す方法はあるのだろうか?、ということです。それを必要としている時点で処理の設計を間違えているのだろうか?とりあえず、外側で宣言した変数に再代入させる方法は上手くいきませんでした。redux
でサーバとやりとりする際にaction creator
の関数の中にPromise
を使った非同期通信を書いた時に、レスポンスの中身が取り出せない。。。
haskellのApplicative laws - composition
こんにちは。
すごいHaskellたのしく学ぼう!の11章に出てくるアプリカティブ則の一つ、「composition則」について何を言っているかを考えてみました。
composition則
Applicative型クラスを継承するデータ型は、関数を実装すること以外にも、実装した関数が満たさなければいけない規則が存在します。これを確認するのは設計者に委ねられています。Applicative型クラスでは4つの法則が存在します。ここではその4つのうちの一つ「composition則」(正式名称不明)について考えてみたいと思います。ちなみに4つの法則はBasic libraries
のリファレンスControl.Applicativeのページに載っています。
「composition則」は次で定義されています;
pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
なぜこの法則を満たさなければいけないのでしょうか。ここでは関数合成に対して、「アプリカティブファンクターに包まれた合成演算子(.)
を使って関数合成を行うことと、<*>
を使って関数合成を行うことは同じか?」ということが成り立って欲しいようです。単なる想像ですが、既存に存在している合成演算子(.)
と新しい合成演算子<*>
の間にある種の整合性を保っておきたいように見えます。composition則はそのことを確かめましょう、といっています。
composition則の定義を見ると、左辺がごちゃごちゃしています。適応の順を整理していきましょう。
pure (.) <*> u <*> v <*> w -- = ( pure (.) ) <*> u <*> v <*> w -- = ( ( pure (.) ) <*> u ) <*> v <*> w -- = ( ( ( pure (.) ) <*> u ) <*> v ) <*> w :
実際に適用して行ってみましょう。
-- u, v は以下; -- u :: Num a => Maybe (a -> a) -- v :: Num a => Maybe (a -> a) ghci>:t pure (.) pure (.) :: Applicative f => f ((b -> c) -> (a -> b) -> a -> c) -- まず合成演算子(.)がpureによってアプリカティブファンクターに包まれます。 ghci>:t pure (.) <*> u pure (.) <*> u :: Num c => Maybe ((a -> c) -> a -> c) -- 次に<*>で、アプリカティブファンクターに包まれた合成演算子の、 -- 第一引数の関数としてuを渡します。 ghci>:t pure (.) <*> u <*> v pure (.) <*> u <*> v :: Num c => Maybe (c -> c) -- 再び<*>で、アプリカティブファンクターに包まれた合成演算子の、 -- 第二引数の関数としてvを渡します。 ghci>:t pure (.) <*> u <*> v <*> Just 1 pure (.) <*> u <*> v <*> Just 1 :: Num b => Maybe b -- 最後に完成したアプリカティブファンクターに包まれた関数に対し、 -- <*>でアプリカティブ値を渡します。
左辺はこれで終わりです。右辺は<*>
で関数適用をチェインしているだけなので割愛です。
なぜ必要なのか?
これに関してははっきりとしたことはわかりませんでした。composition則が何を保証したいかは、結合演算子がどんな関数にも使えることに関係してくるのではないかと思うのですが自信はないです。調べたいと思います。
Vimで折畳
こんにちは。
Vimについて書きます。
概要
Vimでは文章を折りたたむことができるようです(Vi には折畳は無い)。htmlを編集している時など、ファイルが長くてちょっと読みづらいといった時にとても便利です。
Vimユーザマニュアルはとても素晴らしいので、ここにあれこれ解説を書く意味もないかと思います。なのでここでは、コマンドとその動きとかを軽く書こうと思います。Vimを起動した状態で:help fold
や:help usr_28.txt
を実行すると関連するドキュメントを読むことが出来ます。またwebの翻訳も利用可能です。
コマンド
折りたたみ関連のコマンドの多くはz
で始まります。基本的なものは以下です。
コマンド | 内容 |
---|---|
zf |
折り畳む (Fold) |
zo |
折り畳みを開く (Open) |
zc |
折り畳みを閉じる (Close) |
za |
折り畳みの開く閉じるを切り替える (Alter?) |
zd |
折り畳みを削除する(Delete) |
zf
はオペレータなので、)
や2j
などのモーションか、it
やip
などのテキストオブジェクトを後ろにくっつけて使用します。
他にもファイル全体の操作もあります。
コマンド | 内容 |
---|---|
zr |
すべての折り畳みを開く(減らす) (Reduce) |
zm |
すべての折り畳みを閉じる(増やす) (More) |
zr
,zm
はすべての折り畳みに対する操作ですが、あくまで現在表示されているものが対象になります。つまり入れ子になっている折り畳みを開いたり閉じたりは出来ません。この時に使用するのがzR
,zM
です。これらは再帰的に折り畳みに作用します。同様に、o
,c
,a
,d
も大文字にすることで再帰的に操作を行います。例えばzR
は再帰的にすべての折り畳みを開くので、結果すべての折り畳みを開きます。zM
と交互に使うのが楽かもしれないです。
あと、折り畳み間を移動するのに便利なコマンドです。
コマンド | 内容 |
---|---|
zj |
画面下方向の次の折り畳みへ移動。 |
zk |
画面上方向の次の折り畳みへ移動。 |
[z |
現在開いている折り畳みの先頭へ移動、すでに先頭の場合はその外側の先頭へ移動、すでに一番外側の折り畳みなら何も起きない。 |
]z |
現在開いている折り畳みの末尾へ移動、動作は[z と同様。 |
疑問
折りたたまれている情報はどこにある?
マニュアルによると、ファイルを破棄すると折り畳みの情報は失われてしまうそうです。折り畳みの情報を保存するコマンドはmkview
、呼び出すコマンドはloadview
です。
保存されたファイルはset viewdir
コマンドで表示されるディレクトリ内で確認できます。ファイルを開いてみるとローカル変数の定義の後に以下のような記述が見つかりました。数字を変えたら折り畳み位置が変わりましたのでこれが保存された設定値だと思います。
... 3,6fold 8,21fold ...
感想
保存について書きましたが、折り畳みを保存して呼び出して使うのは現実的ではないと思います。めんどくさいです。htmlやコードは構造がはっきりしているため、自動的に折り畳みを定義できるはずで、実際そういう設定がVimにはあるようです。
set foldmethod
で現在の設定が確認できます。現在はmanual
になっているので、これは手動で折り畳みを定義するデフォルトの設定なようです。
この他指定可能な値は以下になるようです。
値 | 内容 |
---|---|
manual | 折り畳みは手動で設定する。 |
indent | 等しいインデントの行で折り畳みを作る。 |
expr | オプションfoldexpr で深さを設定する。 |
marker | マーカーで折り畳みを指定する。 |
syntax | 構文強調表示のキーワードを使って指定する。 |
diff | 変更されていないテキストを折り畳む。 |
とりあえずset foldmethod=indent
をvimrc
に設定して様子を見ます。ただしこれを設定すると開くファイルがことごとくデフォルトで折り畳まれてしまっています。これは結構面倒臭いのでset foldlevel=100
とか書いておくと初期表示時に折り畳まれなくなります(なんか乱暴ですね...)。
以上です!
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とかならビデオチャットできないだろうか?と考えています。