その手の平は尻もつかめるさ

ギジュツ的な事をメーンで書く予定です

OS Xのクリップボードとtmuxのクリップボードを共通化する

OS Xクリップボードとtmuxのクリップボード *1 を共通にする設定に関するメモです.
なお,tmuxのバージョンは1.9a,reattach-to-user-namespaceのバージョンは2.3です.

1. まずreattach-to-user-namespace入れる
$ brew install reattach-to-user-namespace

簡単.

2. 設定ファイルを書く

.tmux.confに設定を書く.

if-shell 'test "$(uname -s)" = Darwin && which reattach-to-user-namespace > /dev/null 2>&1' 'source-file ~/.tmux-reattach-to-user-namespace.conf'

if-shell使うと,第一引数がtrueの時だけ第二引数を実行してくれて便利.
上記の設定では,「OSがDarwin,つまりOS X」かつ「reattach-to-user-namespaceがパス通ってるところに置いてある」時だけ,~/.tmux-reattach-to-user-namespace.confを読みに行くという感じになっています.


で,.tmux-reattach-to-user-namespace.confを書く.

set-option -g default-command "exec reattach-to-user-namespace -l $SHELL"

unbind -t vi-copy Enter
bind -t vi-copy Enter copy-pipe "reattach-to-user-namespace pbcopy"

unbind ]
bind ] run-shell "reattach-to-user-namespace pbpaste | tmux load-buffer - && tmux paste-buffer"

reattach-to-user-namespace経由でシェルを起動することで,tmux上でもpbcopypbpasteを正常に使える状態にしておき,あとはキーバインドをオーバーライドして,コピー時は内容をOSのクリップボードに送り,ペースト時はOSのクリップボードから読み込んでくるようにすればOK.


別に.tmux.confに書いても良いとは思うんですけど,.tmux.confif-shell1発書いて「こういう条件の時にこの外部設定ファイル読み込むんですよ〜」と表明したほうがわかりやすい気がしてこうした次第.気分です.

結論

if-shellは便利

*1:クリップボードと言うのだろうか

XSのABRTをキャッチしたい





