MacBook AirのディスクをCase-sensitiveにして大文字小文字のファイルを作れるようにする

MacBook Airを買ってから2年経ったのでこの前新たにMacBook Airを買いました。
で、会社でも家でもCase-sensitive問題で頭を抱えた経験があるので今回は最初からCase-sensitiveでインストールしました。

流れ

1. commandキーとRキーを押しながら再起動

2. 無線LANが繋がってることを確認する

3. Disk Utility を選択して、Eraseを選択。元のフォーマットはこんな感じでただのジャーナルが選ばれてる

4. Case-sensitiveを選んで消去

5. OS Xの再インストールを選ぶと「ダウンロードするよ!自動的に再起動するよ!」となるのでひたすら待つ(ウチのWiMAXがホント遅いせいで長時間がかかったけど普通は2時間くらい)

6. これで大文字ファイルと小文字ファイルが区別される

% touch test
% touch Test
% ls
Test test

おまけ. brew doctorするとこんなWarningが出ますが問題なく使えています(このWarningわかりやすくていいですね)

% brew doctor 
Warning: Your file-system on / appears to be CaSe SeNsItIvE.
Homebrew is less tested with that - don't worry but please report issues.


APPLE MacBook Air 1.3GHz Dual Core i5/13.3

APPLE MacBook Air 1.3GHz Dual Core i5/13.3"/4GB/128GB MD760J/A




Perlのリファレンス比較は==演算子

Perl徹底攻略 (WEB+DB PRESS plus)を買いました。
p.18にリファレンス同士の比較が書いてあり、

リファレンス同士の比較

2つのリファレンスが同じものを指していれば値は等しくなります。ですから、リファレンスが入っている変数$xと$yがあったとき、==演算子で値を比べれば同じ物を指しているかどうかがわかります。

# $xと$yにはリファレンスが入っている
if ($x == $y) {
    # $xと$yは同じものを指している
} else {
    # $xと$yは別のものを指している
}

となっていて
「あれ、リファレンスって数字ではないのに==演算子なのか。アドレスが入っているからかな?」
と気になったのでperldoc perlrefしてみました。

Using a reference as a number produces an integer representing its storage location in memory. The only useful thing to be done with this is to compare two references numerically to see whether they refer to the same location.

if ($ref1 == $ref2) {  # cheap numeric compare of references
    print "refs 1 and 2 refer to the same thing\n";
}

リファレンスを数値として使うと、メモリ内のストレージの位置の 整数表現を生成します。 これを利用して便利な唯一の状況は、二つのリファレンスを数値として 比較することで、同じ場所を参照しているかどうかを調べる場合です。

if ($ref1 == $ref2) {  # cheap numeric compare of references
    print "refs 1 and 2 refer to the same thing\n";
}

http://perldoc.jp/docs/perl/5.16.1/perlref.pod

おー、なるほど。やっぱり==で数値として扱われて比較されるのかー。
こういうコンテキストやっぱり楽しい。Perl好きです。

commコマンドを使って文字列でsort済みの2つのファイルの比較をし「片方だけに存在する行」「共通の行」を出力する

追記

「文字列でsort済みの」って入れ忘れてた。
manに

The comm utility reads file1 and file2, which should be sorted lexically,

って書いてある通り、「sorted lexically」でないとダメ(「sort -n」ではなく「sort」でsortする)

なので、

comm <(sort id_num.txt) <(sort id_even.txt)

ってやるのが確実

追記終わり



commコマンドの名前をすぐに忘れてしまい、ググっても「catしてuniq」とかそういうのしか出てこなくて「あのコマンド名なんだっけなー」ってなるのでメモ残す

やりたいこと

  • 2つのファイルがあり、どちらかのファイルにのみ存在する行と、その行がどちらのファイルに存在するかを出力したい
  • 2つのファイルがあり、両方に存在する行を出力したい

実際にやってみる

書いてもよくわからないし後で見たときによくわからないので実践。

id_num.txt と id_even.txt という2つのファイルを用意する
  • id_num.txt
% cat id_num.txt
1
2
3
4
5
  • id_even.txt
% cat id_even.txt
2
4
6

grepすればわかる通り、

  • 「1」「3」「5」はnumにだけ存在する
  • 「6」はevenにだけ存在する
  • 「2」と「4」は共通
commコマンドを(オプションなしで)実行するとタブ区切りで以下の出力がされる
% comm id_num.txt id_even.txt
1
		2
3
		4
5
	6

左から「1つめの引数のファイルだけに存在する行」「2つめの引数のファイルだけに存在する行」「両方のファイルに存在する行」となる

「表示したくないカラム番号」をオプションで渡す
  • id_num.txtだけに存在する行が欲しい(つまり、2番目と3番目のカラムを表示したくない)
