Shogo's Blog

Aug 24, 2013 - 2 minute read - Comments - perl redis

Perl の Redis ライブラリを調べた

最近Redis を使ったコードを書くようになったのですが、 キー名を毎回指定するのがだるいです。 Ruby には redis-objects というのがあって、 Redisのキーをオブジェクトとして扱うことができるようです。 きっと、Perl にも似たようなのあるだろ、って思って調べてみました。 ほしいもの 低レベルなRedisのライブラリはたいていメソッドとRedisのコマンドが一対一対応していて、 次のようなコードになると思います。 $redis->set('key-name', 'piyopiyo'); $redis->get('key_name'); でも、Redisに何か操作をしたいわけじゃなくて、 Redisのキーに対して操作をしたいので、 次のように書けるべきだと思うんです。 my $key = key($redis, 'key-name'); $key->set('piyopiyo'); $key->get(); Redis::Hash, Redis::List Redis::Hashと Redis::Listは Perlのハッシュや配列と同じ操作で Redis にアクセスできるようにするライブラリ。 use utf8; use warnings; use strict; use 5.014; use Redis::Hash; tie my %my_hash, 'Redis::Hash', 'hash_prefix', (server => 'localhost:6379'); # set hash_prefix:hogehoge piyopiyo # set hash_prefix:fugafuga fugufugu $my_hash{hogehoge} = 'piyopiyo'; $my_hash{fugafuga} = 'fugufugu'; # get hash_prefix:hogehoge piyopiyo say $my_hash{hogehoge}; # piyopiyo # keys hash_prefix:* say join ',', keys %my_hash; #fugafuga,hogehoge # keys hash_prefix:* # get hash_prefix:fugafuga # get hash_prefix:hogehoge say join ',', values %my_hash; #fugufugu,piyopiyo # del hash_prefix:hogehoge delete $my_hash{hogehoge}; tie とかよくわかない。 Perl の黒魔術を見た気がしました。

Jul 13, 2013 - 3 minute read - Comments - perl

ランダム抽出アルゴリズムについて考える

数日前に社内IRCで「スマートな非復元抽出の方法はないか」と話題になったので、 ランダムサンプリングのアルゴリズムについて調べたり考えたりしてみた。 復元抽出 非復元抽出の手法って調べてもなかなか出てこない・・・。 ひとまず、復元抽出についてまとめてみましょう。 線形検索 一番簡単な実装方法。 どの区間に入るかを線形検索して求める。 選択肢の個数nとすると計算量はO(n)。 use strict; use warnings; use List::Util qw(sum); sub linear_search_method { my $weights = shift; my $num = shift; my $sum = sum @$weights; my $length = @$weights; my @a; for (1..$num) { my $r = rand($sum); for my $i(0..$length-1) { $r -= $weights->[$i]; if($r < 0) { push @a, $i; last; } } } return \@a; } print join ', ', @{linear_search_method [1,2,3], 100}; バイナリサーチ あらかじめ累積分布表を作っておき、どの区間に入るかをバイナリサーチ。 準備にO(n)、選択に O(log n)かかる。

May 15, 2013 - 2 minute read - Comments - chrome perl

Google Cloud Messaging for Chrome を試してみた

少し前にGoogle Cloud Messaging for Chrome が発表されました。 Android向けに提供されていた Push 通信の仕組みである GCM の Chrome 版です。 ちょうど GCM for Android に触っていたところだったので、 for Chrome のほうも試してみることにしました。 拡張機能の登録 公式ページの説明にしたがって、 APIを使えるようにします。 GCMはOAuth2.0で認証を行うので、 クライアントIDを作る Refresh Token を作る という2ステップが必要。 クライアントIDを作る まず、新しい OAuth2.0 のアプリを作成。 拡張機能をアップロードする予定のGoogleアカウントで以下の作業して Client IDを作ります。 Google APIs Console にログインする ** Create… ** メニューから新しいプロジェクトを作成 “Services” を開いて ** Google Cloud Messaging for Chrome API ** を有効化 “API Access” を開いて ** Create an OAuth 2.0 cliend ID… ** というボタンをクリック branding information を適当に入力 “Application Type” という項目の “Web application” を選択 “Create client ID”!! Client ID と Client Secret が表示されるのでメモしておきましょう。

May 12, 2013 - 1 minute read - Comments - RaspberryPi

RaspberryPiからメールを送る

