Changeset 27033
- Timestamp:
- 12/18/08 23:59:19 (4 years ago)
- Files:
-
- 1 modified
Legend:
- Unmodified
- Added
- Removed
-
websites/perl-users.jp/html/articles/advent-calendar/adventcal.rss
r26833 r27033 5 5 <link>http://perl-users.jp/articles/advent-calendar/2008/</link> 6 6 <title>JPerl Advent Calendar 2008</title> 7 <pubDate>T ue, 16 Dec 2008 08:59:37+0900</pubDate>7 <pubDate>Thu, 18 Dec 2008 23:56:31 +0900</pubDate> 8 8 <item> 9 9 <author>nobody@example.com</author> … … 15 15 -e syntax OK「sub foo() { }」のように書くと、この関数が引数をとらないことを宣言できます。この関数が定数を返す場合、この関数は定数として展開されます。 16 16 </description> 17 <dc:date>2008-12- 05T03:45:03Z</dc:date>17 <dc:date>2008-12-16T11:03:38Z</dc:date> 18 18 <title>定数の展開</title> 19 <pubDate> Fri, 05 Dec 2008 03:45:03-0000</pubDate>19 <pubDate>Tue, 16 Dec 2008 11:03:38 -0000</pubDate> 20 20 <content:encoded><body><h1>定数の展開</h1><pre><code>% perl -MO=Deparse,-print -e&apos;sub foo() { 0 }; warn foo&apos; 21 21 sub foo () { 0 } … … 23 23 -e syntax OK</code></pre><p>「<code>sub foo() { }</code>」のように書くと、この関数が引数をとらないことを宣言できます。</p><p>この関数が定数を返す場合、この関数は定数として展開されます。</p></body> 24 24 </content:encoded> 25 <dcterms:modified>2008-12- 05T03:45:03Z</dcterms:modified>25 <dcterms:modified>2008-12-16T11:03:38Z</dcterms:modified> 26 26 <guid isPermaLink="false">tag:perl-users.jp,2006:http://perl-users.jp/articles/advent-calendar/2008/01.html</guid> 27 27 </item> … … 44 44 he is soffritto「\my $vars」と書く事により変数の定義をしつつ戻り値で、その定義した変数のリファレンスを返す。よって、関数の引数に「\my $xxx」と書くとコンパクトに書ける。が、これはTTくらいでしか使ってるの見た事無い。つぎのバトンはid:lestrratさん。 45 45 </description> 46 <dc:date>2008-12- 05T03:45:03Z</dc:date>46 <dc:date>2008-12-16T11:03:38Z</dc:date> 47 47 <title>myのリファレンス</title> 48 <pubDate> Fri, 05 Dec 2008 03:45:03-0000</pubDate>48 <pubDate>Tue, 16 Dec 2008 11:03:38 -0000</pubDate> 49 49 <content:encoded><body><h1>myのリファレンス</h1><pre><code>$ cat cool.pl 50 50 use strict; … … 61 61 he is soffritto</code></pre><p>「<code>\my $vars</code>」と書く事により変数の定義をしつつ戻り値で、その定義した変数のリファレンスを返す。</p><p>よって、関数の引数に「<code>\my $xxx</code>」と書くとコンパクトに書ける。</p><p>が、これは<abbr title="Template Toolkit">TT</abbr>くらいでしか使ってるの見た事無い。</p><p>つぎのバトンはid:lestrratさん。</p></body> 62 62 </content:encoded> 63 <dcterms:modified>2008-12- 05T03:45:03Z</dcterms:modified>63 <dcterms:modified>2008-12-16T11:03:38Z</dcterms:modified> 64 64 <guid isPermaLink="false">tag:perl-users.jp,2006:http://perl-users.jp/articles/advent-calendar/2008/02.html</guid> 65 65 </item> … … 94 94 }なお、全てのeval{}でこの処理を加える必要はありませんが、該当する箇所にエラーがあってもわざとそれを無視する今回のような場合には明示的にlocalをつけておいたほうが安全です。次はid:hidekさん 95 95 </description> 96 <dc:date>2008-12- 07T09:21:35Z</dc:date>96 <dc:date>2008-12-16T13:43:19Z</dc:date> 97 97 <title>$@はグローバル変数</title> 98 <pubDate> Sun, 07 Dec 2008 09:21:35-0000</pubDate>99 <content:encoded><body><h1>$@はグローバル変数</h1><p>あまり意識されていませんが、<code>$@</code>はグローバル変数です。気をつけないとおかしなことになります。以下のコードでは<code>die()</code>で例外を発生させているので「<samp>Error is Dummy error</samp>」と表示されるように見えますが、表示されません。</p><pre ><code> package Hoge;98 <pubDate>Tue, 16 Dec 2008 13:43:19 -0000</pubDate> 99 <content:encoded><body><h1>$@はグローバル変数</h1><p>あまり意識されていませんが、<code>$@</code>はグローバル変数です。気をつけないとおかしなことになります。以下のコードでは<code>die()</code>で例外を発生させているので「<samp>Error is Dummy error</samp>」と表示されるように見えますが、表示されません。</p><pre class="lang-perl"><code> package Hoge; 100 100 sub new { bless {}, shift } 101 101 sub cleanup { … … 117 117 } else { 118 118 print &quot;Everything OK!\n&quot;; 119 }</code></pre><p><code>eval</code>が<code>die()</code>によって終了し、スコープが切り替わる段階で<code>DESTROY</code>が呼ばれます。その中で<code>eval{}</code>をもう一度呼んでいますが、ここではエラーがなかったため<code>$@</code>が空に設定されるのです。よって、エラーの値を出力するころにはすでに<code>$@</code>は空で、エラーを検知できません。</p><p>このようなグローバル変数を変更する可能性のあるコードを書く場合は<code>local</code>で修飾すると良いでしょう。</p><pre ><code> sub DESTROY {119 }</code></pre><p><code>eval</code>が<code>die()</code>によって終了し、スコープが切り替わる段階で<code>DESTROY</code>が呼ばれます。その中で<code>eval{}</code>をもう一度呼んでいますが、ここではエラーがなかったため<code>$@</code>が空に設定されるのです。よって、エラーの値を出力するころにはすでに<code>$@</code>は空で、エラーを検知できません。</p><p>このようなグローバル変数を変更する可能性のあるコードを書く場合は<code>local</code>で修飾すると良いでしょう。</p><pre class="lang-perl"><code> sub DESTROY { 120 120 my $self = shift; 121 121 local $@; … … 123 123 }</code></pre><p>なお、全ての<code>eval{}</code>でこの処理を加える必要はありませんが、該当する箇所にエラーがあってもわざとそれを無視する今回のような場合には明示的に<code>local</code>をつけておいたほうが安全です。</p><p>次はid:hidekさん</p></body> 124 124 </content:encoded> 125 <dcterms:modified>2008-12- 07T09:21:35Z</dcterms:modified>125 <dcterms:modified>2008-12-16T13:43:19Z</dcterms:modified> 126 126 <guid isPermaLink="false">tag:perl-users.jp,2006:http://perl-users.jp/articles/advent-calendar/2008/03.html</guid> 127 127 </item> … … 156 156 my $scalar_ref = anon_scalar('a');と書くことも出来ます次はid:hirose31さん 157 157 </description> 158 <dc:date>2008-12- 07T09:21:35Z</dc:date>158 <dc:date>2008-12-16T13:43:19Z</dc:date> 159 159 <title>無名スカラー変数のリファレンス</title> 160 <pubDate> Sun, 07 Dec 2008 09:21:35-0000</pubDate>161 <content:encoded><body><h1>無名スカラー変数のリファレンス</h1><pre ><code> my $array_ref = [qw/a b c/];160 <pubDate>Tue, 16 Dec 2008 13:43:19 -0000</pubDate> 161 <content:encoded><body><h1>無名スカラー変数のリファレンス</h1><pre class="lang-perl"><code> my $array_ref = [qw/a b c/]; 162 162 my $hash_ref = +{1 =&gt; &apos;a&apos;, 2 =&gt; &apos;b&apos;, 3 =&gt; &apos;c&apos;}; 163 my $code_ref = sub {&apos;a&apos;};</code></pre><p> Perlでは、無名配列のリファレンス 無名ハッシュのリファレンス、無名サブルーチンのリファレンスはこのように書けますが、無名スカラーのリファレンスを書く特別な構文はないので、以下のように書きます。 </p><pre ><code> my $scalar_ref = \do {&apos;a&apos;};</code></pre><p> これはDamian Conway先生オススメのインサイドアウトオブジェクトのコンストラクタで見られる書き方です。 </p><pre><code> {163 my $code_ref = sub {&apos;a&apos;};</code></pre><p> Perlでは、無名配列のリファレンス 無名ハッシュのリファレンス、無名サブルーチンのリファレンスはこのように書けますが、無名スカラーのリファレンスを書く特別な構文はないので、以下のように書きます。 </p><pre class="lang-perl"><code> my $scalar_ref = \do {&apos;a&apos;};</code></pre><p> これはDamian Conway先生オススメのインサイドアウトオブジェクトのコンストラクタで見られる書き方です。 </p><pre class="lang-perl"><code> { 164 164 package Person; 165 165 my %props = (); … … 179 179 # $person-&gt;{name} = &apos;Tom&apos;; #エラー 180 180 $person-&gt;name(&apos;Tom&apos;); 181 warn $person-&gt;name; # Tom</code></pre><p> こうすることによってスカラーをバックエンドとするオブジェクトを作ることができ、オブジェクトのカプセル化をすることができます。 </p><p>さらにid:tokuhiromさんの指摘で </p><pre ><code> my $scalar_ref = \&apos;a&apos;;</code></pre><p>でもよいことを知りました。</p><p>ただしこの場合はimmutableされて変更ができなくなるので先ほどの例では「<samp>Modification of a read-only value</samp>」とエラーになります。</p><pre><code>Template-&gt;new-&gt;process(\&apos;[% foo %]&apos;, {foo =&gt; 3})</code></pre><p>とかでも使うそうです。</p><p>typseterさんに指摘されて思い出しましたが、そういえばDBICでも使いますね。</p><pre><code> select =&gt; [181 warn $person-&gt;name; # Tom</code></pre><p> こうすることによってスカラーをバックエンドとするオブジェクトを作ることができ、オブジェクトのカプセル化をすることができます。 </p><p>さらにid:tokuhiromさんの指摘で </p><pre class="lang-perl"><code> my $scalar_ref = \&apos;a&apos;;</code></pre><p>でもよいことを知りました。</p><p>ただしこの場合はimmutableされて変更ができなくなるので先ほどの例では「<samp>Modification of a read-only value</samp>」とエラーになります。</p><pre class="lang-perl"><code>Template-&gt;new-&gt;process(\&apos;[% foo %]&apos;, {foo =&gt; 3})</code></pre><p>とかでも使うそうです。</p><p>typseterさんに指摘されて思い出しましたが、そういえばDBICでも使いますね。</p><pre class="lang-perl"><code> select =&gt; [ 182 182 \&quot;date_trunc (&apos;day&apos;, created_on) as created_day&quot;, 183 183 { count =&gt; &apos;id&apos; } 184 ]</code></pre><p>または音速のXS使いid:gfxさんのData::Utilを使うと</p><pre ><code> use Data::Util qw(anon_scalar);184 ]</code></pre><p>または音速のXS使いid:gfxさんのData::Utilを使うと</p><pre class="lang-perl"><code> use Data::Util qw(anon_scalar); 185 185 my $scalar_ref = anon_scalar(&apos;a&apos;);</code></pre><p>と書くことも出来ます</p><p>次はid:hirose31さん</p></body> 186 186 </content:encoded> 187 <dcterms:modified>2008-12- 07T09:21:35Z</dcterms:modified>187 <dcterms:modified>2008-12-16T13:43:19Z</dcterms:modified> 188 188 <guid isPermaLink="false">tag:perl-users.jp,2006:http://perl-users.jp/articles/advent-calendar/2008/04.html</guid> 189 189 </item> … … 223 223 4 <@> leave[1 ref] vKP/REFC というわけでopcodeでたった3つ分だけ効率的です。 次はid:typesterさん 224 224 </description> 225 <dc:date>2008-12- 07T09:21:35Z</dc:date>225 <dc:date>2008-12-16T11:03:38Z</dc:date> 226 226 <title>効率的な変数宣言</title> 227 <pubDate> Sun, 07 Dec 2008 09:21:35-0000</pubDate>227 <pubDate>Tue, 16 Dec 2008 11:03:38 -0000</pubDate> 228 228 <content:encoded><body><h1>効率的な変数宣言</h1><p><code>my %hash=();</code>より<code>my %hash;</code>の方が効率的です。 </p><p> どのぐらい効率的かというと: </p><pre><code>$ perl -MO=Concise -e &apos;my %hash=()&apos; 229 229 7 &lt;@&gt; leave[1 ref] vKP/REFC -&gt;(end) … … 257 257 4 &lt;@&gt; leave[1 ref] vKP/REFC<code></code></code></pre><p> というわけでopcodeでたった3つ分だけ効率的です。 </p><p>次はid:typesterさん</p></body> 258 258 </content:encoded> 259 <dcterms:modified>2008-12- 07T09:21:35Z</dcterms:modified>259 <dcterms:modified>2008-12-16T11:03:38Z</dcterms:modified> 260 260 <guid isPermaLink="false">tag:perl-users.jp,2006:http://perl-users.jp/articles/advent-calendar/2008/05.html</guid> 261 261 </item> … … 269 269 などのように書くことも出来るようになっているそうです。次は dann さん。 270 270 </description> 271 <dc:date>2008-12- 06T10:19:09Z</dc:date>271 <dc:date>2008-12-16T11:03:38Z</dc:date> 272 272 <title>ファイルテスト演算子をつかいまくる</title> 273 <pubDate> Sat, 06 Dec 2008 10:19:09-0000</pubDate>273 <pubDate>Tue, 16 Dec 2008 11:03:38 -0000</pubDate> 274 274 <content:encoded><body><h1>ファイルテスト演算子をつかいまくる</h1><p><code>_</code> という特殊変数があり、これは最後にテスト演算子に渡した引数を示します。</p><p>よって、ファイルでありかつリーダブルでありかつ実行可能であるというようなテストをしたい場合は、</p><pre><code>-f $file &amp;&amp; -r $file &amp;&amp; -x $file; 275 275 </code></pre><p>とするかわりに、</p><pre><code>-f $file &amp;&amp; -r _ &amp;&amp; -x _; … … 277 277 </code></pre><p>などのように書くことも出来るようになっているそうです。</p><p>次は dann さん。</p></body> 278 278 </content:encoded> 279 <dcterms:modified>2008-12- 06T10:19:09Z</dcterms:modified>279 <dcterms:modified>2008-12-16T11:03:38Z</dcterms:modified> 280 280 <guid isPermaLink="false">tag:perl-users.jp,2006:http://perl-users.jp/articles/advent-calendar/2008/06.html</guid> 281 281 </item> … … 318 318 コマンドラインスイッチは、perldoc perlrunで調べられます。他にも便利なコマンドラインスイッチや特殊変数があるので、是非読んでみてください次は yusukebe さん。 319 319 </description> 320 <dc:date>2008-12- 06T16:53:17Z</dc:date>320 <dc:date>2008-12-16T11:03:38Z</dc:date> 321 321 <title>コマンドラインスイッチと特殊変数を使いまくる</title> 322 <pubDate> Sat, 06 Dec 2008 16:53:17-0000</pubDate>322 <pubDate>Tue, 16 Dec 2008 11:03:38 -0000</pubDate> 323 323 <content:encoded><body><h1>コマンドラインスイッチと特殊変数を使いまくる</h1><p>ファイルの奇数行だけを表示したいといったときに、例えばどんなコードを書くでしょうか。</p><p>例えば、以下のように書く事ができます。</p><pre> 324 324 <code> … … 355 355 </pre><p>コマンドラインスイッチは、perldoc perlrunで調べられます。</p><p>他にも便利なコマンドラインスイッチや特殊変数があるので、是非読んでみてください</p><p>次は yusukebe さん。</p></body> 356 356 </content:encoded> 357 <dcterms:modified>2008-12- 06T16:53:17Z</dcterms:modified>357 <dcterms:modified>2008-12-16T11:03:38Z</dcterms:modified> 358 358 <guid isPermaLink="false">tag:perl-users.jp,2006:http://perl-users.jp/articles/advent-calendar/2008/07.html</guid> 359 359 </item> … … 380 380 こんな感じで使えます。 ./dl.pl http://metiss.blog92.fc2.com/blog-entry-142.html Tipsというより便利スクリプトの紹介になってしまいしたことご了承くだされ。 さて、次のバトンは _33rpm さんに渡します。 381 381 </description> 382 <dc:date>2008-12- 08T14:55:47Z</dc:date>382 <dc:date>2008-12-16T11:03:38Z</dc:date> 383 383 <title>ワンライナーで画像収集</title> 384 <pubDate> Mon, 08 Dec 2008 14:55:47-0000</pubDate>384 <pubDate>Tue, 16 Dec 2008 11:03:38 -0000</pubDate> 385 385 <content:encoded><body><h1>ワンライナーで画像収集</h1><p>某掲示板まとめブログのエッチな記事に載っている画像を収集したいときに、Perl ではワンライナー、つまり1行のコマンドで書くことができます。例えば、女の子の太もも画像 ( http://metiss.blog92.fc2.com/blog-entry-142.html ) にリンクされている画像をカレントディレクトリにダウンロードしたければ以下のようなコマンドをたたくだけです。</p><pre> 386 386 <code> … … 401 401 </pre><p> こんな感じで使えます。 </p><code> ./dl.pl http://metiss.blog92.fc2.com/blog-entry-142.html </code><p> Tipsというより便利スクリプトの紹介になってしまいしたことご了承くだされ。 さて、次のバトンは _33rpm さんに渡します。 </p></body> 402 402 </content:encoded> 403 <dcterms:modified>2008-12- 08T14:55:47Z</dcterms:modified>403 <dcterms:modified>2008-12-16T11:03:38Z</dcterms:modified> 404 404 <guid isPermaLink="false">tag:perl-users.jp,2006:http://perl-users.jp/articles/advent-calendar/2008/08.html</guid> 405 405 </item> … … 450 450 こんな書き方も出来ます。さて、次のバトンは nekokak さんに渡します。 451 451 </description> 452 <dc:date>2008-12- 09T05:49:01Z</dc:date>452 <dc:date>2008-12-16T11:03:38Z</dc:date> 453 453 <title>全てのコードにテストを(標準入力乗っ取り編)</title> 454 <pubDate>Tue, 09 Dec 2008 05:49:01-0000</pubDate>454 <pubDate>Tue, 16 Dec 2008 11:03:38 -0000</pubDate> 455 455 <content:encoded><body><h1>全てのコードにテストを(標準入力乗っ取り編)</h1><p> テストコード書くのってピタゴラ装置作るみたいで楽しいですよね。 というわけであらゆるコードにはテストコードを用意してあげたい! けどそういうときちょっとめんどくさいのが、昔ながらのCGIスクリプトやメールフィルタなどのフィルタスクリプト、要はSTDINからデータが入ってくる前提のコードです。 make testやproveからテストする前提だと標準入力に何も渡さないし…、 というわけでこんなかんじでテストを書いてみます。 </p><pre> 456 456 <code> … … 495 495 </pre><p> こんな書き方も出来ます。さて、次のバトンは nekokak さんに渡します。 </p></body> 496 496 </content:encoded> 497 <dcterms:modified>2008-12- 09T05:49:01Z</dcterms:modified>497 <dcterms:modified>2008-12-16T11:03:38Z</dcterms:modified> 498 498 <guid isPermaLink="false">tag:perl-users.jp,2006:http://perl-users.jp/articles/advent-calendar/2008/09.html</guid> 499 499 </item> … … 532 532 このようにファイルを指定せずに書けば、unlinkも必要なく、ゴミファイルが残る心配もありませんね。次は zigorou さん。 533 533 </description> 534 <dc:date>2008-12- 09T16:14:48Z</dc:date>534 <dc:date>2008-12-16T11:03:38Z</dc:date> 535 535 <title>SQLiteを使ったテストのtips</title> 536 <pubDate>Tue, 09 Dec 2008 16:14:48 -0000</pubDate>536 <pubDate>Tue, 16 Dec 2008 11:03:38 -0000</pubDate> 537 537 <content:encoded><body><h1>SQLiteを使ったテストのtips</h1><p>DB周りのモジュールを開発している場合、テストDBにSQLiteを使う事が良くあります。</p><p>その際、普通であれば以下のようなテストコードを書くと思います</p><pre><code>use Test::More tests =&gt; 1; 538 538 use DBI; … … 565 565 </code></pre><p>このようにファイルを指定せずに書けば、unlinkも必要なく、ゴミファイルが残る心配もありませんね。</p><p>次は zigorou さん。</p></body> 566 566 </content:encoded> 567 <dcterms:modified>2008-12- 09T16:14:48Z</dcterms:modified>567 <dcterms:modified>2008-12-16T11:03:38Z</dcterms:modified> 568 568 <guid isPermaLink="false">tag:perl-users.jp,2006:http://perl-users.jp/articles/advent-calendar/2008/10.html</guid> 569 569 </item> … … 612 612 のような形になります。またエラーハンドリングの package 名に対する正規表現によるグルーピングもできます。詳しくは perldoc Carp::Clan を見て下さい。 次は id:kazuhooku さん。 613 613 </description> 614 <dc:date>2008-12-1 5T00:29:28Z</dc:date>614 <dc:date>2008-12-16T13:43:19Z</dc:date> 615 615 <title>Carp::Clan でエラーハンドリングのススメ</title> 616 <pubDate> Mon, 15 Dec 2008 00:29:28-0000</pubDate>616 <pubDate>Tue, 16 Dec 2008 13:43:19 -0000</pubDate> 617 617 <content:encoded><body><h1>Carp::Clan でエラーハンドリングのススメ</h1><div class="section"><h2>Carp, Carp::Clan で共通のこと</h2><dl><dt>carp</dt><dd>呼び出し元の分かる warn</dd><dt>cluck</dt><dd>carp + stack backtrace</dd><dt>croak</dt><dd>呼び出し元の分かる die</dd><dt>confess</dt><dd>croak + stack backtrace</dd></dl><p>但し Carp の場合は <var>cluck</var> や <var>confess</var> を明示的に使うよりも、</p><pre><code>$ perl -MCarp=verbose target.pl 618 </code></pre><p> という使い方でも stacktrace を得ることが出来ます。Carp::Clan の場合は、 </p><pre ><code>use Carp::Clan;618 </code></pre><p> という使い方でも stacktrace を得ることが出来ます。Carp::Clan の場合は、 </p><pre class="lang-perl"><code>use Carp::Clan; 619 619 620 620 $Carp::Clan::Verbose = 1 if $ENV{DEBUG}; … … 623 623 $ perl carp_clan.pl 624 624 NS::A::do_a(): warning message!!! at carp_clan.pl line 19 625 </code></pre><p> という具合です。具体的なコードでは、 </p><pre ><code>#!/usr/bin/perl625 </code></pre><p> という具合です。具体的なコードでは、 </p><pre class="lang-perl"><code>#!/usr/bin/perl 626 626 627 627 use strict; … … 655 655 </code></pre><p> のような形になります。またエラーハンドリングの package 名に対する正規表現によるグルーピングもできます。詳しくは perldoc Carp::Clan を見て下さい。 </p><p> 次は id:kazuhooku さん。 </p></div></body> 656 656 </content:encoded> 657 <dcterms:modified>2008-12-1 5T00:29:28Z</dcterms:modified>657 <dcterms:modified>2008-12-16T13:43:19Z</dcterms:modified> 658 658 <guid isPermaLink="false">tag:perl-users.jp,2006:http://perl-users.jp/articles/advent-calendar/2008/11.html</guid> 659 659 </item> … … 671 671 これなら、不要な変数に頭を悩まされずに1ステートメントで書くことができ、意味も明快です。いいですね。次は id:amachang。 672 672 </description> 673 <dc:date>2008-12-1 1T03:51:21Z</dc:date>673 <dc:date>2008-12-16T13:43:19Z</dc:date> 674 674 <title>非破壊な s/// のススメ</title> 675 <pubDate>T hu, 11 Dec 2008 03:51:21-0000</pubDate>676 <content:encoded><body><h1>非破壊な s/// のススメ</h1><div class="section"><p>正規表現の置換でときどき困るのは、元の文字列が変更されてしまうことです。そのため、たとえば文字列を URI escape する際には、以下のように別変数にコピーした上で、正規表現を適用することになります。</p><pre ><code>my $escaped = $search_str;675 <pubDate>Tue, 16 Dec 2008 13:43:19 -0000</pubDate> 676 <content:encoded><body><h1>非破壊な s/// のススメ</h1><div class="section"><p>正規表現の置換でときどき困るのは、元の文字列が変更されてしまうことです。そのため、たとえば文字列を URI escape する際には、以下のように別変数にコピーした上で、正規表現を適用することになります。</p><pre class="lang-perl"><code>my $escaped = $search_str; 677 677 $escaped =~ s/([^0-9A-Za-z_.!~*&apos;()-])/&apos;%&apos; . uc(unpack(&apos;H2&apos;, $1))/eg; 678 678 my $url = &quot;http://example.com/search?q=$escaped&quot;; 679 </code></pre><p>以前、<a href="http://labs.cybozu.co.jp/blog/kazuho/archives/2007/07/perl_ndes_regex.php">この点がめんどくさいなーとブログに書いた</a>ところ、kazeburo さん他に以下のようなテクニックを教えていただきました。</p><pre ><code>use List::MoreUtils qw(apply);679 </code></pre><p>以前、<a href="http://labs.cybozu.co.jp/blog/kazuho/archives/2007/07/perl_ndes_regex.php">この点がめんどくさいなーとブログに書いた</a>ところ、kazeburo さん他に以下のようなテクニックを教えていただきました。</p><pre class="lang-perl"><code>use List::MoreUtils qw(apply); 680 680 681 681 my $url = &apos;http://example.com/search?q=&apos; … … 683 683 </code></pre><p>これなら、不要な変数に頭を悩まされずに1ステートメントで書くことができ、意味も明快です。いいですね。</p><p>次は id:amachang。</p></div></body> 684 684 </content:encoded> 685 <dcterms:modified>2008-12-1 1T03:51:21Z</dcterms:modified>685 <dcterms:modified>2008-12-16T13:43:19Z</dcterms:modified> 686 686 <guid isPermaLink="false">tag:perl-users.jp,2006:http://perl-users.jp/articles/advent-calendar/2008/12.html</guid> 687 687 </item> … … 1164 1164 1165 1165 </description> 1166 <dc:date>2008-12-1 5T00:29:28Z</dc:date>1166 <dc:date>2008-12-16T13:43:19Z</dc:date> 1167 1167 <title>Win32::GuiTest で Windows の GUI アプリをハックしよう</title> 1168 <pubDate> Mon, 15 Dec 2008 00:29:28-0000</pubDate>1169 <content:encoded><body><h1>Win32::GuiTest で Windows の GUI アプリをハックしよう</h1><p> どうも<a href="http://d.hatena.ne.jp/amachang/">あまちゃん</a>です。 突然ですが、 Win32::GuiTest というモジュールを使うと Windows の GUI アプリを楽しくハックする事ができます。 </p><p> 使う側は特にめんどうくさいことをしなくても (時には別プロセスに入り込んで)様々な情報を取得してきたり設定してきたりしてくれます。 </p><div><h2>インストール</h2><p><a href="http://strawberryperl.com/">Strawberry Perl</a> を使っているなら普通に < pre><code>C:\&gt; cpan -i Win32::GuiTest1170 </code></pre> でインストールできます。 <a href="http://www.activestate.com/Products/activeperl/index.mhtml">ActivePerl</a> を使っている場合は、<a href="http://sourceforge.net/projects/winguitest">PPM があります</a>。 </p></div><div class="step"><h2>ケーススタディ</h2><p> ソースはコピペすれば動くと思いますよっと。UTF-8 で書いてます。 </p><div id="toc"></div><div><h3>基本的な書き方</h3><pre><code>use strict;1168 <pubDate>Tue, 16 Dec 2008 13:43:19 -0000</pubDate> 1169 <content:encoded><body><h1>Win32::GuiTest で Windows の GUI アプリをハックしよう</h1><p> どうも<a href="http://d.hatena.ne.jp/amachang/">あまちゃん</a>です。 突然ですが、 Win32::GuiTest というモジュールを使うと Windows の GUI アプリを楽しくハックする事ができます。 </p><p> 使う側は特にめんどうくさいことをしなくても (時には別プロセスに入り込んで)様々な情報を取得してきたり設定してきたりしてくれます。 </p><div><h2>インストール</h2><p><a href="http://strawberryperl.com/">Strawberry Perl</a> を使っているなら普通に </p><pre><code>C:\&gt; cpan -i Win32::GuiTest 1170 </code></pre><p> でインストールできます。 <a href="http://www.activestate.com/Products/activeperl/index.mhtml">ActivePerl</a> を使っている場合は、<a href="http://sourceforge.net/projects/winguitest">PPM があります</a>。 </p></div><div class="step"><h2>ケーススタディ</h2><p> ソースはコピペすれば動くと思いますよっと。UTF-8 で書いてます。 </p><div id="toc"></div><div><h3>基本的な書き方</h3><pre class="lang-perl"><code>use strict; 1171 1171 use warnings; 1172 1172 use utf8; … … 1180 1180 1181 1181 # ここで Win32::GuiTest を使う 1182 </code></pre></div><div><h3>マウスを動かす</h3><pre ><code>use strict;1182 </code></pre></div><div><h3>マウスを動かす</h3><pre class="lang-perl"><code>use strict; 1183 1183 use warnings; 1184 1184 use utf8; … … 1199 1199 MouseMoveAbsPix(cos($i / 10) * 400 + 400, sin($i / 10) * 400 + 400); 1200 1200 } 1201 </code></pre></div><div><h3>デスクトップ領域を取得する</h3><pre ><code>use strict;1201 </code></pre></div><div><h3>デスクトップ領域を取得する</h3><pre class="lang-perl"><code>use strict; 1202 1202 use warnings; 1203 1203 use utf8; … … 1226 1226 ); 1227 1227 } 1228 </code></pre></div><div><h3>全ウィンドウの列挙</h3><pre ><code>use strict;1228 </code></pre></div><div><h3>全ウィンドウの列挙</h3><pre class="lang-perl"><code>use strict; 1229 1229 use warnings; 1230 1230 use utf8; … … 1246 1246 # ここで各ウィンドウ($child)にあんなことやこんなことをする 1247 1247 } 1248 </code></pre></div><div><h3>ウィンドウの情報を取得</h3><pre ><code>use strict;1248 </code></pre></div><div><h3>ウィンドウの情報を取得</h3><pre class="lang-perl"><code>use strict; 1249 1249 use warnings; 1250 1250 use utf8; … … 1275 1275 print $cp932-&gt;encode(&apos;--&apos; x $window_depth . $window_text . &apos;(&apos; . $class_name . &quot;)\n&quot;); 1276 1276 } 1277 </code></pre> <a href="http://f.hatena.ne.jp/amachang/20081211163602"><img alt="20081211163602" src="http://img.f.hatena.ne.jp/images/fotolife/a/amachang/20081211/20081211163602.jpg" /></a></div><div><h3>ウィンドウ名からウィンドウを取得</h3><pre ><code>use strict;1277 </code></pre> <a href="http://f.hatena.ne.jp/amachang/20081211163602"><img alt="20081211163602" src="http://img.f.hatena.ne.jp/images/fotolife/a/amachang/20081211/20081211163602.jpg" /></a></div><div><h3>ウィンドウ名からウィンドウを取得</h3><pre class="lang-perl"><code>use strict; 1278 1278 use warnings; 1279 1279 use utf8; … … 1293 1293 # 情報の表示 1294 1294 print $cp932-&gt;encode(GetWindowText($win) . &apos;(&apos; . GetClassName($win) . &quot;)\n&quot;); 1295 </code></pre> <a href="http://f.hatena.ne.jp/amachang/20081211164830"><img alt="20081211164830" src="http://img.f.hatena.ne.jp/images/fotolife/a/amachang/20081211/20081211164830.jpg" /></a></div><div><h3>Button をクリックさせる</h3><pre ><code>use strict;1295 </code></pre> <a href="http://f.hatena.ne.jp/amachang/20081211164830"><img alt="20081211164830" src="http://img.f.hatena.ne.jp/images/fotolife/a/amachang/20081211/20081211164830.jpg" /></a></div><div><h3>Button をクリックさせる</h3><pre class="lang-perl"><code>use strict; 1296 1296 use warnings; 1297 1297 use utf8; … … 1316 1316 # FindWindowLike から SendLButton*() までを一発でやってくれる 1317 1317 # MouseClick という関数もありますが、今回は使いません 1318 </code></pre> <a href="http://f.hatena.ne.jp/amachang/20081211170226"><img alt="20081211170226" src="http://img.f.hatena.ne.jp/images/fotolife/a/amachang/20081211/20081211170226.jpg" /></a></div><div><h3>メニューの取得</h3><pre ><code>use strict;1318 </code></pre> <a href="http://f.hatena.ne.jp/amachang/20081211170226"><img alt="20081211170226" src="http://img.f.hatena.ne.jp/images/fotolife/a/amachang/20081211/20081211170226.jpg" /></a></div><div><h3>メニューの取得</h3><pre class="lang-perl"><code>use strict; 1319 1319 use warnings; 1320 1320 use utf8; … … 1361 1361 1362 1362 })-&gt;(GetMenu($notepad), 0); # メモ帳のメインメニューを渡す 1363 </code></pre> <a href="http://f.hatena.ne.jp/amachang/20081211184621"><img alt="20081211184621" src="http://img.f.hatena.ne.jp/images/fotolife/a/amachang/20081211/20081211184621.jpg" /></a></div><div><h3>メニューの選択</h3><pre ><code>use strict;1363 </code></pre> <a href="http://f.hatena.ne.jp/amachang/20081211184621"><img alt="20081211184621" src="http://img.f.hatena.ne.jp/images/fotolife/a/amachang/20081211/20081211184621.jpg" /></a></div><div><h3>メニューの選択</h3><pre class="lang-perl"><code>use strict; 1364 1364 use warnings; 1365 1365 use utf8; … … 1387 1387 # しかも、フルの名前を指定しないといけないので、めんどうです。 1388 1388 # 今回は使いません>< 1389 </code></pre> <a href="http://f.hatena.ne.jp/amachang/20081211190920"><img alt="20081211190920" src="http://img.f.hatena.ne.jp/images/fotolife/a/amachang/20081211/20081211190920.jpg" /></a></div><div><h3>エディットボックスへ文字を入力する(1)</h3><pre ><code>use strict;1389 </code></pre> <a href="http://f.hatena.ne.jp/amachang/20081211190920"><img alt="20081211190920" src="http://img.f.hatena.ne.jp/images/fotolife/a/amachang/20081211/20081211190920.jpg" /></a></div><div><h3>エディットボックスへ文字を入力する(1)</h3><pre class="lang-perl"><code>use strict; 1390 1390 use warnings; 1391 1391 use utf8; … … 1408 1408 # WMSetText を使って、エディットボックスの値を直接設定 1409 1409 WMSetText($edit, $cp932-&gt;encode(&apos;ほげほげ&apos;)); 1410 </code></pre> <a href="http://f.hatena.ne.jp/amachang/20081211190921"><img alt="20081211190921" src="http://img.f.hatena.ne.jp/images/fotolife/a/amachang/20081211/20081211190921.jpg" /></a></div><div><h3>エディットボックスへ文字を入力する(2)(キーボード入力をエミュレート)</h3><pre ><code>use strict;1410 </code></pre> <a href="http://f.hatena.ne.jp/amachang/20081211190921"><img alt="20081211190921" src="http://img.f.hatena.ne.jp/images/fotolife/a/amachang/20081211/20081211190921.jpg" /></a></div><div><h3>エディットボックスへ文字を入力する(2)(キーボード入力をエミュレート)</h3><pre class="lang-perl"><code>use strict; 1411 1411 use warnings; 1412 1412 use utf8; … … 1430 1430 # キーボード入力をエミュレート 1431 1431 SendKeys(&apos;hoge{ENTER}hoge{ENTER}fuga{ENTER}piyo&apos;); 1432 </code></pre> <a href="http://f.hatena.ne.jp/amachang/20081211190922"><img alt="20081211190922" src="http://img.f.hatena.ne.jp/images/fotolife/a/amachang/20081211/20081211190922.jpg" /></a></div><div><h3>エディットボックスの文字を取得する</h3><pre ><code>use strict;1432 </code></pre> <a href="http://f.hatena.ne.jp/amachang/20081211190922"><img alt="20081211190922" src="http://img.f.hatena.ne.jp/images/fotolife/a/amachang/20081211/20081211190922.jpg" /></a></div><div><h3>エディットボックスの文字を取得する</h3><pre class="lang-perl"><code>use strict; 1433 1433 use warnings; 1434 1434 use utf8; … … 1451 1451 # (CP932 で帰ってくるので、そのまま print してるけど、プログラム中で扱う時は decode すべき) 1452 1452 print WMGetText($edit) . &quot;\n&quot;; 1453 </code></pre> <a href="http://f.hatena.ne.jp/amachang/20081211190923"><img alt="20081211190923" src="http://img.f.hatena.ne.jp/images/fotolife/a/amachang/20081211/20081211190923.jpg" /></a></div><div><h3>ツリービューを選択する</h3><pre ><code>use strict;1453 </code></pre> <a href="http://f.hatena.ne.jp/amachang/20081211190923"><img alt="20081211190923" src="http://img.f.hatena.ne.jp/images/fotolife/a/amachang/20081211/20081211190923.jpg" /></a></div><div><h3>ツリービューを選択する</h3><pre class="lang-perl"><code>use strict; 1454 1454 use warnings; 1455 1455 use utf8; … … 1482 1482 1483 1483 # この関数は、エクスプローラーでも威力を発揮します。 1484 </code></pre> <a href="http://f.hatena.ne.jp/amachang/20081212005253"><img alt="20081212005253" src="http://img.f.hatena.ne.jp/images/fotolife/a/amachang/20081212/20081212005253.jpg" /></a></div><div><h3>リストビューのアイテムをダブルクリックする</h3><pre ><code>use strict;1484 </code></pre> <a href="http://f.hatena.ne.jp/amachang/20081212005253"><img alt="20081212005253" src="http://img.f.hatena.ne.jp/images/fotolife/a/amachang/20081212/20081212005253.jpg" /></a></div><div><h3>リストビューのアイテムをダブルクリックする</h3><pre class="lang-perl"><code>use strict; 1485 1485 use warnings; 1486 1486 use utf8; … … 1559 1559 # この例のように、 1560 1560 # 自分で共有メモリ(AllocateVirtualBuffer)を使って生のメッセージでやるというパターンは結構あります。 1561 </code></pre> <a href="http://f.hatena.ne.jp/amachang/20081212020713"><img alt="20081212020713" src="http://img.f.hatena.ne.jp/images/fotolife/a/amachang/20081212/20081212020713.jpg" /></a></div><div><h3>ツールバーをクリックする</h3><pre ><code>use strict;1561 </code></pre> <a href="http://f.hatena.ne.jp/amachang/20081212020713"><img alt="20081212020713" src="http://img.f.hatena.ne.jp/images/fotolife/a/amachang/20081212/20081212020713.jpg" /></a></div><div><h3>ツールバーをクリックする</h3><pre class="lang-perl"><code>use strict; 1562 1562 use warnings; 1563 1563 use utf8; … … 1641 1641 </script></body> 1642 1642 </content:encoded> 1643 <dcterms:modified>2008-12-1 5T00:29:28Z</dcterms:modified>1643 <dcterms:modified>2008-12-16T13:43:19Z</dcterms:modified> 1644 1644 <guid isPermaLink="false">tag:perl-users.jp,2006:http://perl-users.jp/articles/advent-calendar/2008/13.html</guid> 1645 1645 </item> … … 1700 1700 実行結果:BBB:BBC:XXX:B次は国内滞在説が根強い id:miyagawa さんにお願いしたいと思います。;-) 1701 1701 </description> 1702 <dc:date>2008-12-1 5T02:23:17Z</dc:date>1702 <dc:date>2008-12-16T13:43:19Z</dc:date> 1703 1703 <title>ヒアドキュメントの中でPerlの式を書く</title> 1704 <pubDate> Mon, 15 Dec 2008 02:23:17-0000</pubDate>1705 <content:encoded><body><h1>ヒアドキュメントの中でPerlの式を書く</h1><div class="section"><p><a href="http://news.google.co.jp/news?hl=ja&amp;q=%E6%96%B0%E5%B9%B9%E7%B7%9A&amp;um=1&amp;ie=UTF-8&amp;ncl=1259980833&amp;sa=X&amp;oi=news_result&amp;resnum=1&amp;ct=more-results&amp;cd=1">初代新幹線「0系」最後のラストランの日</a>に、新幹線N700系のぞみでイーモバイルしながらこの記事を書いている<a href="http://namazu.org/~takesako/">id:TAKESAKO</a>です。</p><p>Perlのヒアドキュメントを使うと複数行にわたる文字列を一気に代入したりするときに楽なので、使っている人も多いと思います。</p><pre ><code>my $foo = &quot;bar&quot;;1704 <pubDate>Tue, 16 Dec 2008 13:43:19 -0000</pubDate> 1705 <content:encoded><body><h1>ヒアドキュメントの中でPerlの式を書く</h1><div class="section"><p><a href="http://news.google.co.jp/news?hl=ja&amp;q=%E6%96%B0%E5%B9%B9%E7%B7%9A&amp;um=1&amp;ie=UTF-8&amp;ncl=1259980833&amp;sa=X&amp;oi=news_result&amp;resnum=1&amp;ct=more-results&amp;cd=1">初代新幹線「0系」最後のラストランの日</a>に、新幹線N700系のぞみでイーモバイルしながらこの記事を書いている<a href="http://namazu.org/~takesako/">id:TAKESAKO</a>です。</p><p>Perlのヒアドキュメントを使うと複数行にわたる文字列を一気に代入したりするときに楽なので、使っている人も多いと思います。</p><pre class="lang-perl"><code>my $foo = &quot;bar&quot;; 1706 1706 my $tmp = time(); # ←関数の実行結果 1707 1707 print&lt;&lt;EOF; … … 1710 1710 &lt;/div&gt; 1711 1711 EOF 1712 </code></pre><p>しかし、ヒアドキュメントの途中でサブルーチンの実行結果も一緒に埋め込みたいときがでてくるときがあります。</p><h2>@{[ Perlの式 ]}</h2><p>そのようなときは、<code>@{[ リスト ]}</code> というイディオムを使うと非常に便利です。</p><pre ><code>print&lt;&lt;EOF;1712 </code></pre><p>しかし、ヒアドキュメントの途中でサブルーチンの実行結果も一緒に埋め込みたいときがでてくるときがあります。</p><h2>@{[ Perlの式 ]}</h2><p>そのようなときは、<code>@{[ リスト ]}</code> というイディオムを使うと非常に便利です。</p><pre class="lang-perl"><code>print&lt;&lt;EOF; 1713 1713 &lt;div class=&quot;${foo}1&quot;&gt; 1714 1714 &lt;h1&gt;TIME: @{[ time() ]}&lt;/h1&gt; 1715 1715 &lt;/div&gt; 1716 1716 EOF 1717 </code></pre><p>実行結果:</p><pre ><samp> &lt;div class=&quot;bar1&quot;&gt;1717 </code></pre><p>実行結果:</p><pre class="lang-html"><samp> &lt;div class=&quot;bar1&quot;&gt; 1718 1718 &lt;h1&gt;TIME: 1229258525&lt;/h1&gt; 1719 1719 &lt;/div&gt; 1720 </samp></pre><p>@{[ リスト ]} の中にはPerlの式をそのまま書くことができます。</p><p>この例では、関数 time() の実行結果がヒアドキュメントの文字列の中に埋め込まれていることがわかります。</p><h3>リストコンテキストの罠</h3><p>しかし、このイディオムの式はリストコンテキストで評価されるので、wantarray でサブルーチンの戻り値を切り替えている関数などでは期待通りの結果が得られない場合があります。</p><pre ><code>print&lt;&lt;EOF;1720 </samp></pre><p>@{[ リスト ]} の中にはPerlの式をそのまま書くことができます。</p><p>この例では、関数 time() の実行結果がヒアドキュメントの文字列の中に埋め込まれていることがわかります。</p><h3>リストコンテキストの罠</h3><p>しかし、このイディオムの式はリストコンテキストで評価されるので、wantarray でサブルーチンの戻り値を切り替えている関数などでは期待通りの結果が得られない場合があります。</p><pre class="lang-perl"><code>print&lt;&lt;EOF; 1721 1721 &lt;div class=&quot;${foo}2&quot;&gt; 1722 1722 &lt;h1&gt;TIME: @{[ localtime() ]}&lt;/h1&gt; 1723 1723 &lt;/div&gt; 1724 1724 EOF 1725 </code></pre><p>実行結果:</p><pre ><samp> &lt;div class=&quot;bar2&quot;&gt;1725 </code></pre><p>実行結果:</p><pre class="lang-html"><samp> &lt;div class=&quot;bar2&quot;&gt; 1726 1726 &lt;h1&gt;TIME: 5 42 21 14 11 108 0 348 0&lt;/h1&gt; 1727 1727 &lt;/div&gt; 1728 </samp></pre><p>このような場合は、式が必ずスカラーコンテキストで評価されるように、 <code>&quot;&quot;.localtime()</code> もしくは <code>scalar localtime()</code> などと記述すれば大丈夫です。</p><pre ><code>print&lt;&lt;EOF;1728 </samp></pre><p>このような場合は、式が必ずスカラーコンテキストで評価されるように、 <code>&quot;&quot;.localtime()</code> もしくは <code>scalar localtime()</code> などと記述すれば大丈夫です。</p><pre class="lang-perl"><code>print&lt;&lt;EOF; 1729 1729 &lt;div class=&quot;${foo}3&quot;&gt; 1730 1730 &lt;h1&gt;TIME: @{[ scalar localtime() ]}&lt;/h1&gt; 1731 1731 &lt;/div&gt; 1732 1732 EOF 1733 </code></pre><p>実行結果:</p><pre ><samp> &lt;div class=&quot;bar3&quot;&gt;1733 </code></pre><p>実行結果:</p><pre class="lang-html"><samp> &lt;div class=&quot;bar3&quot;&gt; 1734 1734 &lt;h1&gt;TIME: Sun Dec 14 21:42:05 2008&lt;/h1&gt; 1735 1735 &lt;/div&gt; 1736 </samp></pre><h3>&quot;@a\n&quot; と特殊変数 $&quot;</h3><p>ちなみに、ダブルクォート文字列中に <code>@a</code> を埋め込んだ場合、例えば <code>&quot;@a\n&quot;</code> は <code>join($&quot;,@a).&quot;\n&quot;</code> と等価となります。</p><p><code>$&quot;</code> は、配列のリストを文字列に変換するときに自動的に要素間に挿入する文字列(デフォルトは空白 <code>&quot; &quot;</code>)を意味する特殊変数です。<br /> 一時的に <code>$&quot;</code> の値を書き換えて、要素間に自動挿入する文字列をデフォルトの空白から任意の文字列に変更することもできます。</p><pre ><code>do {1736 </samp></pre><h3>&quot;@a\n&quot; と特殊変数 $&quot;</h3><p>ちなみに、ダブルクォート文字列中に <code>@a</code> を埋め込んだ場合、例えば <code>&quot;@a\n&quot;</code> は <code>join($&quot;,@a).&quot;\n&quot;</code> と等価となります。</p><p><code>$&quot;</code> は、配列のリストを文字列に変換するときに自動的に要素間に挿入する文字列(デフォルトは空白 <code>&quot; &quot;</code>)を意味する特殊変数です。<br /> 一時的に <code>$&quot;</code> の値を書き換えて、要素間に自動挿入する文字列をデフォルトの空白から任意の文字列に変更することもできます。</p><pre class="lang-perl"><code>do { 1737 1737 local $&quot; = &quot; x &quot;; 1738 1738 my @a = (&apos;sin&apos;, &apos;cos&apos;, &apos;tan&apos;); 1739 1739 print &quot;@a\n&quot;; 1740 1740 }; 1741 </code></pre><p>実行結果:</p><pre ><samp>sin x cos x tan</samp></pre><p>ここで、<code>&quot;@a\n&quot;</code> の部分を <code>&quot;@{[ リスト ]}\n&quot;</code> に置き換えて、</p><pre><code>print &quot;@{[ &apos;sin&apos;, &apos;cos&apos;, &apos;tan&apos; ]}\n&quot;;1742 </code></pre><p>と、無名配列のデリファレンス <code>@{[ ]}</code> を使うことによって、配列 <code>@a</code> を使わずに、文字列中に直接リストの値を埋め込むことが可能になります。</p><p>このようにヒアドキュメントの他にも、ダブルクォーテーションで囲まれた文字列の中でも <code>@{[ リスト ]}</code> のイディオムを使ってPerlの式を展開することができます。</p><h2>閑話休題</h2><p><a href="http://perl-users.jp/articles/advent-calendar/2008/12.html">数日前の復習</a>ですが、Perlは破壊的な正規表現の置換を行なうので、以下のように複数行にわけて書くのが面倒という話がありました。</p><pre ><code>my $a = &quot;AAA&quot;;1741 </code></pre><p>実行結果:</p><pre class="lang-html"><samp>sin x cos x tan</samp></pre><p>ここで、<code>&quot;@a\n&quot;</code> の部分を <code>&quot;@{[ リスト ]}\n&quot;</code> に置き換えて、</p><pre class="lang-perl"><code>print &quot;@{[ &apos;sin&apos;, &apos;cos&apos;, &apos;tan&apos; ]}\n&quot;; 1742 </code></pre><p>と、無名配列のデリファレンス <code>@{[ ]}</code> を使うことによって、配列 <code>@a</code> を使わずに、文字列中に直接リストの値を埋め込むことが可能になります。</p><p>このようにヒアドキュメントの他にも、ダブルクォーテーションで囲まれた文字列の中でも <code>@{[ リスト ]}</code> のイディオムを使ってPerlの式を展開することができます。</p><h2>閑話休題</h2><p><a href="http://perl-users.jp/articles/advent-calendar/2008/12.html">数日前の復習</a>ですが、Perlは破壊的な正規表現の置換を行なうので、以下のように複数行にわけて書くのが面倒という話がありました。</p><pre class="lang-perl"><code>my $a = &quot;AAA&quot;; 1743 1743 my $b = $a; 1744 1744 $b =~ s/A/B/g; 1745 1745 print &quot;&apos;$a&apos; =&gt; &apos;$b&apos;,\n&quot;; 1746 </code></pre><p>実行結果:</p><pre ><samp>&apos;AAA&apos; =&gt; &apos;BBB&apos;,1747 </samp></pre><p>これは、<code>$b = $a</code> の代入文を <code>( )</code> で括って、置換演算子 <code>=~</code> の左辺に持ってくることによって1行短縮することができます。</p><pre ><code>my $a = &quot;AAA&quot;;1746 </code></pre><p>実行結果:</p><pre class="lang-html"><samp>&apos;AAA&apos; =&gt; &apos;BBB&apos;, 1747 </samp></pre><p>これは、<code>$b = $a</code> の代入文を <code>( )</code> で括って、置換演算子 <code>=~</code> の左辺に持ってくることによって1行短縮することができます。</p><pre class="lang-perl"><code>my $a = &quot;AAA&quot;; 1748 1748 ( my $b = $a ) =~ s/A/B/g; 1749 </code></pre><p>代入演算子 <code>=</code> は左結合なので、さらに変数 <code>$a</code> の初期化も1行にまとめることができます。</p><pre ><code>( my $b = my $a = &quot;AAA&quot; ) =~ s/A/B/g;1750 </code></pre><p>これは、<code>my</code>の存在しなかったPerl4の時代で基本的なテクニックだったそうです。</p><h2>@{[ リスト ]} の応用例</h2><p>ここで<code>map</code>を使えば一時変数の名前もつけなくてよくなるはず…と思って以下のコードを書いて実行してみます。</p><pre ><code>my ($b) = map { s/A/B/g; $_ } (&quot;AAA&quot;);1751 </code></pre><p>実行結果:</p><pre ><samp>Modification of a read-only value attempted at - line 1.</samp></pre><p>しかし、このコードは Perl に怒られてエラーになってしまいます。</p><p><code>(&quot;AAA&quot;)</code> としただけでは <code>&quot;AAA&quot;</code> は文字列定数(read-only value)と解釈されるので、値の変更ができないのです。</p><h3>map の初期値に @{[]}</h3><p>そこで登場するのが <code>@{[ リスト ]}</code> の応用例です。</p><pre><code>my ($b) = map { s/A/B/g; $_ } @{[&quot;AAA&quot;]};1752 </code></pre><p><code>[&quot;AAA&quot;]</code>で無名配列を作ってすぐに<code>@{}</code>でデリファレンスしてあげれば、 一時的な配列を作成することができます。<br />この配列の値は破壊的な変更が可能です。</p><h3>grep の第一引数に s///</h3><p>ちなみに、<code>map</code> の他に <code>grep</code> を使っても同じようなコードを書くことができます。</p><pre ><code>my ($b) = grep s/A/B/g || 1, @{[&quot;AAA&quot;]};1753 </code></pre><p>このとき <code>|| 1</code> の部分を省略してしまうと、 <code>s///</code> の戻り値がそのまま評価されてしまうので、置換に失敗した要素が取り除かれてしまいます。</p><pre ><code>print join &quot;:&quot;, grep s/A/B/g, @{[&quot;AAA&quot;, &quot;ABC&quot;, &quot;XXX&quot;, &quot;A&quot;]};1754 </code></pre><p>実行結果:</p><pre ><samp>BBB:BBC:B</samp></pre><p><code>s/A/B/</code> の置換に失敗した <code>&quot;XXX&quot;</code> の値も含めたい場合は、<code>s</code> の後ろに <code>|| 1</code> をつけて、パターンマッチの結果に関わらず常に真になるようにします。</p><pre><code>print join &quot;:&quot;, grep s/A/B/g || 1, @{[&quot;AAA&quot;, &quot;ABC&quot;, &quot;XXX&quot;, &quot;A&quot;]};1755 </code></pre><p>実行結果:</p><pre ><samp>BBB:BBC:XXX:B</samp></pre><p>次は国内滞在説が根強い id:miyagawa さんにお願いしたいと思います。;-)</p></div></body>1749 </code></pre><p>代入演算子 <code>=</code> は左結合なので、さらに変数 <code>$a</code> の初期化も1行にまとめることができます。</p><pre class="lang-perl"><code>( my $b = my $a = &quot;AAA&quot; ) =~ s/A/B/g; 1750 </code></pre><p>これは、<code>my</code>の存在しなかったPerl4の時代で基本的なテクニックだったそうです。</p><h2>@{[ リスト ]} の応用例</h2><p>ここで<code>map</code>を使えば一時変数の名前もつけなくてよくなるはず…と思って以下のコードを書いて実行してみます。</p><pre class="lang-perl"><code>my ($b) = map { s/A/B/g; $_ } (&quot;AAA&quot;); 1751 </code></pre><p>実行結果:</p><pre class="lang-html"><samp>Modification of a read-only value attempted at - line 1.</samp></pre><p>しかし、このコードは Perl に怒られてエラーになってしまいます。</p><p><code>(&quot;AAA&quot;)</code> としただけでは <code>&quot;AAA&quot;</code> は文字列定数(read-only value)と解釈されるので、値の変更ができないのです。</p><h3>map の初期値に @{[]}</h3><p>そこで登場するのが <code>@{[ リスト ]}</code> の応用例です。</p><pre class="lang-perl"><code>my ($b) = map { s/A/B/g; $_ } @{[&quot;AAA&quot;]}; 1752 </code></pre><p><code>[&quot;AAA&quot;]</code>で無名配列を作ってすぐに<code>@{}</code>でデリファレンスしてあげれば、 一時的な配列を作成することができます。<br />この配列の値は破壊的な変更が可能です。</p><h3>grep の第一引数に s///</h3><p>ちなみに、<code>map</code> の他に <code>grep</code> を使っても同じようなコードを書くことができます。</p><pre class="lang-perl"><code>my ($b) = grep s/A/B/g || 1, @{[&quot;AAA&quot;]}; 1753 </code></pre><p>このとき <code>|| 1</code> の部分を省略してしまうと、 <code>s///</code> の戻り値がそのまま評価されてしまうので、置換に失敗した要素が取り除かれてしまいます。</p><pre class="lang-perl"><code>print join &quot;:&quot;, grep s/A/B/g, @{[&quot;AAA&quot;, &quot;ABC&quot;, &quot;XXX&quot;, &quot;A&quot;]}; 1754 </code></pre><p>実行結果:</p><pre class="lang-html"><samp>BBB:BBC:B</samp></pre><p><code>s/A/B/</code> の置換に失敗した <code>&quot;XXX&quot;</code> の値も含めたい場合は、<code>s</code> の後ろに <code>|| 1</code> をつけて、パターンマッチの結果に関わらず常に真になるようにします。</p><pre class="lang-perl"><code>print join &quot;:&quot;, grep s/A/B/g || 1, @{[&quot;AAA&quot;, &quot;ABC&quot;, &quot;XXX&quot;, &quot;A&quot;]}; 1755 </code></pre><p>実行結果:</p><pre class="lang-html"><samp>BBB:BBC:XXX:B</samp></pre><p>次は国内滞在説が根強い id:miyagawa さんにお願いしたいと思います。;-)</p></div></body> 1756 1756 </content:encoded> 1757 <dcterms:modified>2008-12-1 5T02:23:17Z</dcterms:modified>1757 <dcterms:modified>2008-12-16T13:43:19Z</dcterms:modified> 1758 1758 <guid isPermaLink="false">tag:perl-users.jp,2006:http://perl-users.jp/articles/advent-calendar/2008/14.html</guid> 1759 1759 </item> … … 1762 1762 <dc:creator>nobody@example.com</dc:creator> 1763 1763 <link>http://perl-users.jp/articles/advent-calendar/2008/15.html</link> 1764 <description>perldoc を使いこなす国内滞在説が根強い miyagawa です。アメリカ合衆国国内という意味であれば、いつもそうなんですが。今回は意外と知られていないと思われる perldoc コマンドの使い方を紹介します。perldoc は Perl モジュールに含まれる POD ドキュメントを整形して man 表示するツールで、perldoc モジュール名のようにして実行します。% perldoc Web::Scraper-l オプションをつけるとそのモジュールのあるパス、-m オプションで POD の代わりにソースコードそのものを表示することができます。% perldoc -l Web::Scraper 1764 <description>perldoc を使いこなす国内滞在説が根強い miyagawa です。アメリカ合衆国国内という意味であれば、いつもそうなんですが。今回は意外と知られていないと思われる perldoc コマンドの使い方を紹介します。perldoc は Perl モジュールに含まれる POD ドキュメントを整形して man 表示するツールで、perldoc モジュール名のようにして実行します。% perldoc Web::Scraper 1765 -l オプションをつけるとそのモジュールのあるパス、-m オプションで POD の代わりにソースコードそのものを表示することができます。% perldoc -l Web::Scraper 1765 1766 /Library/Perl/5.8.6/Web/Scraper.pm 1766 1767 # .pm ファイルを vi で開く 1767 1768 % vi `perldoc -l Web::Scraper` 1768 1769 # .pm ファイルを PAGER で開く 1769 % perldoc -m Web::Scraper 1770 エラーにファイル名と行数が書いてあってその部分を見たい、なんてときには emacsやviなどにパイプで渡して開くと便利ですね。-f オプションは perl の組み込み関数のドキュメントを表示します。% perldoc -f index1770 % perldoc -m Web::Scraper 1771 エラーにファイル名と行数が書いてあってその部分を見たい、なんてときには Emacs や Vim などにパイプで渡して開くと便利ですね。-f オプションは Perl の組み込み関数のドキュメントを表示します。% perldoc -f index 1771 1772 index STR,SUBSTR,POSITION 1772 1773 index STR,SUBSTR … … 1783 1784 次は sekimura さん。 1784 1785 </description> 1785 <dc:date>2008-12-1 5T23:45:43Z</dc:date>1786 <dc:date>2008-12-16T11:03:38Z</dc:date> 1786 1787 <title>perldoc を使いこなす</title> 1787 <pubDate>Mon, 15 Dec 2008 23:45:43 -0000</pubDate> 1788 <content:encoded><body><h1>perldoc を使いこなす</h1><div class="section"><p>国内滞在説が根強い miyagawa です。アメリカ合衆国国内という意味であれば、いつもそうなんですが。</p><p>今回は意外と知られていないと思われる perldoc コマンドの使い方を紹介します。perldoc は Perl モジュールに含まれる POD ドキュメントを整形して man 表示するツールで、perldoc モジュール名のようにして実行します。</p><pre><code>% perldoc Web::Scraper</code></pre><p>-l オプションをつけるとそのモジュールのあるパス、-m オプションで POD の代わりにソースコードそのものを表示することができます。</p><pre><code>% perldoc -l Web::Scraper 1788 <pubDate>Tue, 16 Dec 2008 11:03:38 -0000</pubDate> 1789 <content:encoded><body><h1>perldoc を使いこなす</h1><div class="section"><p>国内滞在説が根強い miyagawa です。アメリカ合衆国国内という意味であれば、いつもそうなんですが。</p><p>今回は意外と知られていないと思われる perldoc コマンドの使い方を紹介します。perldoc は Perl モジュールに含まれる POD ドキュメントを整形して man 表示するツールで、perldoc モジュール名のようにして実行します。</p><pre><code>% perldoc Web::Scraper 1790 </code></pre><p><code>-l</code> オプションをつけるとそのモジュールのあるパス、<code>-m</code> オプションで POD の代わりにソースコードそのものを表示することができます。</p><pre><code>% perldoc -l Web::Scraper 1789 1791 /Library/Perl/5.8.6/Web/Scraper.pm 1790 1792 # .pm ファイルを vi で開く 1791 1793 % vi `perldoc -l Web::Scraper` 1792 1794 # .pm ファイルを PAGER で開く 1793 % perldoc -m Web::Scraper 1794 </code></pre><p>エラーにファイル名と行数が書いてあってその部分を見たい、なんてときには emacsやviなどにパイプで渡して開くと便利ですね。</p><p>-f オプションは perl の組み込み関数のドキュメントを表示します。</p><pre><code>% perldoc -f index1795 % perldoc -m Web::Scraper 1796 </code></pre><p>エラーにファイル名と行数が書いてあってその部分を見たい、なんてときには Emacs や Vim などにパイプで渡して開くと便利ですね。</p><p><code>-f</code> オプションは Perl の組み込み関数のドキュメントを表示します。</p><pre><code>% perldoc -f index 1795 1797 index STR,SUBSTR,POSITION 1796 1798 index STR,SUBSTR … … 1803 1805 to--but don&apos;t do that). If the substring is not found, returns 1804 1806 one less than the base, ordinarily &quot;-1&quot;. 1805 </code></pre><p>よく <a href="http://perl-users.jp/articles/advent-calendar/2008/06.html"> -X ファイルテスト演算子</a> の対応を忘れてしまって困りますが、そういうときは perldoc -f -X とすると一覧がでてきます。</p><p>そうそう、ターミナルのロケールに UTF-8 を設定している場合、<a href="http://use.perl.org/~jbisbee/journal/36868">perldoc と nroff では、コードに含まれるシングルクォートやダブルクォートが UTF-8 の全角文字に変換されてしまい、ペーストしても動かない</a> という問題があります。ちょっとバッドノウハウ気味ですが、perldoc に -t オプションをつけていつもテキスト表示するか、一時的に LANG環境変数を無効化することで対応しています。</p><pre><code>% alias perldoc1807 </code></pre><p>よく <a href="http://perl-users.jp/articles/advent-calendar/2008/06.html"><code>-X</code> ファイルテスト演算子</a> の対応を忘れてしまって困りますが、そういうときは <code>perldoc -f -X</code> とすると一覧がでてきます。</p><p>そうそう、ターミナルのロケールに UTF-8 を設定している場合、<a href="http://use.perl.org/~jbisbee/journal/36868">perldoc と nroff では、コードに含まれるシングルクォートやダブルクォートが UTF-8 の全角文字に変換されてしまい、ペーストしても動かない</a> という問題があります。ちょっとバッドノウハウ気味ですが、perldoc に <code>-t</code> オプションをつけていつもテキスト表示するか、一時的に <var>LANG</var> 環境変数を無効化することで対応しています。</p><pre><code>% alias perldoc 1806 1808 env LANG=C perldoc 1807 1809 </code></pre><p>次は sekimura さん。</p></div></body> 1808 1810 </content:encoded> 1809 <dcterms:modified>2008-12-1 5T23:45:43Z</dcterms:modified>1811 <dcterms:modified>2008-12-16T11:03:38Z</dcterms:modified> 1810 1812 <guid isPermaLink="false">tag:perl-users.jp,2006:http://perl-users.jp/articles/advent-calendar/2008/15.html</guid> 1813 </item> 1814 <item> 1815 <author>nobody@example.com</author> 1816 <dc:creator>nobody@example.com</dc:creator> 1817 <link>http://perl-users.jp/articles/advent-calendar/2008/16.html</link> 1818 <description>Perl の map と grep を使うすしを奢らなければいけないなんて、バトンを渡されてから知りました。おいしい寿司が食いたい sekimura です。今回は使いこなすと気持ちよくて、使いすぎると気持ち悪いと言われてしまう grep と map の使い方について紹介します。この二つは文法がよく似ていて、同時に使われることも多いので一気に両方の使い方を覚えるのをおすすめします。grep: 配列をフィルターするまずは、前回覚えた perldoc を使って grep とはなにかを調べてみましょう。$ perldoc -f grep 1819 grep BLOCK LIST 1820 grep EXPR,LIST 1821 This is similar in spirit to, but not the same as, grep(1) and 1822 its relatives. In particular, it is not limited to using 1823 regular expressions. 1824 1825 Evaluates the BLOCK or EXPR for each element of LIST (locally 1826 setting $_ to each element) and returns the list value 1827 consisting of those elements for which the expression evaluated 1828 to true. In scalar context, returns the number of times the 1829 expression was true. 1830 「UNIX コマンドの grep 等のコマンドと似てるけど違うもの。だって正規表現以外も使えるんだぜ」とか書いていますね。LIST の全要素を($_ を局所的にセットしながら)BLOCK か EXPR で評価し、その結果が真となるものだけからなる配列を返します。スカラコンテキストの場合は、結果が真となる要素の数を返します。例えば %ENV のキーからなる配列から "H" で始まるものだけを抜き出すには以下のようにします。$ perl -e 'print join " ", (grep /^H/, keys %ENV), "\n"' 1831 HOME HISTCONTROL 1832 grep は BLOCK を使った場合にもっと楽しくなります。例えば、以下のように、ある二つの配列をつなぎ合わせたものから、重複を取り除いた配列を得ることができます。my @cities = ('Sapporo', 'Nishitokyo', 'Yokohama'); 1833 my @prefs = ('Hokkaido', 'Tokyo', 'Yokohama'); 1834 my %seen; 1835 1836 my @uniq = grep { ++$seen{$_} < 2 } (@cities, @prefs); 1837 1838 ## @uniq には ('Sapporo', 'Nishitokyo', 'Yokohama', 'Hokkaido', 'Tokyo') が入る。 1839 逆に重複したものだけ抜き出したいときには以下のように grep で取得した配列に対して grep することで得られます。my @lunch = ('Bento', 'Ramen', 'Onigiri', 'Curry'); 1840 my @dinner = ('Tonkatsu', 'Ramen', 'Curry'); 1841 my %seen; 1842 1843 my @dup = grep { $seen{$_} >= 2 } grep { ++$seen{$_} > 1 } (@luch, @dinner); 1844 1845 ## @dup には ('Ramen', 'Curry') が入る。 1846 map: 配列の要素を変換する例によって perldoc -f map しましょう。$ perldoc -f map 1847 map BLOCK LIST 1848 map EXPR,LIST 1849 Evaluates the BLOCK or EXPR for each element of LIST (locally 1850 setting $_ to each element) and returns the list value composed 1851 of the results of each such evaluation. In scalar context, 1852 returns the total number of elements so generated. Evaluates 1853 BLOCK or EXPR in list context, so each element of LIST may 1854 produce zero, one, or more elements in the returned value. 1855 ほとんど同じことが書いてありますね。grep はフィルターなので、得られる配列は与えられた配列のサブセットになるのに対して、map では与えられた各要素を変換し、その結果を配列として得ることが可能です。my %price_map = ( 1856 'Ramen' => 400, 1857 'Curry' => 650, 1858 'Katsudon' => 600, 1859 ); 1860 my @today = ('Ramen', 'Curry'); 1861 my @meshi_dai = map { $price_map{$_} } @today; 1862 ## @meshi_dai には ('400', '650') が入る。 1863 1864 my @zei_komi = map { $_ x 1.05 } @meshi_dai; 1865 ## @zei_komi には ('420', '682.5') が入る。 1866 grep のときにはスルーしましたが、 BLOCK 内での $_ は元の要素のリファレンスなので、$_ を変更してしまうと、元の要素も変更されてしまいます。よく、「破壊的」と呼ばれるケースですね。これを防ぐには、 BLOCK の内部で my 変数にコピーしてから変更を加えていきます。my @addresses = ('katsuo@example.com', 'wakame@example.com', 'tara@example.com'); 1867 my @no_spam = map { my $email = $_; $email =~ s/\@/ at /; $email } @addresses; 1868 1869 ## @no_spam には ('katsuo at example.com', 'wakame at example.com', 'tara at example.com) が入る。 1870 このようにして、@addresses の要素を変更すること無く @no_spam という変換後の要素を持つ配列を得ることができます。使いどころgrep, map 両方共 for, foreach のループで書き換えることができますが、それぞれ「フィルター」と「変換」という意味をコードを読む人に的確に伝えることができるのがメリットではないでしょうか。その他にも、デバッガーやワンライナーでループ処理を簡素に書けるのも利点です。 sort 等のコマンドと組み合わせて UNIX のパイプのようにデータを処理すると自分が偉くなったような錯覚に陥るのがオススメどころです。 ただし、 grep, map を単にループ処理をするために、左辺値を受け取らずに使うのはコードを読む人を混乱させるので避けた方がいいでしょう。 (SEE ALSO perldoc perlstyle) 次は nipotan さん と思ったら風邪引いてピンチだそうで。 antipop さんお願いします。 1871 </description> 1872 <dc:date>2008-12-17T13:37:36Z</dc:date> 1873 <title>Perl の map と grep を使う</title> 1874 <pubDate>Wed, 17 Dec 2008 13:37:36 -0000</pubDate> 1875 <content:encoded><body><h1>Perl の map と grep を使う</h1><div class="section"><p>すしを奢らなければいけないなんて、バトンを渡されてから知りました。おいしい寿司が食いたい sekimura です。</p><p>今回は使いこなすと気持ちよくて、使いすぎると気持ち悪いと言われてしまう <code>grep</code> と <code>map</code> の使い方について紹介します。この二つは文法がよく似ていて、同時に使われることも多いので一気に両方の使い方を覚えるのをおすすめします。</p><h2>grep: 配列をフィルターする</h2><p>まずは、前回覚えた perldoc を使って <code>grep</code> とはなにかを調べてみましょう。</p><pre><code>$ perldoc -f grep 1876 grep BLOCK LIST 1877 grep EXPR,LIST 1878 This is similar in spirit to, but not the same as, grep(1) and 1879 its relatives. In particular, it is not limited to using 1880 regular expressions. 1881 1882 Evaluates the BLOCK or EXPR for each element of LIST (locally 1883 setting $_ to each element) and returns the list value 1884 consisting of those elements for which the expression evaluated 1885 to true. In scalar context, returns the number of times the 1886 expression was true. 1887 </code></pre><p>「UNIX コマンドの <code>grep</code> 等のコマンドと似てるけど違うもの。だって正規表現以外も使えるんだぜ」とか書いていますね。LIST の全要素を(<code>$_</code> を局所的にセットしながら)BLOCK か EXPR で評価し、その結果が真となるものだけからなる配列を返します。スカラコンテキストの場合は、結果が真となる要素の数を返します。例えば <code>%ENV</code> のキーからなる配列から <code>&quot;H&quot;</code> で始まるものだけを抜き出すには以下のようにします。</p><pre><code>$ perl -e &apos;print join &quot; &quot;, (grep /^H/, keys %ENV), &quot;\n&quot;&apos; 1888 HOME HISTCONTROL 1889 </code></pre><p><code>grep</code> は BLOCK を使った場合にもっと楽しくなります。例えば、以下のように、ある二つの配列をつなぎ合わせたものから、重複を取り除いた配列を得ることができます。</p><pre class="lang-perl"><code>my @cities = (&apos;Sapporo&apos;, &apos;Nishitokyo&apos;, &apos;Yokohama&apos;); 1890 my @prefs = (&apos;Hokkaido&apos;, &apos;Tokyo&apos;, &apos;Yokohama&apos;); 1891 my %seen; 1892 1893 my @uniq = grep { ++$seen{$_} &lt; 2 } (@cities, @prefs); 1894 1895 ## @uniq には (&apos;Sapporo&apos;, &apos;Nishitokyo&apos;, &apos;Yokohama&apos;, &apos;Hokkaido&apos;, &apos;Tokyo&apos;) が入る。 1896 </code></pre><p>逆に重複したものだけ抜き出したいときには以下のように <code>grep</code> で取得した配列に対して <code>grep</code> することで得られます。</p><pre class="lang-perl"><code>my @lunch = (&apos;Bento&apos;, &apos;Ramen&apos;, &apos;Onigiri&apos;, &apos;Curry&apos;); 1897 my @dinner = (&apos;Tonkatsu&apos;, &apos;Ramen&apos;, &apos;Curry&apos;); 1898 my %seen; 1899 1900 my @dup = grep { $seen{$_} &gt;= 2 } grep { ++$seen{$_} &gt; 1 } (@luch, @dinner); 1901 1902 ## @dup には (&apos;Ramen&apos;, &apos;Curry&apos;) が入る。 1903 </code></pre><h2>map: 配列の要素を変換する</h2><p>例によって <code>perldoc -f map</code> しましょう。</p><pre><code>$ perldoc -f map 1904 map BLOCK LIST 1905 map EXPR,LIST 1906 Evaluates the BLOCK or EXPR for each element of LIST (locally 1907 setting $_ to each element) and returns the list value composed 1908 of the results of each such evaluation. In scalar context, 1909 returns the total number of elements so generated. Evaluates 1910 BLOCK or EXPR in list context, so each element of LIST may 1911 produce zero, one, or more elements in the returned value. 1912 </code></pre><p>ほとんど同じことが書いてありますね。<code>grep</code> はフィルターなので、得られる配列は与えられた配列のサブセットになるのに対して、<code>map</code> では与えられた各要素を変換し、その結果を配列として得ることが可能です。</p><pre class="lang-perl"><code>my %price_map = ( 1913 &apos;Ramen&apos; =&gt; 400, 1914 &apos;Curry&apos; =&gt; 650, 1915 &apos;Katsudon&apos; =&gt; 600, 1916 ); 1917 my @today = (&apos;Ramen&apos;, &apos;Curry&apos;); 1918 my @meshi_dai = map { $price_map{$_} } @today; 1919 ## @meshi_dai には (&apos;400&apos;, &apos;650&apos;) が入る。 1920 1921 my @zei_komi = map { $_ x 1.05 } @meshi_dai; 1922 ## @zei_komi には (&apos;420&apos;, &apos;682.5&apos;) が入る。 1923 </code></pre><p><code>grep</code> のときにはスルーしましたが、 BLOCK 内での <code>$_</code> は元の要素のリファレンスなので、<code>$_</code> を変更してしまうと、元の要素も変更されてしまいます。よく、「破壊的」と呼ばれるケースですね。これを防ぐには、 BLOCK の内部で <code>my</code> 変数にコピーしてから変更を加えていきます。</p><pre class="lang-perl"><code>my @addresses = (&apos;katsuo@example.com&apos;, &apos;wakame@example.com&apos;, &apos;tara@example.com&apos;); 1924 my @no_spam = map { my $email = $_; $email =~ s/\@/ at /; $email } @addresses; 1925 1926 ## @no_spam には (&apos;katsuo at example.com&apos;, &apos;wakame at example.com&apos;, &apos;tara at example.com) が入る。 1927 </code></pre><p>このようにして、<code>@addresses</code> の要素を変更すること無く <code>@no_spam</code> という変換後の要素を持つ配列を得ることができます。</p><h2>使いどころ</h2><p><code>grep</code>, <code>map</code> 両方共 <code>for</code>, <code>foreach</code> のループで書き換えることができますが、それぞれ「フィルター」と「変換」という意味をコードを読む人に的確に伝えることができるのがメリットではないでしょうか。その他にも、デバッガーやワンライナーでループ処理を簡素に書けるのも利点です。 <code>sort</code> 等のコマンドと組み合わせて UNIX のパイプのようにデータを処理すると自分が偉くなったような錯覚に陥るのがオススメどころです。 </p><p>ただし、 <code>grep</code>, <code>map</code> を単にループ処理をするために、左辺値を受け取らずに使うのはコードを読む人を混乱させるので避けた方がいいでしょう。 (SEE ALSO <code>perldoc perlstyle</code>) </p><p>次は <del>nipotan さん</del> と思ったら風邪引いてピンチだそうで。 antipop さんお願いします。</p></div></body> 1928 </content:encoded> 1929 <dcterms:modified>2008-12-17T13:37:36Z</dcterms:modified> 1930 <guid isPermaLink="false">tag:perl-users.jp,2006:http://perl-users.jp/articles/advent-calendar/2008/16.html</guid> 1931 </item> 1932 <item> 1933 <author>nobody@example.com</author> 1934 <dc:creator>nobody@example.com</dc:creator> 1935 <link>http://perl-users.jp/articles/advent-calendar/2008/17.html</link> 1936 <description>酔っぱらった勢いでコードを書く際に大切なことプライベートでコードを書く時は、たいていビールを飲みながらってな毎日のkentaro a.k.a. id:antipopです。こんにちは。そんなわけで、ただでさえtypoが多いのに加えて、酔いにより手元はボロボロ。調子良く書いていても、つまらないtypoをいちいち修正してまわっていると、せっかくの気分のいい酔いも覚めてしまうというものです。そこで、コードを書く時には酒を飲まない、という以外の対策を考えてみました。とにかく全部ぶっつぶすひとはどうしたところで間違いを犯すものです。酔っぱらっていても酔っぱらってなくても、それは同じ。ならば、あり得る名前をあらかじめ定義してはどうか、と考えました。たとえば以下のようなクラスを定義したとします。package Hoge::Fuga::Piyo; 1937 use strict; 1938 use warnings; 1939 1940 # ... (snip) ... 1941 1942 1; 1943 このクラスを使う際に、上記したように手元不如意の状態に陥っていると、ついつい以下のように書いたりして、怒られたりします。my $obj = Hgeo::Ufag::Poyi->new; 1944 こんなことではいつtypoをしてしまうか心配で、あっという間に酔いも覚めてしまうでしょう。この大変に深刻な問題避けるにはどうしたらいいか。そうです。Hoge::Fuga::Piyoという正しいパッケージ名から得られる、あり得る組合せのクラスをあらかじめ定義してまわれば、たとえ上記のようなコードを書いてしまったとしても、つつがなく、期待通りにスクリプトは動作し、酔い心地を邪魔されることもありません。というわけで、なんらかの方法でもってHoge::Fuga::Piyoの順列組合せを生成して、その名前を元にパッケージを定義してみようと思い立ちました。さて、どのぐらいの組合せがあるのでしょうか。ここでは、パッケージ名に含まれるアルファベットの大文字小文字を考慮しない。組合せは::で分割された文字列どうしで行われる、という条件を置く。::で分割された各パートに含まれる文字を全て使う。つまり各パートの文字数は変わらない。すると、組合せの数はHoge::Fuga::Piyoの場合、4! * 4! * 4! = 1,000通りになります。もしパッケージ名がSome::Complicated::Nameのようなものだった場合、4! * 11! * 4! = 6,600通りなんてな大きな数字になってしまいます。これではダメです……。もう少し賢くさすがに、あり得るものを全部網羅しようなんて無茶でした。それに上記の条件だと組み合わせに過ぎないので、typoにありがちな隣のキーを押してしまったというような状況を救うことができません。もっと人間の現実にやりがちな間違いに適切に対応した、賢い仕組みが必要です。そこでCPANを漁ってみると、Acme::Tpyoなんてな、その名前からしてひとを食ったようなモジュールが見つかります。ちなみにTpyoは僕がtypoしたのではなく、モジュール名自体がtypoを表現しています。これはどんなモジュールかというと、ある文字列を渡すと、わざとtypoした文章を生成して返してくれるというものです。しかも、ただ単にtypoした文字列を返すだけではなく、typingしているひとの性格や能力、その時の状態をエミュレートしてtypoしてくれるというインテリジェントなtypoマシーンなのです。たとえば、typistがキーをどれだけミスタイプしがちか、指の太さ、キータッチの強さ、カフェインや、はたまたアルコールの摂取状況なんてことまで考慮してくれます。まさに、今回の問題を解決するに際して、まさにぴったりの機能を提供してくれるものであるといえます。typo safeな環境を作るそこで、上述のAcme::Tpyoを用いて、typoに強い環境を作ってみましょう。その際、基本的には「全方位作戦」の方針を踏襲しますが、無制限に候補を羅列してもしかたがないし、また、Acme::Tpyoのインテリジェントなtypo生成能力により現実的にあり得る間違いをかなりしぼれると思わるので、適当な数だけ候補を生成するようにして、大体において役立ちそうというラインで満足するようにしておきましょう。ここではAcme::Class::TypoSafeというモジュールを作成してみました。実装の詳細を検討する前に、まずは使い方を見ておきましょう。このモジュールのテストコードを以下に示します。use strict; 1945 use warnings; 1946 use Test::More qw(no_plan); 1947 use UNIVERSAL::require; 1948 1949 package Some::Complicated::Name; 1950 use Acme::Class::TypoSafe; 1951 1952 sub new { bless {}, shift } 1953 sub difficult_to_type_correctly {} 1954 1955 package main; 1956 1957 my %packages = Acme::Class::TypoSafe->typo_packages; 1958 for my $package (keys %packages) { 1959 my $obj = $package->new; 1960 isa_ok $obj, $package; 1961 isa_ok $obj, 'Some::Complicated::Name'; 1962 can_ok $obj, ('new', 'difficult_to_type_correctly', @{$packages{$package}}); 1963 } 1964 ここでは、Some::Complicated::Nameというクラスを定義して、それを使ってコードを書いていこうとしています。Acme::Class::TypoSafeの使い方は上記の通り簡単で、単にuseするだけです。その結果、デフォルトでは100通りのtypoしたらこうなるだろうなというパッケージ名を生成して、それをAcme::Class::TypoSafeをuseしたモジュールの子クラスとして定義します。 package Some::Complicated::Name; 1965 use Acme::Class::TypoSafe; 1966 1967 # ... (snip) ... 1968 1969 package main; 1970 my $foo = Sone::Conplicared::Name->new; 1971 my $bar = Sien::Cmopcalired::Nane->new; 1972 酔っぱらって手元不如意のままこんなコードを書いても、もう安心です。ちょっとしたtypoぐらい気にかけなくても、各種パラメタの調整具合や、確率的な事情にもよりますが、けっこう拾ってくれるようです。さらにtypoしてみるここまでは、パッケージ名についてtypoした場合のことしか考慮していませんでした。typoにおいて更に深刻なのは、パッケージ名よりも書く機会の多いメソッド名でしょう。そこで、Acme::Class::TypoSafeを、メソッド名についてもuse元パッケージのメソッド一覧からそれぞれのtypo文字列を生成してエイリアスメソッドを定義するということにしました。……といいたいところなのですが、この記事を書いている時点では、以下のように実装してみたものの、use元のメソッド一覧を取得することができず、そのためメソッド名についてはいまも安心できない状況が続いています。package Acme::Class::TypoSafe; 1973 use strict; 1974 use warnings; 1975 use Acme::Tpyo; 1976 use Class::Inspector; 1977 1978 our $VERSION = '0.01'; 1979 our %TYPO_PACKAGES = (); 1980 1981 sub import { 1982 my ($class, %args) = @_; 1983 my $package = caller; 1984 my $keyset = delete $args{keyset}; 1985 my $tpyist = delete $args{tpyist}; 1986 my $count = delete $args{count} || 100; 1987 my $tpyo = Acme::Tpyo->new($keyset, $tpyist); 1988 1989 my $i = 0; 1990 while ($i < $count) { 1991 my $typo_package = join '::', map { 1992 my $part = $_; 1993 my $typo = $tpyo->misspell($part); 1994 ucfirst(lc($typo)); 1995 } split '::', $package; 1996 1997 next if $package eq $typo_package || 1998 $typo_package =~ /[^[:alnum:]_\:]/; 1999 2000 # define typo packages 2001 eval <<"EOS"; 2002 package $typo_package; 2003 use base qw($package); 2004 EOS 2005 $TYPO_PACKAGES{$typo_package} = []; 2006 2007 # define typo functions for typo packages 2008 for my $function (@{Class::Inspector->functions($package)}) { 2009 warn $function; 2010 for (1 .. $count) { 2011 my $typo_function = $tpyo->misspell($function); 2012 warn $typo_function; 2013 next if $function eq $typo_function || 2014 $typo_function =~ /[^[:alnum:]_]/; 2015 { 2016 no strict 'refs'; 2017 *{$typo_package . '::' . $typo_function} 2018 = *{$package . '::' . $function}; 2019 } 2020 push @{$TYPO_PACKAGES{$typo_package}}, $typo_function; 2021 } 2022 } 2023 2024 $i++; 2025 } 2026 } 2027 2028 sub typo_packages { 2029 %TYPO_PACKAGES; 2030 } 2031 2032 1; 2033 このAcme::Class::TypoSafeはCodeReposに置いてありますので、typoに惑わされることのない安心・快適な泥酔コーディングを目指すPerlハッカーのみなさんに、改善をお願いしたいところであります。というわけで、次ははこべさん、お願いします。 2034 </description> 2035 <dc:date>2008-12-17T15:44:03Z</dc:date> 2036 <title>酔っぱらった勢いでコードを書く際に大切なこと</title> 2037 <pubDate>Wed, 17 Dec 2008 15:44:03 -0000</pubDate> 2038 <content:encoded><body><h1>酔っぱらった勢いでコードを書く際に大切なこと</h1><div class="section"><p>プライベートでコードを書く時は、たいていビールを飲みながらってな毎日のkentaro a.k.a. <a href="http://d.hatena.ne.jp/antipop/">id:antipop</a>です。こんにちは。</p><p>そんなわけで、ただでさえtypoが多いのに加えて、酔いにより手元はボロボロ。調子良く書いていても、つまらないtypoをいちいち修正してまわっていると、せっかくの気分のいい酔いも覚めてしまうというものです。そこで、コードを書く時には酒を飲まない、という以外の対策を考えてみました。</p><h2>とにかく全部ぶっつぶす</h2><p>ひとはどうしたところで間違いを犯すものです。酔っぱらっていても酔っぱらってなくても、それは同じ。ならば、あり得る名前をあらかじめ定義してはどうか、と考えました。</p><p>たとえば以下のようなクラスを定義したとします。</p><pre class="lang-perl"><code>package Hoge::Fuga::Piyo; 2039 use strict; 2040 use warnings; 2041 2042 # ... (snip) ... 2043 2044 1; 2045 </code></pre><p>このクラスを使う際に、上記したように手元不如意の状態に陥っていると、ついつい以下のように書いたりして、怒られたりします。</p><pre class="lang-perl"><code>my $obj = Hgeo::Ufag::Poyi-&gt;new; 2046 </code></pre><p>こんなことではいつtypoをしてしまうか心配で、あっという間に酔いも覚めてしまうでしょう。</p><p>この大変に深刻な問題避けるにはどうしたらいいか。そうです。Hoge::Fuga::Piyoという正しいパッケージ名から得られる、あり得る組合せのクラスをあらかじめ定義してまわれば、たとえ上記のようなコードを書いてしまったとしても、つつがなく、期待通りにスクリプトは動作し、酔い心地を邪魔されることもありません。</p><p>というわけで、なんらかの方法でもってHoge::Fuga::Piyoの順列組合せを生成して、その名前を元にパッケージを定義してみようと思い立ちました。さて、どのぐらいの組合せがあるのでしょうか。ここでは、</p><ul><li>パッケージ名に含まれるアルファベットの大文字小文字を考慮しない。</li><li>組合せは::で分割された文字列どうしで行われる、という条件を置く。</li><li>::で分割された各パートに含まれる文字を全て使う。つまり各パートの文字数は変わらない。</li></ul><p>すると、組合せの数はHoge::Fuga::Piyoの場合、<code>4! * 4! * 4! = 1,000</code>通りになります。もしパッケージ名がSome::Complicated::Nameのようなものだった場合、<code>4! * 11! * 4! = 6,600</code>通りなんてな大きな数字になってしまいます。これではダメです……。</p><h2>もう少し賢く</h2><p>さすがに、あり得るものを全部網羅しようなんて無茶でした。それに上記の条件だと組み合わせに過ぎないので、typoにありがちな隣のキーを押してしまったというような状況を救うことができません。もっと人間の現実にやりがちな間違いに適切に対応した、賢い仕組みが必要です。</p><p>そこでCPANを漁ってみると、<a href="http://search.cpan.org/dist/Acme-Tpyo/">Acme::Tpyo</a>なんてな、その名前からしてひとを食ったようなモジュールが見つかります。ちなみにTpyoは僕がtypoしたのではなく、モジュール名自体がtypoを表現しています。</p><p>これはどんなモジュールかというと、ある文字列を渡すと、わざとtypoした文章を生成して返してくれるというものです。しかも、ただ単にtypoした文字列を返すだけではなく、typingしているひとの性格や能力、その時の状態をエミュレートしてtypoしてくれるというインテリジェントなtypoマシーンなのです。たとえば、typistがキーをどれだけミスタイプしがちか、指の太さ、キータッチの強さ、カフェインや、はたまたアルコールの摂取状況なんてことまで考慮してくれます。まさに、今回の問題を解決するに際して、まさにぴったりの機能を提供してくれるものであるといえます。</p><h2>typo safeな環境を作る</h2><p>そこで、上述のAcme::Tpyoを用いて、typoに強い環境を作ってみましょう。その際、基本的には「全方位作戦」の方針を踏襲しますが、無制限に候補を羅列してもしかたがないし、また、Acme::Tpyoのインテリジェントなtypo生成能力により現実的にあり得る間違いをかなりしぼれると思わるので、適当な数だけ候補を生成するようにして、大体において役立ちそうというラインで満足するようにしておきましょう。</p><p>ここでは<a href="http://svn.coderepos.org/share/lang/perl/Acme-Class-TypoSafe/trunk/">Acme::Class::TypoSafe</a>というモジュールを作成してみました。実装の詳細を検討する前に、まずは使い方を見ておきましょう。このモジュールのテストコードを以下に示します。</p><pre class="lang-perl"><code>use strict; 2047 use warnings; 2048 use Test::More qw(no_plan); 2049 use UNIVERSAL::require; 2050 2051 package Some::Complicated::Name; 2052 use Acme::Class::TypoSafe; 2053 2054 sub new { bless {}, shift } 2055 sub difficult_to_type_correctly {} 2056 2057 package main; 2058 2059 my %packages = Acme::Class::TypoSafe-&gt;typo_packages; 2060 for my $package (keys %packages) { 2061 my $obj = $package-&gt;new; 2062 isa_ok $obj, $package; 2063 isa_ok $obj, &apos;Some::Complicated::Name&apos;; 2064 can_ok $obj, (&apos;new&apos;, &apos;difficult_to_type_correctly&apos;, @{$packages{$package}}); 2065 } 2066 </code></pre><p>ここでは、Some::Complicated::Nameというクラスを定義して、それを使ってコードを書いていこうとしています。Acme::Class::TypoSafeの使い方は上記の通り簡単で、単に<code>use</code>するだけです。その結果、デフォルトでは100通りのtypoしたらこうなるだろうなというパッケージ名を生成して、それをAcme::Class::TypoSafeを<code>use</code>したモジュールの子クラスとして定義します。</p><pre class="lang-perl"><code> package Some::Complicated::Name; 2067 use Acme::Class::TypoSafe; 2068 2069 # ... (snip) ... 2070 2071 package main; 2072 my $foo = Sone::Conplicared::Name-&gt;new; 2073 my $bar = Sien::Cmopcalired::Nane-&gt;new; 2074 </code></pre><p>酔っぱらって手元不如意のままこんなコードを書いても、もう安心です。ちょっとしたtypoぐらい気にかけなくても、各種パラメタの調整具合や、確率的な事情にもよりますが、けっこう拾ってくれるようです。</p><h2>さらにtypoしてみる</h2><p>ここまでは、パッケージ名についてtypoした場合のことしか考慮していませんでした。typoにおいて更に深刻なのは、パッケージ名よりも書く機会の多いメソッド名でしょう。そこで、Acme::Class::TypoSafeを、メソッド名についても<code>use</code>元パッケージのメソッド一覧からそれぞれのtypo文字列を生成してエイリアスメソッドを定義するということにしました。</p><p>……といいたいところなのですが、この記事を書いている時点では、以下のように実装してみたものの、<code>use</code>元のメソッド一覧を取得することができず、そのためメソッド名についてはいまも安心できない状況が続いています。</p><pre class="lang-perl"><code>package Acme::Class::TypoSafe; 2075 use strict; 2076 use warnings; 2077 use Acme::Tpyo; 2078 use Class::Inspector; 2079 2080 our $VERSION = &apos;0.01&apos;; 2081 our %TYPO_PACKAGES = (); 2082 2083 sub import { 2084 my ($class, %args) = @_; 2085 my $package = caller; 2086 my $keyset = delete $args{keyset}; 2087 my $tpyist = delete $args{tpyist}; 2088 my $count = delete $args{count} || 100; 2089 my $tpyo = Acme::Tpyo-&gt;new($keyset, $tpyist); 2090 2091 my $i = 0; 2092 while ($i &lt; $count) { 2093 my $typo_package = join &apos;::&apos;, map { 2094 my $part = $_; 2095 my $typo = $tpyo-&gt;misspell($part); 2096 ucfirst(lc($typo)); 2097 } split &apos;::&apos;, $package; 2098 2099 next if $package eq $typo_package || 2100 $typo_package =~ /[^[:alnum:]_\:]/; 2101 2102 # define typo packages 2103 eval &lt;&lt;&quot;EOS&quot;; 2104 package $typo_package; 2105 use base qw($package); 2106 EOS 2107 $TYPO_PACKAGES{$typo_package} = []; 2108 2109 # define typo functions for typo packages 2110 for my $function (@{Class::Inspector-&gt;functions($package)}) { 2111 warn $function; 2112 for (1 .. $count) { 2113 my $typo_function = $tpyo-&gt;misspell($function); 2114 warn $typo_function; 2115 next if $function eq $typo_function || 2116 $typo_function =~ /[^[:alnum:]_]/; 2117 { 2118 no strict &apos;refs&apos;; 2119 *{$typo_package . &apos;::&apos; . $typo_function} 2120 = *{$package . &apos;::&apos; . $function}; 2121 } 2122 push @{$TYPO_PACKAGES{$typo_package}}, $typo_function; 2123 } 2124 } 2125 2126 $i++; 2127 } 2128 } 2129 2130 sub typo_packages { 2131 %TYPO_PACKAGES; 2132 } 2133 2134 1; 2135 </code></pre><p>このAcme::Class::TypoSafeは<a href="http://svn.coderepos.org/share/lang/perl/Acme-Class-TypoSafe/trunk/">CodeReposに置いてあります</a>ので、typoに惑わされることのない安心・快適な泥酔コーディングを目指すPerlハッカーのみなさんに、改善をお願いしたいところであります。</p><p>というわけで、次は<a href="http://d.hatena.ne.jp/hakobe932/">はこべさん</a>、お願いします。</p></div></body> 2136 </content:encoded> 2137 <dcterms:modified>2008-12-17T15:44:03Z</dcterms:modified> 2138 <guid isPermaLink="false">tag:perl-users.jp,2006:http://perl-users.jp/articles/advent-calendar/2008/17.html</guid> 2139 </item> 2140 <item> 2141 <author>nobody@example.com</author> 2142 <dc:creator>nobody@example.com</dc:creator> 2143 <link>http://perl-users.jp/articles/advent-calendar/2008/18.html</link> 2144 <description>ファイルのすべてを飲み込む方法 原稿を落としたら,すしをおごりにアメリカに行かないといけないとの噂にgkbrしているid:hakobe932です. 932は草津の932です.こんにちは. ファイルの中身をすべて読み込む処理というのは,非常によくある処理です.TIMTOWTDIが信条のPerlでは,ファイルの中身をすべて読み込む方法もたくさんあります.ここでは,どんな方法があるのか紹介いきましょう. 行単位で読み込むもっともシンプルなのは行入力演算子(<>)を使って行毎にデータを読み込み,それを連結する方法です.open my $fh, '<', './inputfile' 2145 or die "failed to open: $!"; 2146 my $content = ''; 2147 while (my $line = <$fh>) { 2148 $content .= $line; 2149 } 2150 print $content; 2151 もう少し工夫して後置whileを使うと1行で書くことができます.open my $fh, '<', './inputfile' 2152 or die "failed to open: $!"; 2153 my $content = ''; 2154 $content .= $_ while <$fh>; 2155 print $content; 2156 行入力演算子はリストコンテキストで評価すると,すべての行のリストが返ってくるので,次のように書くこともできます. open my $fh, '<', './inputfile' 2157 or die "failed to open: $!"; 2158 my $content = join '', <$fh>; 2159 print $content; 2160 $/を利用して読み込む行入力演算子でファイルの内容をすべて読み込むのは,シンプルで比較的わかりやすいですが,行を一度保存するのでメモリを食いがちです.もう少し効率の良い方法として$/変数を使うやりかたがあります.グローバル変数$/をローカル化すると行入力演算子でファイルの内容をすべて読み込むことができます. open my $fh, '<', './inputfile' 2161 or die "failed to open: $!"; 2162 my $content = ''; 2163 { 2164 local $/; 2165 $content = <$fh>; 2166 } 2167 print $content; 2168 これでは少しかっこわるいのですが,doブロックを使えばもう少しきれいです.open my $fh, '<', './inputfile' 2169 or die "failed to open: $!"; 2170 my $content = do { local $/; <$fh> }; 2171 print $content; 2172 これでコードもずいぶんスッキリしました.しかし,おまじない的なコードで,ぱっとみたときに何をやっているのかわかりにくいですね. ファイルのすべてを飲み込む そこで,ファイルのすべてを飲み込むためにPerl6::Slurpを使いましょう.Perl6::Slurpは名前の通りファイルの内容をすべて飲み込みます. use Perl6::Slurp; 2173 my $content = slurp './inputfile'; 2174 print $content; 2175 Perl6::Slurpでexportされるslurp関数を使えば,おまじない的な部分がなくなって,とってもシンプルでわかりやすいコードが書けます.しかも,ファイルハンドルをopenする手間もへって良いとこづくしですね!他にPath::Classを使うという手もあります. use Path::Class; 2176 my $inputfile = file('./inputfile'); 2177 my $content = $inputfile->slurp; 2178 print $content; 2179 Path::Classはslurpするのに使う以外にも,ファイル操作に関する便利なメソッドがたくさん用意されています.モダンなPerlコードでは定番の のファイル操作のモジュールですね.このように,すでにあるモジュールを使うとすっきり簡潔にコードが書けます.モジュールの充実しているPerlの醍醐味かもデスネ.おまけ: readシステムコールで直接読み込むsysread関数をつかえばreadシステムコールを発行して指定サイズ文だけファイルからデータを読み込めます.-s 演算子をファイルハンドルに対して使えばファイルサイズが取れるので,以下のようにすればファイルの内容をすべて読み込むことができます. open my $fh, '<', './inputfile' 2180 or die "failed to open: $!"; 2181 my $content; 2182 sysread $fh, $content, -s $fh; 2183 print $content; 2184 事前にサイズを指定してシステムコールを発行するので,非常に高速なはずです.ただし,生のシステムコールを叩くのでいろいろな例外事項の対処を自分でしないといけないため,あまりおすすめできません! まとめというわけで,いろいろな方法でファイルの中身をすべて読み込んで見ました.多くの選択肢があってなかなかPerlらしい感じですね.わかりやすさや,覚えやすさ,書いているプログラムの性質などに合わせて,良さそうなのを選べば良いと思います.個人的には,Perl6::Slurpや$/を使った方法をよく使います.これからは,Path::Classを使うのがオシャレかもしれませんね.もし,このほかにもファイルを飲み込む方法があればぜひ教えてクダサイ.といわけで,次はid:secondlifeさんよろしくお願いします. 2185 </description> 2186 <dc:date>2008-12-18T14:45:57Z</dc:date> 2187 <title>ファイルのすべてを飲み込む方法</title> 2188 <pubDate>Thu, 18 Dec 2008 14:45:57 -0000</pubDate> 2189 <content:encoded><body><h1>ファイルのすべてを飲み込む方法</h1><div class="section"><p> 原稿を落としたら,すしをおごりにアメリカに行かないといけないとの噂にgkbrしている<a href="http://d.hatena.ne.jp/hakobe932">id:hakobe932</a>です. 932は草津の932です.こんにちは. </p><p>ファイルの中身をすべて読み込む処理というのは,非常によくある処理です.TIMTOWTDIが信条のPerlでは,ファイルの中身をすべて読み込む方法もたくさんあります.ここでは,どんな方法があるのか紹介いきましょう. </p><h2>行単位で読み込む</h2><p>もっともシンプルなのは行入力演算子(&lt;&gt;)を使って行毎にデータを読み込み,それを連結する方法です.</p><pre class="lang-perl"><code>open my $fh, &apos;&lt;&apos;, &apos;./inputfile&apos; 2190 or die &quot;failed to open: $!&quot;; 2191 my $content = &apos;&apos;; 2192 while (my $line = &lt;$fh&gt;) { 2193 $content .= $line; 2194 } 2195 print $content; 2196 </code></pre><p>もう少し工夫して後置whileを使うと1行で書くことができます.</p><pre class="lang-perl"><code>open my $fh, &apos;&lt;&apos;, &apos;./inputfile&apos; 2197 or die &quot;failed to open: $!&quot;; 2198 my $content = &apos;&apos;; 2199 $content .= $_ while &lt;$fh&gt;; 2200 print $content; 2201 </code></pre><p>行入力演算子はリストコンテキストで評価すると,すべての行のリストが返ってくるので,次のように書くこともできます. </p><pre class="lang-perl"><code>open my $fh, &apos;&lt;&apos;, &apos;./inputfile&apos; 2202 or die &quot;failed to open: $!&quot;; 2203 my $content = join &apos;&apos;, &lt;$fh&gt;; 2204 print $content; 2205 </code></pre><h2>$/を利用して読み込む</h2><p>行入力演算子でファイルの内容をすべて読み込むのは,シンプルで比較的わかりやすいですが,行を一度保存するのでメモリを食いがちです.もう少し効率の良い方法として$/変数を使うやりかたがあります.</p><p>グローバル変数$/をローカル化すると行入力演算子でファイルの内容をすべて読み込むことができます. </p><pre class="lang-perl"><code>open my $fh, &apos;&lt;&apos;, &apos;./inputfile&apos; 2206 or die &quot;failed to open: $!&quot;; 2207 my $content = &apos;&apos;; 2208 { 2209 local $/; 2210 $content = &lt;$fh&gt;; 2211 } 2212 print $content; 2213 </code></pre><p>これでは少しかっこわるいのですが,doブロックを使えばもう少しきれいです.</p><pre class="lang-perl"><code>open my $fh, &apos;&lt;&apos;, &apos;./inputfile&apos; 2214 or die &quot;failed to open: $!&quot;; 2215 my $content = do { local $/; &lt;$fh&gt; }; 2216 print $content; 2217 </code></pre><p>これでコードもずいぶんスッキリしました.しかし,おまじない的なコードで,ぱっとみたときに何をやっているのかわかりにくいですね. </p><h2>ファイルのすべてを飲み込む</h2><p> そこで,ファイルのすべてを飲み込むために<a href="http://search.cpan.org/perldoc?Perl6::Slurp">Perl6::Slurp</a>を使いましょう.Perl6::Slurpは名前の通りファイルの内容をすべて飲み込みます. </p><pre class="lang-perl"><code>use Perl6::Slurp; 2218 my $content = slurp &apos;./inputfile&apos;; 2219 print $content; 2220 </code></pre><p>Perl6::Slurpでexportされるslurp関数を使えば,おまじない的な部分がなくなって,とってもシンプルでわかりやすいコードが書けます.しかも,ファイルハンドルをopenする手間もへって良いとこづくしですね!</p><p>他に<a href="http://search.cpan.org/perldoc?Path::Class">Path::Class</a>を使うという手もあります. </p><pre class="lang-perl"><code>use Path::Class; 2221 my $inputfile = file(&apos;./inputfile&apos;); 2222 my $content = $inputfile-&gt;slurp; 2223 print $content; 2224 </code></pre><p>Path::Classはslurpするのに使う以外にも,ファイル操作に関する便利なメソッドがたくさん用意されています.モダンなPerlコードでは定番の のファイル操作のモジュールですね.</p><p>このように,すでにあるモジュールを使うとすっきり簡潔にコードが書けます.モジュールの充実しているPerlの醍醐味かもデスネ.</p><h2>おまけ: readシステムコールで直接読み込む</h2><p>sysread関数をつかえばreadシステムコールを発行して指定サイズ文だけファイルからデータを読み込めます.-s 演算子をファイルハンドルに対して使えばファイルサイズが取れるので,以下のようにすればファイルの内容をすべて読み込むことができます. </p><pre class="lang-perl"><code>open my $fh, &apos;&lt;&apos;, &apos;./inputfile&apos; 2225 or die &quot;failed to open: $!&quot;; 2226 my $content; 2227 sysread $fh, $content, -s $fh; 2228 print $content; 2229 </code></pre><p>事前にサイズを指定してシステムコールを発行するので,非常に高速なはずです.ただし,生のシステムコールを叩くのでいろいろな例外事項の対処を自分でしないといけないため,あまりおすすめできません! </p><h2>まとめ</h2><p>というわけで,いろいろな方法でファイルの中身をすべて読み込んで見ました.多くの選択肢があってなかなかPerlらしい感じですね.わかりやすさや,覚えやすさ,書いているプログラムの性質などに合わせて,良さそうなのを選べば良いと思います.個人的には,Perl6::Slurpや$/を使った方法をよく使います.これからは,Path::Classを使うのがオシャレかもしれませんね.</p><p>もし,このほかにもファイルを飲み込む方法があればぜひ教えてクダサイ.</p><p>といわけで,次は<a href="http://d.hatena.ne.jp/secondlife">id:secondlife</a>さんよろしくお願いします.</p></div></body> 2230 </content:encoded> 2231 <dcterms:modified>2008-12-18T14:45:57Z</dcterms:modified> 2232 <guid isPermaLink="false">tag:perl-users.jp,2006:http://perl-users.jp/articles/advent-calendar/2008/18.html</guid> 1811 2233 </item> 1812 2234 </channel>
![(please configure the [header_logo] section in trac.ini)](/share/chrome/site/your_project_logo.png)