% comm -23 id_num.txt id_even.txt
1
3
5
  • id_even.txtだけに存在する行が欲しい(つまり、1番目と3番目のカラムを表示したくない)
% comm -13 id_num.txt id_even.txt
6
  • 両方のファイルに存在する行が欲しい(つまり、1番目と2番目のカラムを表示したくない)
% comm -12 id_num.txt id_even.txt
2
4
(補足)大文字と小文字を区別したくない場合には「-i」を付ければ良い
% cat id_large.txt
A

% cat id_small.txt
a

% comm id_large.txt id_small.txt # 区別されているので別々に出力される
A
	a

% comm -i id_large.txt id_small.txt # 区別されないので「共通の行」として出力される
		A

ちなみに comm の由来と思われるもの

manを見ると

NAME
     comm -- select or reject lines common to two files

と書いてあるので「common」の略ですかね

foreachで連想配列に代入した後はcurrentが変わるのでwhileでeachするとハマる

へぇ、phpってこういう動きするんだーと思ったので。
普段while each使わないのでハマったことなかったのだけど、何かbugがあるなぁと思うコードを見てみたらforeachの後にwhile使ってるのが原因ってことがわかった。



bugをわかりにくくしていたのは以下のことがあったから。

  • foreachしてるでカーソルは最後まで進む
  • ただし、foreachの中で元の連想配列に代入していた場合にはカーソルが2つめになる

このせいでwhileが中途半端に成功してるのがbugに繋がった。

<?php
$hash = array(
    'a' => 1,
    'b' => 2,
    'c' => 3,
);

var_dump('current => ' . current($hash)); // string(20) "before: current => 1"

foreach ($hash as $key => $val) {
}

var_dump('foreach: current => ' . current($hash)); // string(19) "after: current => "

foreach ($hash as $key => $val) {
    $hash[$key] = $val;
}

var_dump('foreach + insert: current => ' . current($hash)); // string(19) "after: current => 2"

while (list($key, $val) = each($hash)) {
    var_dump($key, $val);
}

// string(1) "b"
// int(2)
// string(1) "c"
// int(3)

refs. PHP: each - Manual

USBに1Password.agilekeychainを入れててパスワード解除ができなくなったときの対処法

MacBook Airのディスク容量が少ないのでDropboxをUSBに入れてそこに1Passwordを入れているのですが、
USBを外して付け直したりすると1Passwordが上手く動かないことがあり困りました。

現象

  • 正しいマスターパスワードを入れているのに解除できない
    • コンソールにはこんな感じのエラーが出る

13/03/17 14:11:47.436 1Password Helper: 暗号化キーファイルは存在しますが 'file://localhost/Users/monmon/Dropbox/1Password/1Password.agilekeychain/data/default/1password.keys' からは読み込めません

  • 1passwordのhelper(メニューに出てるやつ)が起動したり終了したりを繰り返す

対処法

権限の問題だと思うんですがググったりしても同じような例が出なかったので、
1Passwordを終了させた後に

mv /Users/monmon/Library/Containers/com.agilebits.onepassword-osx-helper ~/tmp/. 

みたいにしてonepasswordの情報をよけて起動し直しました。

そうすると「初めてお使いですか?」「使ったことありますか?」のあの起動画面が出てくるので、「使ったことある」を選んで進めればよしと。

Rubyはハッシュに数値と文字列のkeyが持てるんですね

Rubyで書かれたコードにbugがあって調べることになり、初めて知りました。

Ruby

% ruby -e 'h = {1 => true, "1" => false}; p h'
{1=>true, "1"=>false}

Perl

% perl -MData::Dumper -e '$h = {1 => true, "1" => false}; warn Dumper $h'
$VAR1 = {
          '1' => 'false'
        };

PHP

% php -r '$h = array(1 => true, "1" => false); var_dump($h);'
array(1) {
  [1]=>
  bool(false)
}

追記

ということで、

  • Object#hash ハッシュの格納に用いられるハッシュ値の計算
  • Object#eql? キーの同一性判定

の2つを定義すれば何でもkeyになるよという話。
ハッシュの特性を考えれば確かにそうですよねそうですよね。

Perlとバーコードリーダーで本棚整理をするよ! #vgadvent2012


こんにちはこんにちは、VOYAGE GROUPエンジニアblog Advent Calendar 17日めを担当するmonmonといいます!
どんなネタにしようか土曜日まで迷ったあげく、せっかくの会社のAdvent Calendarなので会社に関係する話にしました!

どんな話をするの?


会社の1FにOASISという図書室がありまして、その本の整理をしたいなぁと思いました(あくまで今回のネタ用なので実際はやらないです)。
なので、今回はその話をしようと思います。
ちなみに上の展開写真がOASISね。

