Changeset 26873
- Timestamp:
- 12/16/08 19:20:01 (4 years ago)
- Location:
- websites/perl-users.jp
- Files:
-
- 2 modified
-
html/articles/advent-calendar/2008/16.html (modified) (6 diffs)
-
ttroot/articles/advent-calendar/2008/16.html (modified) (6 diffs)
Legend:
- Unmodified
- Added
- Removed
-
websites/perl-users.jp/html/articles/advent-calendar/2008/16.html
r26870 r26873 13 13 <p>すしを奢らなければいけないなんて、バトンを渡されてから知りました。おいしい寿司が食いたい sekimura です。</p> 14 14 15 <p>今回は使いこなすと気持ちよくて、使いすぎると気持ち悪いと言われてしまう grep と mapの使い方について紹介します。この二つは文法がよく似ていて、同時に使われることも多いので一気に両方の使い方を覚えるのをおすすめします。</p>15 <p>今回は使いこなすと気持ちよくて、使いすぎると気持ち悪いと言われてしまう <code>grep</code> と <code>map</code> の使い方について紹介します。この二つは文法がよく似ていて、同時に使われることも多いので一気に両方の使い方を覚えるのをおすすめします。</p> 16 16 17 17 <h2>grep: 配列をフィルターする</h2> 18 18 19 <p>まずは、前回覚えた perldoc を使って grepとはなにかを調べてみましょう。</p>19 <p>まずは、前回覚えた perldoc を使って <code>grep</code> とはなにかを調べてみましょう。</p> 20 20 21 21 <pre><code>$ perldoc -f grep … … 33 33 </code></pre> 34 34 35 <p>「U nix コマンドの grep 等のコマンドと似てるけど違うもの。だって正規表現以外も使えるんだぜ」とか書いていますね。 LIST の全要素を($_ を局所的にセットしながら) BLOCK か EXPR で評価し、その結果が真となるものだけからなる配列を返します。スカラコンテキストの場合は、結果が真となる要素の数を返します。例えば %ENV のキーからなる配列から "H"で始まるものだけを抜き出すには以下のようにします。</p>35 <p>「UNIX コマンドの <code>grep</code> 等のコマンドと似てるけど違うもの。だって正規表現以外も使えるんだぜ」とか書いていますね。LIST の全要素を(<code>$_</code> を局所的にセットしながら)BLOCK か <code>EXPR</code> で評価し、その結果が真となるものだけからなる配列を返します。スカラコンテキストの場合は、結果が真となる要素の数を返します。例えば <code>%ENV</code> のキーからなる配列から <code>"H"</code> で始まるものだけを抜き出すには以下のようにします。</p> 36 36 37 37 <pre><code>$ perl -e 'print join " ", (grep /^H/, keys %ENV), "\n"' 38 HOME HISTCONTROL 38 HOME HISTCONTROL 39 39 </code></pre> 40 40 41 <p> grepは BLOCK を使った場合にもっと楽しくなります。例えば、以下のように、ある二つの配列をつなぎ合わせたものから、重複を取り除いた配列を得ることができます。</p>41 <p><code>grep</code> は BLOCK を使った場合にもっと楽しくなります。例えば、以下のように、ある二つの配列をつなぎ合わせたものから、重複を取り除いた配列を得ることができます。</p> 42 42 43 <pre><code> 44 my @cities = ('Sapporo', 'Nishitokyo', 'Yokohama'); 43 <pre><code>my @cities = ('Sapporo', 'Nishitokyo', 'Yokohama'); 45 44 my @prefs = ('Hokkaido', 'Tokyo', 'Yokohama'); 46 45 my %seen; … … 51 50 </code></pre> 52 51 53 <p>逆に重複したものだけ抜き出したいときには以下のように grep で取得した配列に対して grepすることで得られます。</p>52 <p>逆に重複したものだけ抜き出したいときには以下のように <code>grep</code> で取得した配列に対して <code>grep</code> することで得られます。</p> 54 53 55 <pre><code> 56 my @lunch = ('Bento', 'Ramen', 'Onigiri', 'Curry'); 54 <pre><code>my @lunch = ('Bento', 'Ramen', 'Onigiri', 'Curry'); 57 55 my @dinner = ('Tonkatsu', 'Ramen', 'Curry'); 58 56 my %seen; 59 57 60 my @dup = grep { $seen{$_} >= 2 } grep { ++$seen{$_} >1 } (@luch, @dinner);58 my @dup = grep { $seen{$_} >= 2 } grep { ++$seen{$_} > 1 } (@luch, @dinner); 61 59 62 60 ## @dup には ('Ramen', 'Curry') が入る。 … … 65 63 <h2>map: 配列の要素を変換する</h2> 66 64 67 <p>例によって perldoc -f mapしましょう。</p>65 <p>例によって <code>perldoc -f map</code> しましょう。</p> 68 66 69 67 <pre><code>$ perldoc -f map … … 78 76 </code></pre> 79 77 80 <p>ほとんど同じことが書いてありますね。 grep はフィルターなので、得られる配列は与えられた配列のサブセットになるのに対して、 mapでは与えられた各要素を変換し、その結果を配列として得ることが可能です。</p>78 <p>ほとんど同じことが書いてありますね。<code>grep</code> はフィルターなので、得られる配列は与えられた配列のサブセットになるのに対して、<code>map</code> では与えられた各要素を変換し、その結果を配列として得ることが可能です。</p> 81 79 82 <pre><code> 83 my %price_map = ( 84 'Ramen' => 400, 85 'Curry' => 650, 86 'Katsudon' => 600, 80 <pre><code>my %price_map = ( 81 'Ramen' => 400, 82 'Curry' => 650, 83 'Katsudon' => 600, 87 84 ); 88 85 my @today = ('Ramen', 'Curry'); … … 94 91 </code></pre> 95 92 96 <p> grep のときにはスルーしましたが、 BLOCK 内での $_ は元の要素のリファレンスなので、 $_ を変更してしまうと、元の要素も変更されてしまいます。よく、「破壊的」と呼ばれるケースですね。これを防ぐには、 BLOCK の内部で my変数にコピーしてから変更を加えていきます。</p>93 <p><code>grep</code> のときにはスルーしましたが、 BLOCK 内での <code>$_</code> は元の要素のリファレンスなので、<code>$_</code> を変更してしまうと、元の要素も変更されてしまいます。よく、「破壊的」と呼ばれるケースですね。これを防ぐには、 BLOCK の内部で <code>my</code> 変数にコピーしてから変更を加えていきます。</p> 97 94 98 <pre><code> 99 my @addresses = ('katsuo@example.com', 'wakame@example.com', 'tara@example.com'); 95 <pre><code>my @addresses = ('katsuo@example.com', 'wakame@example.com', 'tara@example.com'); 100 96 my @no_spam = map { my $email = $_; $email =~ s/\@/ at /; $email } @addresses; 101 </code></pre>102 97 103 98 ## @no_spam には ('katsuo at example.com', 'wakame at example.com', 'tara at example.com) が入る。 104 99 </code></pre> 105 100 106 <p>このようにして、 @addresses の要素を変更すること無く @no_spamという変換後の要素を持つ配列を得ることができます。</p>101 <p>このようにして、<code>@addresses</code> の要素を変更すること無く <code>@no_spam</code> という変換後の要素を持つ配列を得ることができます。</p> 107 102 108 103 <h2>使いどころ</h2> 109 <p> grep, map 両方共 for, foreach のループで書き換えることができますが、それぞれ「フィルター」と「変換」という意味をコードを読む人に的確に伝えることができるのがメリットではないでしょうか。その他にも、デバッガーやワンライナーでループ処理を簡素に書けるのも利点です。 sort 等のコマンドと組み合わせて Unixのパイプのようにデータを処理すると自分が偉くなったような錯覚に陥るのがオススメどころです。104 <p><code>grep</code>, <code>map</code> 両方共 <code>for</code>, <code>foreach</code> のループで書き換えることができますが、それぞれ「フィルター」と「変換」という意味をコードを読む人に的確に伝えることができるのがメリットではないでしょうか。その他にも、デバッガーやワンライナーでループ処理を簡素に書けるのも利点です。 <code>sort</code> 等のコマンドと組み合わせて UNIX のパイプのようにデータを処理すると自分が偉くなったような錯覚に陥るのがオススメどころです。 110 105 </p> 111 <p>ただし、 grep, map を単にループ処理をするために、左辺値を受け取らずに使うのはコードを読む人を混乱させるので避けた方がいいでしょう。 (SEE ALSO perldoc perlstyle)106 <p>ただし、 <code>grep</code>, <code>map</code> を単にループ処理をするために、左辺値を受け取らずに使うのはコードを読む人を混乱させるので避けた方がいいでしょう。 (SEE ALSO <code>perldoc perlstyle</code>) 112 107 </p> 113 108 -
websites/perl-users.jp/ttroot/articles/advent-calendar/2008/16.html
r26870 r26873 14 14 <p>すしを奢らなければいけないなんて、バトンを渡されてから知りました。おいしい寿司が食いたい sekimura です。</p> 15 15 16 <p>今回は使いこなすと気持ちよくて、使いすぎると気持ち悪いと言われてしまう grep と mapの使い方について紹介します。この二つは文法がよく似ていて、同時に使われることも多いので一気に両方の使い方を覚えるのをおすすめします。</p>16 <p>今回は使いこなすと気持ちよくて、使いすぎると気持ち悪いと言われてしまう <code>grep</code> と <code>map</code> の使い方について紹介します。この二つは文法がよく似ていて、同時に使われることも多いので一気に両方の使い方を覚えるのをおすすめします。</p> 17 17 18 18 <h2>grep: 配列をフィルターする</h2> 19 19 20 <p>まずは、前回覚えた perldoc を使って grepとはなにかを調べてみましょう。</p>20 <p>まずは、前回覚えた perldoc を使って <code>grep</code> とはなにかを調べてみましょう。</p> 21 21 22 22 <pre><code>$ perldoc -f grep … … 34 34 </code></pre> 35 35 36 <p>「U nix コマンドの grep 等のコマンドと似てるけど違うもの。だって正規表現以外も使えるんだぜ」とか書いていますね。 LIST の全要素を($_ を局所的にセットしながら) BLOCK か EXPR で評価し、その結果が真となるものだけからなる配列を返します。スカラコンテキストの場合は、結果が真となる要素の数を返します。例えば %ENV のキーからなる配列から "H"で始まるものだけを抜き出すには以下のようにします。</p>36 <p>「UNIX コマンドの <code>grep</code> 等のコマンドと似てるけど違うもの。だって正規表現以外も使えるんだぜ」とか書いていますね。LIST の全要素を(<code>$_</code> を局所的にセットしながら)BLOCK か <code>EXPR</code> で評価し、その結果が真となるものだけからなる配列を返します。スカラコンテキストの場合は、結果が真となる要素の数を返します。例えば <code>%ENV</code> のキーからなる配列から <code>"H"</code> で始まるものだけを抜き出すには以下のようにします。</p> 37 37 38 38 <pre><code>$ perl -e 'print join " ", (grep /^H/, keys %ENV), "\n"' 39 HOME HISTCONTROL 39 HOME HISTCONTROL 40 40 </code></pre> 41 41 42 <p> grepは BLOCK を使った場合にもっと楽しくなります。例えば、以下のように、ある二つの配列をつなぎ合わせたものから、重複を取り除いた配列を得ることができます。</p>42 <p><code>grep</code> は BLOCK を使った場合にもっと楽しくなります。例えば、以下のように、ある二つの配列をつなぎ合わせたものから、重複を取り除いた配列を得ることができます。</p> 43 43 44 <pre><code> 45 my @cities = ('Sapporo', 'Nishitokyo', 'Yokohama'); 44 <pre><code>my @cities = ('Sapporo', 'Nishitokyo', 'Yokohama'); 46 45 my @prefs = ('Hokkaido', 'Tokyo', 'Yokohama'); 47 46 my %seen; … … 52 51 </code></pre> 53 52 54 <p>逆に重複したものだけ抜き出したいときには以下のように grep で取得した配列に対して grepすることで得られます。</p>53 <p>逆に重複したものだけ抜き出したいときには以下のように <code>grep</code> で取得した配列に対して <code>grep</code> することで得られます。</p> 55 54 56 <pre><code> 57 my @lunch = ('Bento', 'Ramen', 'Onigiri', 'Curry'); 55 <pre><code>my @lunch = ('Bento', 'Ramen', 'Onigiri', 'Curry'); 58 56 my @dinner = ('Tonkatsu', 'Ramen', 'Curry'); 59 57 my %seen; 60 58 61 my @dup = grep { $seen{$_} >= 2 } grep { ++$seen{$_} >1 } (@luch, @dinner);59 my @dup = grep { $seen{$_} >= 2 } grep { ++$seen{$_} > 1 } (@luch, @dinner); 62 60 63 61 ## @dup には ('Ramen', 'Curry') が入る。 … … 66 64 <h2>map: 配列の要素を変換する</h2> 67 65 68 <p>例によって perldoc -f mapしましょう。</p>66 <p>例によって <code>perldoc -f map</code> しましょう。</p> 69 67 70 68 <pre><code>$ perldoc -f map … … 79 77 </code></pre> 80 78 81 <p>ほとんど同じことが書いてありますね。 grep はフィルターなので、得られる配列は与えられた配列のサブセットになるのに対して、 mapでは与えられた各要素を変換し、その結果を配列として得ることが可能です。</p>79 <p>ほとんど同じことが書いてありますね。<code>grep</code> はフィルターなので、得られる配列は与えられた配列のサブセットになるのに対して、<code>map</code> では与えられた各要素を変換し、その結果を配列として得ることが可能です。</p> 82 80 83 <pre><code> 84 my %price_map = ( 85 'Ramen' => 400, 86 'Curry' => 650, 87 'Katsudon' => 600, 81 <pre><code>my %price_map = ( 82 'Ramen' => 400, 83 'Curry' => 650, 84 'Katsudon' => 600, 88 85 ); 89 86 my @today = ('Ramen', 'Curry'); … … 95 92 </code></pre> 96 93 97 <p> grep のときにはスルーしましたが、 BLOCK 内での $_ は元の要素のリファレンスなので、 $_ を変更してしまうと、元の要素も変更されてしまいます。よく、「破壊的」と呼ばれるケースですね。これを防ぐには、 BLOCK の内部で my変数にコピーしてから変更を加えていきます。</p>94 <p><code>grep</code> のときにはスルーしましたが、 BLOCK 内での <code>$_</code> は元の要素のリファレンスなので、<code>$_</code> を変更してしまうと、元の要素も変更されてしまいます。よく、「破壊的」と呼ばれるケースですね。これを防ぐには、 BLOCK の内部で <code>my</code> 変数にコピーしてから変更を加えていきます。</p> 98 95 99 <pre><code> 100 my @addresses = ('katsuo@example.com', 'wakame@example.com', 'tara@example.com'); 96 <pre><code>my @addresses = ('katsuo@example.com', 'wakame@example.com', 'tara@example.com'); 101 97 my @no_spam = map { my $email = $_; $email =~ s/\@/ at /; $email } @addresses; 102 </code></pre>103 98 104 99 ## @no_spam には ('katsuo at example.com', 'wakame at example.com', 'tara at example.com) が入る。 105 100 </code></pre> 106 101 107 <p>このようにして、 @addresses の要素を変更すること無く @no_spamという変換後の要素を持つ配列を得ることができます。</p>102 <p>このようにして、<code>@addresses</code> の要素を変更すること無く <code>@no_spam</code> という変換後の要素を持つ配列を得ることができます。</p> 108 103 109 104 <h2>使いどころ</h2> 110 <p> grep, map 両方共 for, foreach のループで書き換えることができますが、それぞれ「フィルター」と「変換」という意味をコードを読む人に的確に伝えることができるのがメリットではないでしょうか。その他にも、デバッガーやワンライナーでループ処理を簡素に書けるのも利点です。 sort 等のコマンドと組み合わせて Unixのパイプのようにデータを処理すると自分が偉くなったような錯覚に陥るのがオススメどころです。105 <p><code>grep</code>, <code>map</code> 両方共 <code>for</code>, <code>foreach</code> のループで書き換えることができますが、それぞれ「フィルター」と「変換」という意味をコードを読む人に的確に伝えることができるのがメリットではないでしょうか。その他にも、デバッガーやワンライナーでループ処理を簡素に書けるのも利点です。 <code>sort</code> 等のコマンドと組み合わせて UNIX のパイプのようにデータを処理すると自分が偉くなったような錯覚に陥るのがオススメどころです。 111 106 </p> 112 <p>ただし、 grep, map を単にループ処理をするために、左辺値を受け取らずに使うのはコードを読む人を混乱させるので避けた方がいいでしょう。 (SEE ALSO perldoc perlstyle)107 <p>ただし、 <code>grep</code>, <code>map</code> を単にループ処理をするために、左辺値を受け取らずに使うのはコードを読む人を混乱させるので避けた方がいいでしょう。 (SEE ALSO <code>perldoc perlstyle</code>) 113 108 </p> 114 109
![(please configure the [header_logo] section in trac.ini)](/share/chrome/site/your_project_logo.png)