以下の様なコードを実行するとABRTしてアプリごと死ぬという厳しい状況にぶち当たりました.
(なおこのバグはfix issue #45 · 93ff36f · goccy/p5-Compiler-Lexer · GitHubにて修正されている様子.@さんには頭が上がりません.ありがとうございます)


$SIG{ABRT} = sub {...};

という具合で書いてやればABRTを握れるのかな〜とか思って書いてみたら,この方法ではシグナルをトラップできない感じだったので,やむなく以下の様にforkを使ってなんとかすることにしました.
https://github.com/moznion/Perl-Lint-Playground/blob/5d9c23b59646dcc65fbb8d95e67368f6b02c3c01/lib/Perl/Lint/Playground/Service/Lint.pm

IO::Pipe便利!!!!!


さて皆さんこういう感じで,入力によってはXSがエラー吐いてどうにもならん時ってどうされてるんですかね?

Published “Perl::Lint Playground”

(またしても日本語は下にあります / Japanese follows English)


I published “Perl::Lint Playground”.
http://perl-lint.moznion.net/


You can try feature of Perl::Lint easier on this playground.
However this and Perl::Lint have not been completed yet, so possibly results are vague.
(e.g. Now, source code that was written in this playground is not file in spite of playground warns by policy of file path. I think there is more problems...)


For now, please play here and tell me your mind.
Enjoy!




Perl::Lint Playgroundを公開しました.
http://perl-lint.moznion.net/


ブラウザ上でPerl::Lintを試してみることができます.
まあ,みなさんもご存知の通りPerl::Lintは未完成ですので,このプレイグラウンドの結果もあんま信用しないほうが良いです.
(例えば,プレイグラウンドで試す為のソースコードはファイルではありませんが,現状ではファイルにまつわるポリシーによって警告されるなどといった問題があります.多分他にも結構あるとおもいます)


とりあえず遊んでみて,僕にフィードバックなどをくれると嬉しいです.
よろしくお願いします.

git grepの代わりにgit agを使う

git grepが便利なので,同じ感覚でag (The Silver Searcher)を使ってみたいという話です.何事も速いほうが良い.

前提

ぶっちゃけagは,デフォの状態で.git/以下の内容や.gitignoreに書かれてるファイルやディレクトリなんかを検索の対象から排除するのであんま旨味は無い.

方法

以下のエイリアスを張る *1.もちろんconfigファイルを直で編集しても良いです.

$ git config alias.ag '!git ls-files | xargs ag'

git ls-filesを使って,gitで管理されているファイルの一覧を持ってきて,xargsを使ってagに渡してやるという感じ.

実際僕はこれで十分なんですが,表示と挙動をgit grep(1)っぽくしたい場合は以下のようになるでしょう.

$ git config alias.ag '!git ls-files | xargs ag --pager="less -F -R" --nogroup --color-match=0\;31 --color-path=0\;0 --no-numbers'

--pager="less -F -R"はお使いのpagerに応じて変更するか,あるいはお好みに応じてpagerオプションを取り去ると良いでしょう.

結論

$ git ls-files | xargs $COMMAND

が基本的に便利!!!!!

*1:場合によって--globalオプションなどを付けても良いでしょう

Released Perl::Lint as underdevelopment

(日本語は下にあります / Japanese follows English)


I've just released Perl::Lint as underdevelopment.
https://metacpan.org/release/MOZNION/Perl-Lint-0.01_01


Development of Perl::Lint is in progress, but I need feedback about this module.
Thus I released it on CPAN to simplify installation in spite of it is on the way.
(Thank you for your advice, @tomhukins and @)


Installation is easy.

$ cpanm --dev Perl::Lint


And usage is simple!

use Perl::Lint qw/lint/;
my $violations = lint(['path/to/file.pl']);


This module is unfinished as you know.
Please don't believe this and me!!

I'm looking forward to hearing your feedback.
Please send your mind to the GitHub Issues or @.

Enjoy!


P.S.
I'm going to talk about Perl::Lint at YAPC::Asia Tokyo 2014.
Let's talk me at there!!



Perl::Lintを開発版としてリリース致しました.
https://metacpan.org/release/MOZNION/Perl-Lint-0.01_01


Perl::Lintは目下のところ開発中のモジュールですが (まだ出来てねえのか!) ,そろそろユーザのフィードバックが欲しいなあとか,Tom Hukins氏や@氏らから「とりあえずリリースしちゃえよ! インストールしにくくてかなわん!」という意見を頂いたとか,そういった事情でunderdevelopment releaseを敢行したというのが背景です.


インストールは至って簡単です.

$ cpanm --dev Perl::Lint


使い方もシンプルです.

use Perl::Lint qw/lint/;
my $violations = lint(['path/to/file.pl']);


ご存知の通りこのモジュールは未完成です.
このモジュール (の吐き出す結果) と俺を信用しないでください.

フィードバックはGitHub Issuesに書いてもらえるか@まで直でメンション飛ばしてもらえると嬉しいです.

よろしくお願いします.


[追記]
そういや言い忘れてましたが,YAPC::Asia 2014というイヴェントでPerl::Lintの話をします.
会場でお会いしましょう!!!

先読みとautovivificationの話,あるいはマイクロオプティマイゼーションの話

Perlの話です.が,先読みの辺りはどの言語でも共通なのでは,という感じです.

追記

なんか先読み関係ない感じになってるのでコメント見ると良いです.この記事の情報は誤っているので後で書き直す.

ここから先は読まなくても良い

さて,配列を走査するような処理を書く時,「1個後のアイテムと現在のアイテムを比較したい」というようなニーズから,1個後のアイテムを読んでおきたいというような事があると思います.

ナイーブに実装するとしたら以下のようになるでしょう (C-Style Loopで書いている理由は以降の比較のためなので深く考えないで下さい).

my @array = (1..100);
for (my $i = 0; my $item = $array[$i]; $i++) {
    my $next_item = $array[$i+1];
    # do something...
}

このコードは,先読みした値を変数に格納しておいて,それを使いまわすというような効率的な書き方に変形する事が出来ます.

my @array = (1..100);
for (my $i = 0, my $next_item; my $item = $next_item || $array[$i]; $i++) {
    $next_item = $array[$i+1];
    # do something...
}

さてこの両者でベンチマークを取ってみると以下の様な感じになります.

use Benchmarks sub {
    my @array = (1..100);

    my $normal = sub {
        for (my $i = 0; my $item = $array[$i]; $i++) {
            my $next_item = $array[$i+1];
            # do something...
        }
    };

    my $read_ahead = sub {
        for (my $i = 0, my $next_item; my $item = $next_item || $array[$i]; $i++) {
            $next_item = $array[$i+1];
            # do something...
        }
    };

    +{
        normal     => $normal,
        read_ahead => $read_ahead,
    };
};
__END__
              Rate     normal read_ahead
normal     39711/s         --       -25%
read_ahead 52609/s        32%         --

速いですね! めでたしめでたし!

めでたくない場合

しかしながら走査するオブジェクトの構造が変わるとそうは問屋がおろしません.
以下の様なコードの場合を考えてみましょう.

use Data::Faker;
my $faker = Data::Faker->new;
my @array;
for (1..100) {
    push @array, +{
        name => $faker->name,
        age  => int rand(80),
    };
}

for (my $i = 0, my $next_item; my $item = $next_item || $array[$i]; $i++) {
    $next_item = $array[$i+1];
    $next_item->{name};
}

このコードは永遠に終わりません.無限ループします.

このコードで配列の最後の要素にアクセスしている時,$next_itemundefとなりますが,このundefに対してハッシュリファレンスのようにアクセスすると,undef{}という風に空のハッシュリファレンスに変化してしまいます (もちろん配列リファレンスで同じようなことを行なった場合も同様です).以下の様な感じ.

my $foo = undef;
$foo->{not_exists}; # <= この時点で$fooは{}になっている!

Perlでは空のハッシュリファレンスは真値であるので,論理和の評価は空ハッシュである$next_itemが返ってきてしまい,for文の条件が常に真として扱われてしまい無限ループしてしまうという訳ですね.

このように,走査対象の配列の中身がハッシュリファレンスで,なおかつ1個次の要素を見る必要がある場合,上手くいかずにハマる場合があるわけです.

そこで我々はどうすべきか

1. ローカル変数を1個作って,そこに$next_itemをコピーしてそっちにアクセスする
for (my $i = 0, my $next_item; my $item = $next_item || $array[$i]; $i++) {
    $next_item = $array[$i+1];
    my $_next_item = $next_item;
                                                                            
    # do something...
    $_next_item->{name};
}

このようにすると無限ループは起きないものの……

use Data::Faker;
use Benchmarks sub {
    my $faker = Data::Faker->new;
    my @array;
    for (1..100) {
        push @array, +{
            name => $faker->name,
            age  => int rand(80),
        };
    }

    my $normal = sub {
        for (my $i = 0; my $item = $array[$i]; $i++) {
            my $next_item = $array[$i+1];

            # do something...
            $next_item->{name};
        }
    };

    my $read_ahead = sub {
        for (my $i = 0, my $next_item; my $item = $next_item || $array[$i]; $i++) {
            $next_item = $array[$i+1];
            my $_next_item = $next_item;

            # do something...
            $_next_item->{name};
        }
    };

    +{
        normal     => $normal,
        read_ahead => $read_ahead,
    };
};
__END__
              Rate read_ahead     normal
read_ahead 19855/s         --       -11%
normal     22399/s        13%         --

先読みしたほうが遅い!!!! 意味無いじゃん!!!!
……まあそりゃそうですよねという感じ.

2. no autovivificationする

undef->{something}->[42]のようにアクセスするとそれぞれ空の{}[]という風になってしまうのが問題なので,その挙動を潰してやれば良い!

ということでautovivificationの出番です.
https://metacpan.org/pod/autovivification

no autovivificationという風に宣言してやると,そのレキシカルスコープでvivification,つまりundefを自動で空のハッシュリファレンスや配列リファレンスに変換する処理を無効にすることが出来ます.

no autovivification;
for (my $i = 0, my $next_item; my $item = $next_item || $array[$i]; $i++) {
    $next_item = $array[$i+1];
    # do something...
    $next_item->{name}; # <= ここでundefが変換されない!
}

ベンチマークの結果は以下のとおり

no autovivification;
use Data::Faker;
use Benchmarks sub {
    my $faker = Data::Faker->new;
    my @array;
    for (1..100) {
        push @array, +{
            name => $faker->name,
            age  => int rand(80),
        };
    }

    my $normal = sub {
        for (my $i = 0; my $item = $array[$i]; $i++) {
            my $next_item = $array[$i+1];
            # do something...
        }
    };

    my $read_ahead = sub {
        for (my $i = 0, my $next_item; my $item = $next_item || $array[$i]; $i++) {
            $next_item = $array[$i+1];
            # do something...
        }
    };

    +{
        normal     => $normal,
        read_ahead => $read_ahead,
    };
};
__END__
              Rate     normal read_ahead
normal     36885/s         --       -18%
read_ahead 44776/s        21%         --

おっ,速い!

まとめ

  • undefにリファレンスアクセスするといつの間にかundefが別のものになる
  • うかつに先読みしない
  • とは言え先読みしたい,なおかつ少しでもパフォーマンスが気になる場合はno autovivificationを使うのが良いのでは無いか.


という感じです

Table要素をCSVとかXLSXとかに変換して保存出来るChrome拡張書いた

皆さん年に2,3回は,「ウェッブページのTable要素をXLSX (もしくはCSV) に変換して保存したいワ!」となることがあると思います.僕はあります.

そういう時は,「心をこめてTableをコピーしてExcel (もしくはお好みのヒョーケーサンソフト) にペースト!」みたいな感じになると思うんですが,コピペギョームはTableの行数がめっちゃ多い時 (1画面で収まらないTable要素とか) にだるいし,コピペ処理中になんらかの事故が起きてミスった結果えらい人に呼ばれて怒られる可能性なども否めないので,そういうことをクリック2発で実現するChrome拡張を書きました.

https://chrome.google.com/webstore/detail/table-to-spreadsheet/haidhlbpihfihbjcggmffnmhgiddjcoc?hl=ja

何をするChrome拡張かというと,Table要素を右クリックして,XLSX (あるいはCSV) に変換するメニューを選択すると,Tableの内容がそれらの形式に変換されてローカルに保存できるというシンプルな拡張となっております.

色々あって,Table以外の部分でもコンテキストメニューが表示されてしまう感じになっていますが,Table以外の箇所でメニューを選択してもalertが出るだけという親切設計 (!?) になっているので安心 (!!??) です *1
あと既知の問題として,テーブルの中にテーブル,というような入れ子構造のテーブルに上手く対応できないというものがあって,これはどうしたもんかなあと思っております.何か良いアイデアがある方がいらっしゃいましたら教えて下さい.


とりあえずなんやかや便利な感じなのでご利用下さいませ.


なおソースは以下です.CSV.jsjs-xlsxがトンデモなく便利でヤバい!
https://github.com/moznion/crx-table-to-spreadsheet

*1:ダサいからなんとかしたい