RaspberryPi に cron を仕込んで定期実行をやってみようと考えました。 cron の設定自体は crontab -e コマンドを実行すれば簡単にできます。 ただ、これだけだとちゃんと動いているか少し心配なので、 エラーが起きた時に何か通知して欲しい。 普通なら設定ファイルに MAILTO=hogehoge@example.com と書いておくと メールが送られるはずなのですが、 メールサーバが動いてないのでうまくいかない・・・。 そういうわけで、RaspberryPiからメールを送るための設定をしたのでメモ。 MTAをインストールする Raspberry Pi には標準でMTA(Message Transfer Agent)が入ってないようなのでインストール。 今回はPostfixを採用 sudo apt-get install postfix 最初、Sendmailも試してみたんだけど、送信者マスカレードがなぜかうまく行かなったので断念。 後述するように、この設定がないとスパムフィルタに引っかかってしまうのです。 プロバイダのSMTPにリレーしてもらう 実際にメールを送りには以下の条件を満たす必要があるようです。 送信元のドメインを引ける 固定IPからのアクセス 固定IPなんて自前で持ってないし、 cron からのメールは送信元が pi@raspberrypi になってしまいドメインを引けません。 そのためそのままではスパムメールとして扱われてしまい、メールが届きません。 そこで、プロバイダが提供しているSMTPサーバにメールをリレーしてもらいます。 /etc/postfix/main.cfに以下の行を追加します。 sender_canonical_maps = regexp:/etc/postfix/canonical relayhost = [smtp.example.com]:587 smtp_sasl_auth_enable = yes smtp_sasl_password_maps = hash:/etc/postfix/relay_password smtp_sasl_security_options = noanonymous プロバイダにリレーしてもらうには SMTP-Auth で認証する必要があるので、 ユーザ名とパスワードを設定しておきます。 smtp.example.com hogehoge:your-password postmapコマンドを使って、Postfixから扱える形式に変換します。 $ postmap hash:/etc/postfix/relay_password さらに、エンベロープのFromがプロバイダから提供されたメールアドレスでないと メールをリレーしてくれないので、 すべてのメールのFromをすべて書き換えるよう設定します。

May 12, 2013 - 1 minute read - Comments - RaspberryPi Python

RaspberryPiでhttps通信が失敗するのを何とかする

RaspberryPiをネットつないでみたので、PythonからいろんなURLを叩いて遊んでいたんだけど、 一部のhttps通信が Connection Timed Out で失敗しちゃう。 プログラムの問題かと思ったけど、curlで叩いてもやっぱりタイムアウト。 Macで全く同じ事をするとうまくいく・・・。 いろいろ調べて、何とかしてみたお話。 原因 接続先がTLSv1にしか対応していないのにSSLv3でアクセスしようとしていたことが問題だったらしい。 明示的にTLSv1を使うように指定して curl を叩いてみるとうまくいった。 $ curl --tlsv3 https://hogehoge なぜRaspberryPiではダメで Macでは成功するのか、という根本的な原因はよくわからなかった。 SSLv3に対応していないなら自動的にフォールバックしてくれてもよさそうなものだけど、 なぜうまく行かないんだろう・・・? Pythonでの対処 PythonでもTLSv3を使えばうまくいくはずなんだけど、 暗号化方式を指定するオプションは見当たらない(2.7での話)。 どうやら標準ライブラリのファイルを直接書き換えるか、 実行時に中身を入れ替えるかしないとできないみたいだ。 この問題普通のUbuntuでも起こるらしく、 そのフォーラムで置き換えコードを見つけた。 import httplib from httplib import HTTPConnection, HTTPS_PORT import ssl class HTTPSConnection(HTTPConnection): "This class allows communication via SSL." default_port = HTTPS_PORT def __init__(self, host, port=None, key_file=None, cert_file=None, strict=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, source_address=None): HTTPConnection.__init__(self, host, port, strict, timeout, source_address) self.key_file = key_file self.cert_file = cert_file def connect(self): "Connect to a host on a given (SSL) port.

May 9, 2013 - 1 minute read - Comments - Python Twitter

tweepyでApplication-only authenticationしてみた

Twitter の API リファレンスを久しぶりに見たら、 Application-only authenticationとかいうのを発見。 特定のユーザと関連付けられない代わりに、普通に認証するより制限が緩いみたい。 3月に追加されてたらしい。 知らなかった・・・。 最近API叩いてなかったからな・・・。 便利そうなので、Python用のTwitterライブラリであるTweepyから使ってみた。 AuthHandler Tweepy用のAuthHandler。 認証部分は TwitterのApplication-only authenticationを試してみた のページからほぼコピペ。 import tweepy import urllib import urllib2 import base64 import json class AppAuthHandler(tweepy.auth.AuthHandler): TOKEN_URL = 'https://api.twitter.com/oauth2/token' def __init__(self, consumer_key, consumer_secret): token_credential = urllib.quote(consumer_key) + ':' + urllib.quote(consumer_secret) credential = base64.b64encode(token_credential) value = {'grant_type': 'client_credentials'} data = urllib.urlencode(value) req = urllib2.Request(self.TOKEN_URL) req.add_header('Authorization', 'Basic ' + credential) req.add_header('Content-Type', 'application/x-www-form-urlencoded;charset=UTF-8') response = urllib2.urlopen(req, data) json_response = json.loads(response.read()) self.