何でこの話にしたの?


理由は3つありまして、

という感じです。


仕事でもそうだけど、何かを作り出すときにこういう部品をつなぎ合わせることで形になっていくってのは楽しいものですね。
あぁ!そういえばジョブズも「役に立つ可能性があるとは思わなかった点と点が、10年後に繋がって素晴らしいものができた」みたいな良い話をしてましたね!それだ!

処理の流れ


さぁ!ということで処理の流れだ!最終的に以下のような感じになったよ!

  1. バーコードリーダーでバーコードを読み取り、ISBN13をqueueに入れる
  2. queueからISBN13を取り出しAmazon Product Advertising APIで検索
  3. あればさらにY!の日本語形態素解析API形態素解析
  4. それらをSQLiteに突っ込む
  5. あとは全文検索でうまー

これをひとつずつ見ていこう!

バーコードリーダーでバーコードを読み取る


バーコードリーダーはこれです。
USBで繋ぐと「ぴっ!」って鳴って、バーコードを読み込むと上の部分が緑に光り、ISBNを入力してくれます。便利!

ジョブキューを使う


ということで次。
ISBNを読み込んだ後、その検索結果を待たずに次のISBNを読み込みたいためジョブキューを使います。
で、今年のYAPC::AsiaでもQudoの話があったし、なのでQudoを使ってみようかなと調べたんですが、
調べているうちにこの辺の記事で今回の用途ならJonkでいいかもと思い変更しました。


Jonkの使い方は
https://metacpan.org/module/Jonk

https://metacpan.org/module/Jonk::Cookbook::Basic
に書いてあり、これをまるっとコピーしてきたらサクッと動くくらい楽で、ほぼそのままコピペです。ありがたいありがたい。
2年前のPerl Advent Calendarでも取り上げられているのでもっと難しいことやるときはこっちも参考にしようかなと思ってました(が、時間なくてここまでいかず)。


具体的なコードはこんな感じ。while 1で無限ループ作ってひたすらISBNをキューに突っ込むだけです。

#!perl
# script/jonk-client.pl
use strict;
use warnings;
use FindBin;
use DBI;
use Jonk::Client;

use lib "$FindBin::Bin/../lib";
use MyApp;

my $setting = MyApp->new(appdir => "$FindBin::Bin/../")->setting;

my $dbh = DBI->connect("dbi:SQLite:$setting->{jonk}->{db}",'','');
my $jonk = Jonk::Client->new($dbh);

while (1) {
    print 'input ISBN: ';
    next if <STDIN> !~ m/(\d+)/xms;

    my $isbn = $1;
    my $job_id = $jonk->enqueue('Book', $isbn);
    print "enqueued job_id: $job_id\n";
}


で、取り出して仕事する方はこんな感じ。実際のロジックはMyApp::Worker::Bookにあるので、そっちを呼び出しているだけですね。

#!perl
# script/jonk-worker.pl
use strict;
use warnings;
use FindBin;
use DBI;
use Jonk::Worker;

use lib "$FindBin::Bin/../lib";
use MyApp;
use MyApp::Worker::Book;

my $setting = MyApp->new(appdir => "$FindBin::Bin/../")->setting;

my $dbh = DBI->connect("dbi:SQLite:$setting->{jonk}->{db}",'','');
my $jonk = Jonk::Worker->new($dbh => {functions => [qw/Book/]});
my $worker = MyApp::Worker::Book->new(
    appdir => "$FindBin::Bin/../",
    setting => $setting,
);

while (1) {
    if (my $job = $jonk->dequeue) {
        warn "work job: $job->{arg}\n";
        $worker->work($job);
    } else {
        sleep(3); # wait for 3 sec.
    }
}


ここで呼び出されたWorkerが

の3つの処理を行います

Amazon Product Advertising APIを使う


これは超簡単。Amazonのサイトで自分のkeyとsecret、それにassociate_tagの3つを取得した後、
CPANにあるURI::Amazon::APAを使えば良いです。
弾さんのブログに使い方の記事もあるのでハマる所はないですね。
唯一のハマりどころは日本のURLである「http://ecs.amazonaws.jp/onca/xml」の方を使うということかしら。


コピペするだけで本の検索ができちゃうなんて素敵ですね!

形態素解析APIを使う


さて、つづいて形態素解析です。
これは何で必要になったかと言うと、FTSを使うものの、タイトルなどを一綴りのまま入れてしまうとヒットしないパターンがあるのでそれをどうにかしたかったから。
以下のブログも参考にしました。
http://blog.mwsoft.jp/article/34911345.html


