Leap Motionを手に入れました
Leap Motionを手に入れました。Leap Motionは手の動きをかなりの精度で取得できるセンサーです。たくさんの言語でプログラミング可能なAPIが提供されていますが、とりあえずJavaScriptのAPIを使ってみます。また、各クラスの内容もざっくりと整理していこうと思います。
ここでは、開発PCがmacなのでSDKはV2 desktopを使います。Windowsの方は新しいSDKが使えるようなのでそちらを使ったほうがいいかもしれません。(VR関係に強化されているように見えます)
参考
- wiki
- 公式
- JavaScript SDK Documentation — Leap Motion JavaScript SDK v2.3 documentation
- github.com/leapmotion/leapjs
- Javascript | Leap Motion Developers
Leap Motion 小型モーションコントローラー 3Dモーション キャプチャー システム [並行輸入品]
- 出版社/メーカー: Leap Motion
- メディア: Personal Computers
- この商品を含むブログを見る
とりあえず
環境設定やインストールは省略します、公式サイトの指示通りにすれば問題ないはずです。
仕組みを理解するために、とりあえずブラウザ上に手を表示させてみます。まずはセンサーの値を取れるか確認をするためのコードを記述します。
ブラウザのコンソールに吐き出すだけ
<html> <head> <!-- <script src="./leap-0.6.4.js"></script> --> <script src="https://js.leapmotion.com/leap-0.6.4.js"></script> <script> Leap.loop(frame => console.log(frame)); </script> </head> <body> </body> </html>
LeapJSを外部リソースとして取得します。Leap
はLeapJSがネームスペースとして確保しているオブジェクトのようです。
値をとるにはLeap.loop
にコールバック関数を渡します。この関数にセンサーが取得したframe
オブジェクトが引数で渡されるので、それに対する処理を記述するのがアプリケーション作成の基本的な流れになると思います。
frame
オブジェクトの詳細はリファレンスに書いてあります。手、指、ツール(棒状のもの)などのデータをプロパティとして持っていてくれています。
プラグイン Rigged Hand
を使う
LeapJSはプラグインという形で機能拡張できるようになっています。プラグインはController
クラスによって管理されます。各プラグインはplugin
メソッドにより登録しuse
メソッドにより有効にしたり、use
メソッドに直接関数を渡すことで、loop
実行時にコールバックとして呼び出されるようになります。
プラグインを使うための準備として、まずは先ほどの処理をLeap.loop
を使わない記述方法に変えておきます。
<html> <head> <!-- <script src="./leap-0.6.4.js"></script> --> <script src="https://js.leapmotion.com/leap-0.6.4.js"></script> <script> const controller = new Leap.Controller(); controller.use( () => ({ frame: frame => console.log(frame), }) ); controller.connect(); </script> </head> <body> </body> </html>
プラグインを追加する準備が整ったところで、リアルな手を表示することができるRigged Hand
というプラグインを追加してみます。
プラグインを使用するにはleap.rigged-hand-0.1.7.js
の他に、いくつかのライブラリを読み込む必要があります。use
メソッドでriggedHand
を使用するようにします。
<html> <head> <!-- 以下から必要なライブラリを取得 https://cdnjs.cloudflare.com/ajax/libs/three.js/r70/three.js https://js.leapmotion.com/leap-0.6.4.js https://js.leapmotion.com/leap-plugins-0.1.10.js https://github.com/leapmotion/leapjs-rigged-hand/blob/master/build/leap.rigged-hand-0.1.7.js --> <script type="text/javascript" src="three.js"></script> <script type="text/javascript" src="leap-0.6.4.js"></script> <script type="text/javascript" src="leap-plugins-0.1.10.js"></script> <script type="text/javascript" src="leap.rigged-hand-0.1.7.js"></script> <script> const controller = new Leap.Controller(); controller.use('riggedHand'); controller.connect(); </script> </head> <body> </body> </html>
動作はこんな感じになりました。実際の手も一緒に撮影すればよかったです。。。
Leap Motion - Rigged Hand Demo
LeapJSのクラスに関する考察
API Referenceによると、LeapJSには次のクラスが存在します。
制御
クラス名 | 概要 |
---|---|
Controller | Leap MotionのAPIに接続するためのインターフェース。オプションや、フレーム更新時のコールバックの設定など、最もさわりそうなオブジェクト |
InteractionBox | Leap Motion Controllerに紐づいた表示領域を扱うオブジェクト?領域に応じたベクトルの正規化方法などを提供してくれるみたいです |
Frame | センサーがフレーム更新ごとに取得した手、指に関するデータを含むオブジェクト |
パーツ
クラス名 | 概要 |
---|---|
Pointable | 指(Finger)やツールなどの抽象クラスで位置や方向、速度などを保持しているオブジェクトです |
Hand | 手のオブジェクトで、5本の指の配列や腕などプロパティに持っています。かなり豊富な情報を持っているようです |
Finger | 指のオブジェクトで、生えている位置や、手の骨の本数文の骨オブジェクトに関する情報を持っています。何指か、などの識別子も持っています |
Bone | 骨のオブジェクトで、指の骨を表しています。前後の関節を持っています |
ジェスチャー
ジェスチャーは多いので個々の概要は省略します。認識してくれるハンドサインという認識です。独自に追加できるかはちょっとわからないです。
- Gesture
- CircleGesture
- ScreenTapGesture
- KeyTapGesture
- SwipeGesture
計算要素
クラス名 | 概要 |
---|---|
Matrix math | 行列のオブジェクト。WebGL用外部ライブラリを使っているようです |
Vector math | ベクトルのオブジェクト。WebGL用外部ライブラリに入っているvec3を使っているようです |
感想
API Referenceを読んで、勝手に各クラスの概要などを書きましたが、このドキュメントを和訳した方が随分人のためになるんだろうな、と思いました。(もしかしたら和訳されている?)
まだまだわかっていないことが多いので、ご指摘いただければ幸いです。
screenコマンドの小さな話
ターミナル操作をいい感じにできる仮想端末マネージャコマンドscreen
を最近知りました。メモしておきます。
私の理解では、ターミナルをラップして、通常のシェル操作に加え、セッション、ウィンドウ、領域という概念を取り込み、それらを操作できるようにするものです。
screen
を実行すると仮想端末のセッション(session)とそれに紐づく一つのウィンドウ(window)が起動します。ウィンドウ内ではいつも通りのシェルの操作に加え、セッションに紐づくウィンドウを複数起動できるようになるので、ssh
等でサーバに接続した後で起動すると便利です。今まで複数ログインしていた場合、一度ログインしてウィンドウを複数起動することで済ますことができます。(TeraTermのセッションの複製に似た感じです。詳しく見ていないですが内部的に使っているのでしょうか?)
使うことのメリットとして、
- 複数ウィンドウを起動できるので、面倒な踏み台経由の
ssh
接続時に結構楽できます。 - 接続が切れてもセッションが残っていてくれるので、再度つなぎなおした時に作業をすぐに再開できます。一つのウィンドウで長い処理を走らせて寝かせておくこともできます。
他にも、コマンドによるコピー&ペーストの操作をキーボードのみでできるようになることも、なかなか嬉しいことです。
基本的な使い方
触っていてよく使っているコマンドを羅列します。
セッション管理系
コマンド | 内容 |
---|---|
screen | 新しいsessionとそれに紐づいた一つのwindowを起動します。 |
screen -ls | 現在起動しているsessionの一覧を表示します |
screen -r | 最後に繋いでいたsessionを再開します。sessionのidを渡せば指定したセッションにつなぎ直すことができます |
Ctrl+a d | session起動中に現在のsessionから抜ける(detach)ことができます。detach後もsessionは起動しているので、一覧で確認できます |
Ctrl+a \ | session起動中、session自体を削除します。一覧からも消えます。起動中の紐づいた全てのwindowも削除されます。私の環境だとCtrl+a Ctrl+¥ になっていました |
チートシート
セッション起動後は、Ctrl+a ?
でコマンドのチートシートが表示されます。私のメモの存在意義を奪いかねないですが、ただ表示されるコマンドがアルファベット順なのでちょっと見にくいです。そこに漬け込みます。
以下主にセッション起動後のコマンドになります。
ウィンドウ管理系
コマンド | 内容 |
---|---|
Ctrl+a c | 新しいwindowを開きます |
Ctrl+a k | windowを削除します、正確には削除しますか?と(y/n)で聞かれます |
Ctrl+a " | windowの一覧を表示します。j ,k でwindowを切り替えることができます |
Ctrl+a N | 現在開いているwindowの名前(Name?)を表示します |
Ctrl+a n, Ctrl+a p | 別のwindowに切り替えます。多分切り替え先はprevious ,next に対応していると思います |
Ctrl+a Ctrl+a | 切り替え前のwindowに切り替えます |
領域管理系
コマンド | 内容 |
---|---|
Ctrl+a S | windowを水平に分割します。windowが紐づかない新しい領域(region)を作成します |
Ctrl+a | | windowを垂直に分割します。windowが紐づかない新しいregionを作成します |
Ctrl+a TAB | 分割されたregion間を移動します |
Ctrl+a X | 分割時、現在有効になっているregionを削除します |
Ctrl+a Q | 分割時、現在有効になっていない他のregionを削除します |
メタ操作系(未整理)
コマンド | 内容 |
---|---|
Ctrl+[ | コピーモードに入ります。hjkl 等のvim ライクな入力で移動することができます。space を押すとコピー開始位置を指定、もう一度space を押すと選択範囲をコピーします |
Ctrl+] | バッファのコピーした内容をペーストします |
使っていての感想
色々あります。
Ctrl+a
がシェルの行頭移動を奪ってしまいます。Ctrl+a a
に行頭移動が振り分けられているので頭を切り替えて使っています。セッションの中にいる、と言うことを意識できるのでこれはこれでいいかもしれないです。キーバインドを変えるのは嫌なので私はこのままです。慣れました。vim
の画面分割と一緒に使うと結構混乱します。vim
のCtrl+w
系の画面移動とごっちゃになったり、screen
でのコピー範囲が行単位なので行番号入っちゃったりしました。これは対応方法がわからないですが、慣れました。- スクロールについて最初どうすればいいか不明でした。ログ出力等で流れてしまった時は、コピーモードに入って遡っています。別にコピーしたいわけではないのでちょっと気持ち悪いです。
vim
の気分で操作しています、less
同様いまだに正しい移動操作がわかりません。 - macにデフォルトで入っている
screen
はヴァージョンが古いようで垂直分割ができないです(2016/12/14に確認)。homebrew
で入れ直しました。あと、ssh
接続時に便利とか書いていましたが意外にコマンド自体が入っていないことがあったりします、注意です。
他にもウィンドウ操作を記録することができたりするのですが、使い道がわからず今のところ使っていないです。さらに他にも複数人で同じセッションを共有するマルチディスプレーモードという便利なものもあるそうですが、私はぼっちなので使っていません。
感想
コピー&ペーストがキーボードから手を動かさずに使えるので、嬉しくてローカルでも使っています。早く使いこなせるようになるといいのですが。まだまだ不明点が多いです。
上記内容に間違いがあれば申し訳ないですが、修正しますので是非教えていただきたいです。
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から注意されるのですが、これが何に使われているかわからないです。自動更新できているから、ひとまず放置しています。
追記: ブラウザ同士のイベントを同期することもできるみたいです。同一ネットワーク内だと同期されるのかな?