Apr 13, 2013 - 1 minute read - Comments -

社内ISUCONに参加した

先日、社内 ISUCON(良い感じにスピードアップコンテスト) に参加してきました。 Livedoorで開催されたISUCONのミニ版で、 Webアプリをひたすら高速化するコンテストです。 高速化の対象はNoPaste。 テキストを共有するWebアプリです。 テキストの投稿・投稿の閲覧・投稿にスターをつける の3つの動作ができる簡単なアプリです。 新卒 vs 先輩ということで、それぞれ4チームが参戦。 チームは二人一組で僕は @Maco_Tasu くんと一緒でした。 @Maco_Tasuくんのブログも参照。 Recent Posts 生成クエリの高速化 高速化前のアプリのベンチマークの結果、スコアは77(≒1分あたりの捌いたリクエスト数)。 何も考えずにデータベースの全行を舐めるクエリを書いていたので、まあ、妥当なスコアですね。 重いのはサイドバーに表示される Recent Posts。 Recent Posts は表示回数が多く、 複数の行、複数のテーブルへのアクセスが発生するため、 きっとここがボトルネックになるだろうと予測してました。 そこで最初にこの部分を解決することにしました。 とりあえずインデックスを張る クエリを修正してアクセスする行を最小化 スターのカウントした結果をテーブルに格納 オリジナルのデータベース構成では、スターした回数だけ行が増えてました 必要なのは投稿ごとのスター数なので、独立したテーブルに この時点で早くも重大なバグを組み込んでしまったことに、この時はまだ気がついていなかった・・・ nginxによる静的ファイル配信 僕がクエリをいじっている間、@Maco_Tasuくんには サーバの設定をお願いしました。 ログの様子を眺めてみると、cssとかjsとかの静的ファイルが結構な量ありました。 最初のスクリプトでは静的ファイルの配信もアプリでやってたので、 これをnginxを使って配信するように変更。 その他のリクエストはリバースプロキシを設定してアプリに投げます。 Starlet と Server::Starter リバースプロキシを設定する際にアプリの起動スクリプトを編集する必要があったので、 ついでに起動時の設定を色々変更。 PSGI実行のStarletというのが速いと聞いてこれを採用。 Starlet使い方調べてたら、Server::Starterを使った例が出てきたので一緒にインストール。 ワーカーの数の数は適当に10個にしました。 ここで2回目のベンチマークを実行。 スコア1300程度で、その時点のトップ! SSIを使ったサイドバーの埋め込み お昼を挟んで、さらなる高速化を目指します。 topコマンドを眺めているとPerlで作ったアプリの負荷が大きい。 ほとんどテンプレートエンジンを呼び出しているだけの単純なコードなので、 ここを高速化するのは面倒くさい。 そこで、前段のnginxでキャッシュする作戦を採用することにしました。 もっともキャッシュが有効なのはサイドバーだろうと予想。 クエリの最適化をしたとは言え、サイドバーには100件程度の投稿が表示されるので、 クエリ実行にもレンダリングにも時間がかかるはず。 さらにすべてのページでサイドバーは共有できるので、大幅な高速化が期待できるはずです。 過去のISUCONの記事にSSI(Server Side Include)を使った例があったのを思い出し、 これを使ってサイドバーのみキャッシュ、nginx内でサイドバーを埋め込むように。

Mar 5, 2013 - 1 minute read - Comments - emacs

出、出〜〜〜〜wwww emacsをふたつ以上実行奴〜〜〜〜www(emacsclient編)