なので、「MeCabを入れて…」と考えたんですが、「今回そんなにガッツリやる必要ないのでもっと楽なのがいいなぁ」と方向修正。
結果、Y!の日本語形態素解析APIです。
で、「これもCPANにあるんじゃないかなぁ」と思ったらやっぱりあったよ!
https://metacpan.org/module/WebService::YahooJapan::WebMA


ただしURLが古いので以下のように変更して使います。

$WebService::YahooJapan::WebMA::APIBase = 'http://jlp.yahooapis.jp/MAService/V1/parse';


具体的なコードはこんな感じ。ほぼSYNOPSISのままですが、word_listの要素が1つの場合に配列になっていなかったのでそこだけ対応しました。

    my $api = WebService::YahooJapan::WebMA->new(
        appid => $self->appid,
    );
     
    my $res = $api->parse(
        sentence => $sentence,
        ress => 'ma',
        response => 'surface,reading,base form'
    ) or croak $api->error;
    my @word_list = (ref $res->{ma_result}->{word_list} eq 'ARRAY')
        ? @{$res->{ma_result}->{word_list}}
        : ($res->{ma_result}->{word_list});
     
    $self->_to_string($self->_flatten(@word_list));

コレを使って、「タイトル」「作者」「出版社」の3つを形態素解析し、それらをspase区切りの文字にしてDBに突っ込むようにしてます。


具体例をあげるとPerl CPANモジュールガイドの場合には、
Perl CPAN もじゅーる モジュール ガイド がいど つみた 冨田 尚樹 なおき わーくす こーぽれーしょん ワークス コーポレーション」のような文字列がDBに突っ込まれるわけです。


ふむ!すごいですね!これで「perl」でも「cpan」でも「モジュール」でも「冨田」でも検索にかかるようになりました!それだけじゃなく「もじゅーる」みたいな読みでもかかるようになりました!そして、なんと「つみた」でもかかります!すごい!

SQLite FTSを使う


さてさて、最後にDBについて。
今回の本データをためるDBですが、今まで話に出て来た通りSQLiteのFTS4を使いました
(ファイルだから開発も楽だしね!)


FTSの使い方は簡単で、いつものcreate tableの文を以下のようにするだけです。

CREATE VIRTUAL TABLE book USING fts4(
    isbn,
    asin,
    title,
    author,
    manufacturer,
    words
);

「CREATE VIRTUAL TABLE」になっているところと、「USING fts4」になっているところが違う所で、他は特に変わりません。
ただ、UNIQUE INDEX利かななくなっちゃうため、アプリ側でユニーク確認をするようにしました。
(もしかしたら何かやり方あるのかもだけど調べきれず。Stack Overflowにはtriggerとかでどうにかする例があったけどそこまでの話ではなかったので深く見てないです)


SQLの発行方法は「match」というキーワードを使うだけ。
また、SQLite FTS3 and FTS4 Extensionsにあるように「where table名 match 'hogehoge'」というようにテーブル名を指定すればカラムをまたいで探してくれます。


具体的なqueryは以下のような感じ。

SELECT * FROM book WHERE book MATCH 'perl cpan';

これで全てのカラムでperlcpanに引っかかるデータを取り出せます。

DEMO的なやつ


以上でできあがった仕組みがこちらです。
https://github.com/monmon/vgadvent2012-app


それでは、写真で使っている様子を見て行きます。


1. まずjonk-client.plを起動するとISBNの入力待ちになります。

2. あとはひたすらバーコードリーダーでISBNを読みまくります。入力するとすぐに「早く次のISBNくれよぉ!」状態になるのでひたすら入れまくります。

3. で、一方違うターミナルでjson-worker.plを起動させておけば、以下のようにキューに入ったISBNを取り出し、一連の処理を始めます。

4. 結果、SQLiteに本データがたまるので、あとは全文検索で検索してうまーです。

5. 「タイトル」と「作者」の両方にあるような「単語」も検索できて便利ですね!!!!

まとめ


初めは「iPhoneでガンガンscanしてブクログに突っ込んで終わり」みたいなネタを考えたんですが、いざバーコードリーダーでやってみるとISBN読み込んでqueueに突っ込むだけだからよりガンガンscanできて楽しかったです。
(ちょうど大そうじの季節だったし、このネタにして良かった!)
まぁエラー処理とか全然考えてないし、既に「原作」と「漫画」のように2人の作者がいる場合にはauthorカラムに「ARRAY(0x7f8f6ac2fe30)」のように突っ込まれちゃうbugが判明したけど気にしません。
作ってみないとわからないことあるしね!リーン何ちゃらだ!


ということで、「コピペばっかりでも簡単にここまで作れるよ、でもコピペばっかりだと全然考慮が足りないから仕事ではちゃんと理解して物を作っていこう」という話でした!
明日は誕生日が終わったばかりの@tadasyさんです!今からでも間に合うのでみんなmention飛ばして祝ってみてください!