emacsを使って編集している最中にシェル操作をしたくなって, C-z を押してバックグラウンドにしてシェル操作. その後,emacsに戻ってくるには fg コマンドを打つ必要があるんだけど, 間違えてもう一回 emacs を新しく立ち上げるというミスを何度もやってしまう・・・. これに対し,猫型さんが複数起動しようとすると警告を出してくれるようにしてくれました. (出、出〜〜〜〜wwww emacsをふたつ以上実行奴〜〜〜〜www) 警告してくれるのはありがたいんだけど, これだとシェル操作中に別のファイルの編集をしたいと思っても,警告が返ってくるだけ. emacs をフォアグラウンドに出して,ファイルの指定をやり直さなきゃいけない. 僕はファイルの編集をしたいんだ!! わかったから早く編集させろ!!! emacsclient 単なる警告じゃなくて, 「裏で動いていたemacsを復帰させ,新しいバッファを開く」 ところまで自動的にやってくれると嬉しいですね. まず,emacs をデーモンモードで起動しておきます. emacs --daemon emacsclient コマンドでファイルを開くと, emacs デーモンさんが新しいバッファで開いてくれます. オプションに -nw を指定しておくと現在の端末で閲覧編集することができます. emacsclient -nw hoge.txt 終了するにはC-x 5 0. C-x C-cでも終了できるけど, デーモンにバッファが残ってしまうみたい. aオプションでemacs デーモンが起動してないときに 編集に使うエディタを指定できる. 空っぽにしておくと,emacs をデーモンモードで起動してくれる. emacsclient -nw -a '' hoge.txt emacs デーモンを終了させるのは以下のコマンド. emacsclient -e '(kill-emacs)' emacsclient に対して alias を作っておけば, 複数起動かどうか意識せずに使えますね. alias emacs='emacsclient -nw -a ""' 参考 emacsclientを使おう emacsclient の使い方の種類と、便利な使い方 emacsclientを終了する方法

Mar 2, 2013 - 1 minute read - Comments - LaTeX epub

LaTeX2EPUBで電子書籍を作ってみる

LaTeXで書いた文章を電子書籍にしたくなったので, LaTeX2EPUBを使ってみました. LaTeX2EPUBはLaTeXMLとReVIEWに依存しているようなので, それぞれインストールしていきます. あと,数式の変換とかにLaTeXを使っているので別途用意する必要あり. いろんなディストリビューションがあるけど, ここでは TeX Live 2012 を使いました. LaTeXML のインストール LaTeXMLはLaTeXの文章をXML形式に変換するソフト. そこからさらにXSLTを使ってXHTMLへ変換できる. ドキュメントに従って 依存するライブラリをインストール. perl -MCPAN -e shell cpan> install DB_File, Parse::RecDescent, File::Which cpan> install XML::LibXML, XML::LibXSLT ドキュメントが少し古いらしく,これだけでは不十分だった. 追加でParse::RecDescentとImage::Magickもインストールしておく. cpan> install Parse::RecDescent cpan> quit yum install ImageMagick-perl 後はソースを取ってきてmakeするだけ. 現時点での最新版0.7.0をインストールした. wget http://dlmf.nist.gov/LaTeXML/releases/LaTeXML-0.7.0.tar.gz tar zxvf LaTeXML-0.7.0.tar.gz cd LaTeXML-0.7.0 perl Makefile.PL make make test make install ReVIEW のインストール ReVIEWは簡単なマークアップ言語で書かれたテキストから PDFやEPUBを作成するためのスクリプトです. このなかのEPUB作成機能に依存しているようなのでインストールしておきます. ReVIEWはgemで簡単インストール. gem install review LaTeX2EPUB のインストール LaTeX2EPUB本体をインストール. 本家の日本語化対応が少し不十分だったので 改造版を上げといた. これをダウンロードしてパスの通ったところに置けばインストール完了.

Feb 21, 2013 - 1 minute read - Comments -

AWSをはじめてみた

Amazon Web Services(AWS)でEC2でも使ってみようかと, 登録を試みました. が,しかし,電話認証の段階で何度やってもシステムエラー・・・. An Error Has Occured システムエラー 電話確認要求を処理できません。後でもう一度お試しください。 こういう時は,とりあえずエラーメッセージでググってみましょう. なんだかそれっぽい記事が見つかりました. AWSアカウント開設で”電話確認要求を処理できません。後でもう一度お試しください。”と怒られ続けた Amazon Developer Forums: 電話による身元確認でエラー発生 どうやら,登録時に入力した「支払い方法」と「アドレス情報」が正しく反映されていないことが原因のようです. アカウントの管理 画面から,「支払い方法」を選び,クレジットカードや請求先を記入します. アドレス情報は「登録内容の変更」から変更可能です. AWSをはじめてみたというエントリだけど, 実はじょりぼっとの「買うべき?」機能を実装するために, AWSのProduct Advertising APIを前々から使っていたのでした. このAPI使うだけなら支払い方法などの入力は不要だったので, 必要最低限の情報しか入力していませんでした. 住所とかの入力もしなかったのですが, 自分が確認したときはアドレス情報の国設定が何故かアメリカになってました. これのせいですかね? 詳しい原因はよくわかりませんが,とりあえず,「支払い方法」と「登録内容の変更」の全項目を正しく入力したら認証が出来ました. 1年は無料でいろいろ遊べるらしいので,何か動かして遊んでみましょう.