ノンプログラマーのためのPerl 〜 forと正規表現を使ったお得なセット

こんにちは。こちらはPerl入学式アドベントカレンダーの20日目の記事です。
(おっと日付が……🙇)

qiita.com

はじめに

あらためまして、まずここで「Perl入学式」について簡単に説明しておきますと、Perl入学式とは、Perlまたはプログラミング自体の未経験者に、Perlを通してプログラミングの基礎をお教えしましょう、という無料のプログラミング講座です。
www.perl-entrance.org

ぼくも2013年の8月、同年度第3回の補講から、まったく素人の状態で生徒として参加しまして、
Perl入学式#3補講に行ってきた - 103

その翌年春からはサポーター(運営側)に加わっています。

……といっても最近は地理的な問題などもあり、リモート参加が大半なのですが。
継続的に現場で開講しているメンバーの皆さんには、本当に敬服するばかりです。

さて本日は、ぼくのようなノンプログラマー、つまり普段の仕事ではプログラミングとは関係のないことをしている人*1が、プログラミング言語Perlを学ぶことによって、どんな便利なことになるのか、みたいな話をします。

といっても、その便利さをすべて説明しようとしたら大変な時間と文章量が必要になりますので、今回はその中でもとくに「これは」というところに絞って説明します。

for文

多くの場合、ぼくが普段の仕事をしているときに、「んー、こりゃプログラミングの力を借りた方がよさそうだな!」と思うのは、

テキストファイルAの内容に何らかの処理を加えて、テキストファイルBとして保存する

みたいなことをやりたい時です。

具体例を挙げてみると、たとえば以下のような名簿があったとして、

ジョン・レノン
ポール・マッカートニー
ジョージ・ハリスン
リンゴ・スター

このすべての文末に敬称「さん」を付けたい、という場合。

いやもちろん、このぐらいなら自分で書けばいいんですが、これが何百人もいたら大変なので……ということ。

こんなとき、ぼくだったらこんなコードを書きます。

#!/usr/bin/env perl
use strict;
use warnings;

my @band = qw/
ジョン・レノン
ポール・マッカートニー
ジョージ・ハリスン
リンゴ・スター
/;

for my $member (@band) {
    print "$memberさん\n";
}

まず配列 @band に4人の名前を入れて、これをforループで回しつつ、出力時に「さん」を加えています。

実行してみると……
gyazo.com

はい、できました。for文、めっちゃ便利ですね。

このfor文については、Perl入学式の講義資料より以下をご参照ください。
https://github.com/perl-entrance-org/workshop-2017/blob/master/2nd/slide.md#for文-配列

qwについては以下をどうぞ。
https://github.com/perl-entrance-org/workshop-2017/blob/master/2nd/slide.md#qw-ショートカット

if文、正規表現

さてしかし、現実の世界では、このように元のデータすべてに同じ処理をするという機会はそうそうなくて、この内の「ある条件に合致するもの」だけに処理を加えたい、ということが多いです。

たとえば、この中で名前の最後に「ン」が付く人だけ敬称を「様」にしたい、という場合にはどうすればいいでしょうか?

そのような時には、こう書きます。

#!/usr/bin/env perl
use strict;
use warnings;

my @band = qw/
ジョン・レノン
ポール・マッカートニー
ジョージ・ハリスン
リンゴ・スター
/;

for my $member (@band) {
    if ($member =~ /\z/) {
        print "$member\n";
    }
    else {
        print "$memberさん\n";
    }
}

実行。
gyazo.com

はい、できました。

このif文と、それに付いてくる正規表現(「=~」とか「//」を使って色々やっているもの)については、同じくPerl入学式の講義資料から以下をご参照ください。

if文
https://github.com/perl-entrance-org/workshop-2017/blob/master/2nd/slide.md#if-else文

正規表現
https://github.com/perl-entrance-org/workshop-2017/blob/master/4th/slide.md#正規表現

__DATA__

さてここで、一旦冒頭の話に戻りますが、普段の仕事(プログラミングとは関係ないそれ)をしていて、時々欲しくなるのは、

テキストファイルAの内容に何らかの処理を加えて、テキストファイルBとして保存する

という能力です。

上の例でいうと、テキストファイルAにあたるのは

ジョン・レノン
ポール・マッカートニー
ジョージ・ハリスン
リンゴ・スター

で、処理後のテキストファイルBにあたるのは

ジョン・レノン
ポール・マッカートニーさん
ジョージ・ハリスン
リンゴ・スターさん

になります。

ただ、さすがにこんな風に、処理したいデータ(ここではその4名)を毎回コードの中に埋め込むというのは面倒というか、できればコードはコード、素材データは素材データとして分けて扱いたいので、上記に加えてぼくが多用するのが「__DATA__」という記法*2です。

さっそく、それを使って書き換えてみましょう。

#!/usr/bin/env perl
use strict;
use warnings;

my @band = <DATA>;

for my $member (@band) {
    chomp $member;
    if ($member =~ /\z/) {
        print "$member\n";
    }
    else {
        print "$memberさん\n";
    }
}

__DATA__
ジョン・レノン
ポール・マッカートニー
ジョージ・ハリスン
リンゴ・スター

はい。だいぶスッキリしました。
結果は先ほどと同じですが、一応実行してみましょう。
gyazo.com

いいですね。
こうすることによって、対象のデータがいくら変わっても、それは「__DATA__」より下の部分で書き換えればよくなり、それより上のコード部分を触る必要がなくなります。

また、この方法だと1本のスクリプトファイルで完結するので、コードもデータもそれぞれ手軽に書き換えられて便利です。

じつのところ、ぼくが普段の仕事に関連して書くコードの大半は、これのバリエーションです。

たとえば、少し前に、別のアドベントカレンダーでこんな記事を書いたのですが、
note103.hateblo.jp

この後半で紹介している、本の脚注データを分析するコードにしても、途中でハッシュを使ったりはしていますが、基本形は上記と同じであることがわかるかと思います。

実際には、もう少し踏み込んだことをしようとすると、コードとは別に置かれた素材ファイルを読み込んだり、結果を別のファイルに書き出したり、あるいは任意のディレクトリにあるファイルを読み込んだりもしたくなってくるのですが、まずは上記のようなことができるだけでも、かなりいろんな作業が効率化するので、入門者の方は試しにこれらのセット(for + if + 正規表現 + __DATA__)を自分の環境で動かしてみてはいかがでしょうか?

ちなみに、環境構築を含む第1回からの講義資料は、以下にまとまっています。
講義資料 - Perl入学式 | Perl Entrance

ということで、本日の記事は、以上です。

明日の……ではなくて、本日のご担当はgomaaaさんです!

Perl入学式のアドベントカレンダー、ひき続き、お楽しみに。
qiita.com

*1:ぼく自身は本の編集をしています。より詳しくはこちらをどうぞ。→ note103 - note103 - Scrapbox

*2:『初めてのPerl(第6版)』によると、厳密には「ファイルハンドル」と呼ばれるようです。ファイルハンドルとは、Perlの処理世界と外部世界(おそらく素材のテキストデータなど)を結びつけるコネクションの名前、とのこと。詳しくは同書をどうぞ。

初めてのPerl 第6版

初めてのPerl 第6版

編集とプログラミングと私

こんにちは。こちらは「編集とライティングにまつわるアレコレ」アドベントカレンダーの2日目の記事です。

adventar.org

昨日は本企画の発案&主宰の id:mohri さんによる以下の記事でした。

mohritaroh.hateblo.jp

GitHub Desktop、なかなか良さそうですね。

こういったGUIツールは何となく使い方を覚えるまでが面倒。という先入観があって触ってなかったですが、思っていたより直感的に使えそうなので一度試してみようかな〜と思いました。

あと、GitHubありきではなくてローカルファイルの版管理ツールとして使ってみるという視点も新鮮でした。

私にとっての「編集」

さて、今回の大テーマは「編集とライティング」ということですが、ライティングはともかく「編集」というのはけっこう大ざっぱというか、幅の広い概念ですよね。

たとえばぼく自身が抱く印象としては、「編集」と言ったら「翻訳」に近いというか、左っかわのテーブルに置いてある「素材」を目の前で何やらこねくり返して、右っかわのテーブルに「仕上がり」としてポンと置いていく、変換していく、みたいな作業を思い浮かべます。

これは文字校正とか、文章全体の構成を組み換えるとか、あるいは文字起こしなんかも近いでしょうか。
(文字起こしは音から文字への変換なので)

しかし一方で、メディアの編集といったら企画とか営業みたいな側面もありえるでしょうし、取材なんていうのもけっこう独自のメソッドというか、世界観がありそうです。

ぼくの場合、対談や座談会をテキスト化するために現場に立ち会う、みたいなことは時々あるものの、やはりどちらかというと前者の翻訳的なこと、仕事場にこもってモノが仕上がるまで出てこない。みたいな作業が多くて、後者の、いわばゼロからイチを立ち上げていくような作業にはあまり馴染みがないので、ここでは馴染みのある前者の方を念頭に置いて書いていきます。

(ここまで前置きでした)

この記事のテーマは……

さて、今回のアドベントカレンダー、当初はこんなに速攻で埋まるとは思っていなくて、きっと一人で何個か書くだろうと勝手に思っていたので、だったら最初はVimの小ネタをサクッと書いて、あとは空き状況によって書けるものを書くか〜、とか思っていたのでしたが、あっという間に全席埋まりましたね。 😅

よって方針を変えまして、この記事ではVimを中心としつつもプログラミング全般みたいなことを視野に入れ、最終的には上の方で書いた、ぼくにとっての編集作業みたいなことをぼんやり醸していけたらいいかと思っております。

これに際して、じつは最初に「編集(やライティング)のアドベントカレンダー」と聞いて、んー、まあネタとしてはこの辺か? と思ったのがあるので、とりあえず見出しだけ列記してみますと、こんな感じで。

1. 便利なショートカットあれこれ
2. 便利なExcel関数あれこれ
3. 私のメール術
4. Vimでどんなことをしてるのか
5. シェル(bash)でどんなことをしてるのか
6. Perlでどんなことをしてるのか

まあ、これで6日は消費できるかな、とか思っていましたが、上記のとおりもうそんな席は残っていませんし、かといってこれを全部1本の記事に詰め込んでいたら書くのも読むのも大変なことになりますから、今回の記事ではその中から4番目のVimと6番目のPerlについて、要点をつまみつつ書いてみます。

ちなみに、そんな経緯でこの後は、しばらくプログラミングにまつわる話が続きますが、実際には上にもあるように「私なりのメールの書き方」とかもネタに入れていたぐらいなので、今後に続けて書かれる方におきましてはこういう技術ネタに縛られず、好きなことを書いてほしいと思っています。

(まだ前置きでした)

Vimでサクッと書き換える

さて、もう何度も書いてるのに今さらですが、こちらをお読みの皆様は「Vim(ビム)」ってご存じでしょうか?

いや、ぼくも他人に語れるほど知ってるわけではないですが、えーと入門的には、やはりドットインストールあたりが親切でしょうか。

https://dotinstall.com/lessons/basic_vim

ちなみにぼく自身がVimに触れはじめたのは、以下の記事の頃でした。2013年。

ここ数日のvim - 103

ようはテキストエディタでありまして、ぼくで言ったらVimの前にはCotEditorというのを使っていましたが、

coteditor.com

まあMacだったらテキストエディットというのもデフォルトでありますし、そういうメモ帳的なものですね。

ただ、このVimというのは単なるメモ帳にはまったくとどまらず、文字を打ち込む以上のことがあれこれできてしまいます。

具体的には、たとえば上に書いたこれ、今回のためのネタ帳ですが。(再掲)

1. 便利なショートカットあれこれ
2. 便利なExcel関数あれこれ
3. 私のメール術
4. Vimでどんなことをしてるのか
5. シェル(bash)でどんなことをしてるのか
6. Perlでどんなことをしてるのか

この行頭のナンバリングに続く「ドット」の部分を、「閉じ丸括弧」に変えたい! というときにどうするか。

まあ、このぐらいの行数だったら手動でポチポチ書き換えてもいいわけですが、これが100行も200行もあったらどうでしょうか。けっこうキビシイですね。

もちろん(というか)、上記のCotEditorをはじめ、エディタに付属している検索&置換機能を使う手もあるわけですが、とりあえずVimだったらこうします。

gyazo.com

はい。その変えたいところ(ドット)を直接さわるようにして、一列分フイッと変えてしまいました。

べりー、便利!

これがもし、検索&置換用のウィンドウにわざわざ入れていくと、こんな感じになると思うのですが。(Googleドキュメントを使ってみます)

gyazo.com

たしかに、これもわかりやすいんですが、でも「今ある状況」と「求める結果」との間にその置換用ウィンドウが挟まってしまって、ちょっと直感的ではありません。

しかし、Vimならば上のとおり、あたかも直接自分の手で対象をさわっているかのように、書き換えていくことができるのです!

(……)

大丈夫でしょうか。大丈夫ですね。

Vimで知りたい情報をカウントする

さらには、Vimを使えばもっと面倒な作業だって簡単にできてしまいます。

例として、こんな素材を持ち出してきましたが。

f:id:note103:20171201023132p:plain

これはぼくが今メインでやっている仕事の「commmons: schola(コモンズ・スコラ)」という、坂本龍一さんがかれこれ10年近く続けているライフワーク的音楽全集で使っている脚注のデータですが。

commmons scholaについて | commmons:schola(コモンズスコラ)-坂本龍一監修による音楽の百科事典- | commmons

このシリーズ、CDブックの形態ですでに第16巻まで刊行していまして、いま第17巻を絶賛制作中なのですが(ピークです)、それゆえ脚注も16巻分のデータが溜まっていまして、上記はそれをスプレッドシートで管理している画面の一部です。

で、その左端の方、A列に数字が並んでいますが、それが巻のナンバーで、B列にある見出し語句が第何巻に掲載されたか、ということをA列から読み取れるようになっています。*1

では、この全データ(2008行ありました)のうち、第6巻の脚注は何個あったんだろう? というときにどうするか。

まあ、そのスプレッドシートの機能をちょこちょこ使えばカウントできるかもしれないですが(知らないのですが)、Vimを使うなら、こうします。

1. まず内容をVimに全コピペして
2. コマンドラインモード(というのがあります)で以下を打ち込む

:%s/\v^6\t//gn

と、このように出てきます。(一番下の方を見ておいてください)

gyazo.com

はい。104個ありました。

Perlで知りたい情報をカウントする

しかしそうなると、6巻だけじゃなくて、他の巻の注釈個数も全部一気に知りたい、という欲も出てきます。

果たして、そういう場合にはどうすればいいでしょうか?

前述のとおり、このシリーズはすでに16巻出ていますから、同じことを地道に16巻分(厳密にはあと15回)繰り返すべきでしょうか?

もちろん、そうするのも自由ですが、思い出してください。我々にはPerlがあるのでした!

(……)

はい。結論から言いますと、じつはつい数日前に、実際にその情報を知りたくてそのためのコードを書いたからここでネタにしているのですが、そのときにはこういうものを書きました。

#!/usr/bin/env perl
use strict;
use warnings;

my @array = <DATA>;

my $num;
my %count;

for (@array) {
    chomp $_;
    if ($_ =~ /\A(\d+)\t/) {
        $num = $1;
        if ($num == 16) { $count{$num}++; }
        elsif ($num == 15) { $count{$num}++; }
        elsif ($num == 14) { $count{$num}++; }
        elsif ($num == 13) { $count{$num}++; }
        elsif ($num == 12) { $count{$num}++; }
        elsif ($num == 11) { $count{$num}++; }
        elsif ($num == 10) { $count{$num}++; }
        elsif ($num == 9) { $count{$num}++; }
        elsif ($num == 8) { $count{$num}++; }
        elsif ($num == 7) { $count{$num}++; }
        elsif ($num == 6) { $count{$num}++; }
        elsif ($num == 5) { $count{$num}++; }
        elsif ($num == 4) { $count{$num}++; }
        elsif ($num == 3) { $count{$num}++; }
        elsif ($num == 2) { $count{$num}++; }
        elsif ($num == 1) { $count{$num}++; }
    }
}

use DDP {deparse => 1};
p %count;

__DATA__

冗長!

……ともあれ、詳細は割愛しますが、この最後の「__DATA__」と書かれた下に件のリストをまた全コピペして、実行するとこんな風に出てきます。
(画面の下半分を見ておいてください)

gyazo.com

ヒュー! 一発で出てきましたね。
6巻もちゃんと104個になっています。

(8巻多すぎ……)

まとめ

いかがでしょうか?

本記事はVimPerlの入門を目的としたものではなく、いち編集者であるぼくが、普段どんなふうに自分なりの作業をしているのか、ということを紹介するものだったので、いろいろな細かい手順や前提を驚異的なまでに省略してしまいましたが、こうしてプログラミングを絡めると、日々の地味〜な作業も飛躍的に効率化され、なおかつ目覚ましいほどのクリエイティヴィティを味わえる! みたいなノリが少しでも伝わりましたら幸いです。

ちなみに、ぼくがVimにのめり込むきっかけになった話や、Perlをはじめとするプログラミング入門についてなどは以下にもちらちら書いていますので、ご興味のある方はぜひどうぞ。

ということで、本日の記事は以上です。

最後までお読み頂き、ありがとうございました!

明日のアドベントカレンダーのご担当は id:minesweeper96 さんです。
お楽しみに!!

adventar.org

*1:同じ語句でも巻によって内容が違うので、「エディ・コクラン」なんかは複数記載されています。

基本情報技術者試験にうかった

f:id:note103:20171115124810p:plain

超ぎりぎり(笑)。*1

これまでの経緯。

基本情報技術者試験を受けてきた - the code to rock
基本情報技術者試験の結果 - the code to rock
基本情報技術者試験を受けてきた(2) - the code to rock

今年の春に「あと0.5点」というところで落ちた後、秋の受験(10/15)に向けて7月から学習を再開し、それから試験当日までの3ヶ月ほど、仕事に並行して勉強を続けていた。

勉強している間は、「前よりわかるようになってるな〜」などと一瞬浮かれた気分になっても、すぐに「でも落ちるかもしれない・・」と冷水をかける自分もいて、気が休まることはなかった。

どれだけ普段うまく行っていても、本番で駄目ならまた受験しなければならないわけで、「これだけの努力をまた続けられるだろうか?」とか、「すでに2回めじゃないか。もし落ちたら、3回めもできるか?(いや、できない)」などという、うんざりするような気持ちに何度も襲われた。

だから、「うかったらどれだけの解放感を味わえるだろう!」と期待していたのだけど、ん〜、案外そんなに、解放感はない。
嬉しさも、思ったほどではない。むしろなんというか、真顔というか、無常というか・・。

といっても、べつに燃え尽きたとかいうのでもなくて、単になんというか、「まだまだ先は長いな〜・・」という感じ。

世の中には、もっと大変なことや取り組むべきことがたくさんあって、自分はまだその入り口にも達してないんじゃないか、という。

これをクリアしたらどれだけ「上」に行けるかと思っていたけど、いやいや、まだ全然ふもとですから、と言われているかのような。

でもまあ、そのように思えるというのは、やっぱりどちらかと言ったら、幸せなことなんだろう。

この脱力感、このドヨーンとした感じ。それも見方によっては、一種の解放感と表現できるのかもしれない。

では唐突に、謝辞の時間。

今回の試験勉強では、以下の方々に大変お世話になった。

  • 福嶋宏訓さん
  • 大滝みや子さん
  • 瀬戸美月さん
  • 岡嶋裕史さん
  • 滝口直樹さん
  • インプレス/ノマド・ワークスさん

いずれも今回使用した参考書の著者さんである。

最初に挙げた福嶋さんは、もう何年も前から情報技術に関する参考書を書かれているようだが、そのかたわら、ご自身のWebサイトで動画講義(スクリーンキャスト的な)を無料で公開していて、今回はそれを猛烈に見た。

Webサイトには読者限定の部屋などもあり、その詳細については以下の本に載っているので、興味のある人は見てみると良いと思う。

※ぼくが買ったのは2017年版だが、そちらに記載されているサイト情報(限定部屋へのパスワードなど)はそろそろ期限が切れるようだから、この2018年版という方を紹介しておく。

うかる!  基本情報技術者 [午前編] 2018年版 (福嶋先生の集中ゼミ)

うかる! 基本情報技術者 [午前編] 2018年版 (福嶋先生の集中ゼミ)

  • 作者:福嶋 宏訓
  • 出版社/メーカー: 日本経済新聞出版社
  • 発売日: 2017/11/16
  • メディア: 単行本(ソフトカバー)
うかる!  基本情報技術者 [午後・アルゴリズム編] 2018年版 (福嶋先生の集中ゼミ)

うかる! 基本情報技術者 [午後・アルゴリズム編] 2018年版 (福嶋先生の集中ゼミ)

  • 作者:福嶋 宏訓
  • 出版社/メーカー: 日本経済新聞出版社
  • 発売日: 2017/11/16
  • メディア: 単行本(ソフトカバー)

※追記(2017/12/05): この記事を書いた少し後、2017年12月からWebサイトがリニューアルされていたので、そちらへのリンクを以下に貼っておく。本を持っていない人でも閲覧できる動画がけっこうたくさんあるはず。エンジョイ!(追記ここまで)
xn--tpto73d.jp

元はと言えば、ぼくはとにかく午後試験第8問のアルゴリズム問題が恐ろしく苦手で、そもそも何を聞かれているのかもわからない、という状況が長く続いたのだけど、なんとかそれを克服したいと思って片っ端からその手の参考書を買った中に福嶋さんの本があった。

福嶋さんの教え方の特徴というのは、生徒(読者)に媚びないというか、対等に接しているようなところで、読者に耳当たりのいい(甘い)ことだけを言う、みたいなところがまったくなく、たとえば「問題を解けないのは勉強が足りないから」とか身もふたもないことをバッサリ言い切る。*2

たしか具体的には、「よく『午前はできるのに午後は時間が足りなくて落ちた』なんて言う人がいるんだけど、それは練習が足りないだけなんだよね」みたいな。これはまったく図星だったし、すごく響いた。

ただぼくの場合は、それを聞いて落ち込むとか、憤りを覚えるとかいうことでもなく、「あ、そっか。時間が足りないだけなんだ。じゃあ、時間をかけて何度も問題練習すればできるようになるかも?」とそのまま受け止めて、実際そのように臨んだ結果、なんと春の試験では7問中2問しか解けなかったアルゴリズム問題(つまり正解率3割未満。しかも当てずっぽう)が、この秋試験では8割取れていた。

まあ、それはちょっと出来すぎとしても、その「なんだ、時間をかければいいだけか」と思わされたのはとても大きなことだった。

2番めに挙げた大滝みや子さんについて、上記の通り、今回は何と言ってもアルゴリズム問題がすべてを左右すると思っていたものの、とにかく何回やっても問題の意味すらわからん、何を聞いているのだ? 問題が悪いだろ! みたいなヒドイ状態だったのだけど、上述のいろいろ買いまくった参考書の中に大滝さんの以下の本もあって、おそらくぼくがアルゴリズム問題の深い蟻地獄的暗闇から抜け出すきっかけになったのは、この本だったと思っている。

基本情報技術者 大滝みや子先生のかんたんアルゴリズム解法 ~流れ図と擬似言語~  第3版

基本情報技術者 大滝みや子先生のかんたんアルゴリズム解法 ~流れ図と擬似言語~ 第3版

  • 作者:大滝 みや子
  • 出版社/メーカー: リックテレコム
  • 発売日: 2015/01/28
  • メディア: 単行本

同書は前半で流れ図、後半で擬似言語(試験に出てくるやつ)の解説という構成になっていて、ページを追うごとに徐々に難しくなっていくのだけど、どれをとっても解説が丁寧で、「あ、これはわかるな」「これもわかるわ」「なかなかわからなくならないな〜」「これもわかる・・」とかやっているうちに最後まで行ってしまった。

これによって、それまでの「俺イコール擬似言語苦手」という思い込みはいつの間にか払拭され、「俺イコール少なくともこの本に書かれているような擬似言語ならわかる」という状態になり、なんというか、「絶対的な苦手意識」が「限定的な苦手意識」に変わった。

この大滝さんの教え方のうまさというか、説明のあり方には目からウロコの感じがあり、そのまま以下の2冊も購入して読んだけど、

基本情報技術者試験 アルゴリズムと表計算

基本情報技術者試験 アルゴリズムと表計算

  • 作者:月江 伸弘
  • 出版社/メーカー: 実教出版
  • 発売日: 2011/10/01
  • メディア: 単行本
情報処理教科書 基本情報技術者 スピードアンサー338

情報処理教科書 基本情報技術者 スピードアンサー338

  • 作者:大滝 みや子
  • 出版社/メーカー: 翔泳社
  • 発売日: 2012/08/24
  • メディア: 単行本(ソフトカバー)

いずれも良書で、表計算の本も非常に役立ったし(これはむしろ、試験後の自分の業務に生きているが)、もう一方の「スピードアンサー」も能率的な本で、これは紙と電子版の両方を買ったのだけど、おそらく今後も折りに触れ読み返すだろう。

教えるのが上手い先生に出会えるというのは、それだけで幸運なことである。
教えるのが上手い先生というのは、単に試験の得点を上げてくれるというだけではなく、勉強することの面白さを教えてくれる。

勉強するのが面白かったり嬉しくなったりすると、あとは言われなくても自分でどんどんやるようになるわけで、上記の福嶋さんのコメントにも繋がるが、自分でどんどんやったら必然的に勉強量も多くなる。

瀬戸さん、岡嶋さん、滝口さんの本は前回の春試験から触れていたもの。

瀬戸さんは以下の本をフライング的に買ったら、

(全文PDF・単語帳アプリ付) 徹底攻略 応用情報技術者教科書 平成30年度

(全文PDF・単語帳アプリ付) 徹底攻略 応用情報技術者教科書 平成30年度

(これも実際に買ったのは平成29年度版だが、一応最新版を載せておく)

ご自身のYouTubeチャンネルで公開している動画が紹介されていて、そこではソート系をはじめとする基礎的なアルゴリズムなど、基本情報にも大いに関わる定番トピックがわかりやすく解説されていたので大変役立った。

滝口さんは昨年末にITパスポートの受験を志した際に以下を買ったのだけど、

それに付録でついていた音声講義が良かったので、今回は以下を買ったところ、それにも付録の音声講義が付いていて、とくに春受験の勉強ではよく聴いた。

ゼロからはじめる基本情報技術者の教科書

ゼロからはじめる基本情報技術者の教科書

  • 作者:滝口直樹
  • 出版社/メーカー: とりい書房
  • 発売日: 2014/11/26
  • メディア: 単行本

岡嶋さんの本は以下でも紹介したが、

基本情報技術者試験を受けてきた - the code to rock

「教科書でこんなの、アリなのか・・?」と思うほどハイコンテクストというか、オタク的とも言えるディープなウケ狙いが散発していて、やはり岡嶋さんの本に出会えたのも幸運なことだった。(これも紙と電子版の両方買った)

平成29年度 ITパスポート合格教本 (情報処理技術者試験)

平成29年度 ITパスポート合格教本 (情報処理技術者試験)

  • 作者:岡嶋 裕史
  • 出版社/メーカー: 技術評論社
  • 発売日: 2016/12/01
  • メディア: 単行本(ソフトカバー)

ちなみに、岡嶋さんのその本は、見てもわかる通り基本情報用ではなくITパスポート用なのだけど、基本情報の午前試験用としても充分使える内容だと思う。

上記の通り、今回はこれまで以上にいろいろな参考書を買って読んだけど、じつは書籍として一番役に立った&繰り返し読んだのは、インプレスが出している過去問集だったと思う。

それも、一応試験前になって最新の29年度秋版も買ったのだけど、実際に頻用したのは春試験のために買った28年度秋版だった。

この過去問集は大判なのだけど、紙が軽いのでどこへ移動するにもよく持ち歩いた。

古い方の過去問集には書き込みやマーキングも多くなっていて、どの問題をどう間違えたのかとか、どのキーワードを忘れやすいか、とかがわかりやすかったから、それはまるでお守りのようで、実際、試験会場に持っていった参考書はこれと上記の「スピードアンサー」の2冊だけだった。

同書は非常に行き届いた内容で、版元のインプレスさんと制作のノマド・ワークスさんには心からの感謝を伝えたい。

もうひとつ、これは今回の試験に直接関わることではないのだけど、このブログではお馴染みの無料プログラミング講座「Perl入学式」の名前も挙げておきたい。

www.perl-entrance.org

やはり2013年、38才の夏に、「プログラミング」とか「YAPC::Asia」といった未知の大海にダイヴすることになったきっかけはPerl入学式であり、もし以下の記事をたまたま読んでいなかったら、

http://yapcasia.org/2013/08/perl-entrance-meets-yapc-asia.htmlyapcasia.org

そしてその時、その話を真に受けて飛び込んでいなかったら、今回こんな試験を受けることもなかっただろう。
今までそんなことは考えもしなかったが、これを書きながら、ふとそのことに気がついた。

校長の @papix さん、そしていつも多岐にわたりサポートしてくださる @xtetsuji さん、その他各地域の皆さんに深く感謝します。

あ、そうそう、上では「今回の試験に直接関わることではない」と書いたけど、ひとつだけ直接関係することがあった。

というのも、今回の午前試験ではこんな問題が出たのだった。

第8問 Perlの実行に関する記述のうち、適切なものはどれか。
 

  • ア UNIX用として開発されており、Windows用の言語処理系はない。
  • イ 実行にWebサーバを必要とする言語であり、CGIの開発に適している。
  • ウ 動的デバッグは、言語処理系から独立したプログラムを実行して行う。
  • エ プログラムをコンパイルしたファイルを事前に用意する必要はない。

 
基本情報技術者試験平成29年度秋期・過去問題より)

おお〜、Perl出てんじゃん! と盛り上がって、一瞬集中が切れてしまった。
(正解はこの注釈→ *3

最後に、これもある意味謝辞ではあるが、以下の動画を紹介して終わりたい。

AlgoRythmics - YouTube

この一連の動画では、ソートのアルゴリズムを素敵なダンスと音楽で表現している。

何をきっかけに知ったのか、すっかり忘れてしまったが、これを見て、ぼくはソートのアルゴリズムを義務的なものとしてではなく、楽しみとして勉強できるようになった。

その辺の厚紙を小さく切ってカードを作り、動画を見ながらそのカードを何度も並べ替え、何度もリプレイし、また何度も並べ替えた。

基本ソート&応用ソートはほぼ網羅されているはずだが、ここでは最初に大きなインパクトを受けたクイックソートを貼っておく。

www.youtube.com

どれも踊りが超かわいい。音楽もいい。センスを感じる。

まあでも、ひとまず終わってよかった。喜びや解放感がないとは書いたが、多少の安心は得た。ほっとした。
プラスの感じではないけれど、マイナスがゼロに近づいた感覚というか。

これからどうするか、少しは考えてもいるが、登るべき山も飛び込むべき海も多すぎて、はっきりした見通しは何もない。
しかしその見通しのなさは、必ずしも不快なものではない。

*1:合格基準点は午前・午後ともに60点なので。前回は59.50点という逆のぎりぎりで涙をのんだ。

*2:念のために書いておくと、とくに失礼な印象はない。比較的若い世代の読者を想定しているとは思うが、むしろ相手を一人前の人間として扱っているからこそ、そういうオープンな雰囲気が醸されているのだと感じる。

*3:エ。もちろん当たっていた。

基本情報技術者試験を受けてきた(2)

  • このところはあまりに忙しく、あっという間に1週間以上経ってしまったが、去る10/15(日)に掲題の試験を受けてきた。
  • 今回は二度目の受験で、前回については以下に書いた。
  • 技術系の人ならご存じだろうが、「基本情報技術者試験」というのは午前試験と午後試験から成っていて、午前は短文の設問に四肢選択で回答する80問、午後は長文の設問を計7問解く、というもの。
  • このうち、午前の方はまあ試験内一次試験のようなもので、午後に比べたら簡単。多くは基礎的な問題で、なおかつ過去問の流用も多い。
    • ちなみに、この過去問流用については「手抜き」的なことを言われがちなのだけど、「流用は駄目だから」というだけの理由でほとんど同内容の新たな問題を再生産するよりも、優れた(流用可能と判断された)過去問を繰り返し使った方が全方位的に有用だと思えるし、受験者にとっても真面目に過去問に取り組んだ分だけ報われるというのは普通に学習意欲を刺激されるし、実際学習の効果も上がると思えるので何も問題ないだろう。
  • 一方、午後問題でそのような流用はない。そしてもちろん(というか)難度も午前よりは上がる。
  • とくにつらいのは、やはり問題文が長いことで、他人の長文を読むというのはこんなにつらいことか、と何度も思わされた。

  • さて、前回4月の試験では、午前の方は順当に(というか想像以上にあっさり)クリアできて、合格基準点の60点に対して、77.5点取れていた。
  • ぼくはその前段階と言える「ITパスポート試験」の勉強を、昨年末からほとんど思いつきで始めて、翌年(つまり今年の)1月末にパスしたものの、この「基本情報」の勉強はそれからしばらく経った3月半ばからようやく再開できたので、その後の実質1ヶ月程度の勉強期間にしては(それもまったく異なる分野で仕事をしている人間としては)、充分と言える成果のように思える。
    • ただし、後述のようにこれは選択式のマークシート問題だからこそ、とも思っているが。
  • 一方、同じ回の午後試験の方では、同じ60点という基準点に対し、なんと59.5点という、これ以上なく惜しいところで落ちてしまった。
  • じつは自己採点をしたときには、午後問題では配点が不明な箇所も少なくないため、やや厳し目に計算して「53点ぐらいだな」と結論し、早々に諦めていたのだが、実際の結果を見たときには、さすがにあまりの惜しさに「思い出し悔し」をしたものだった(造語)。
  • 配点についてもう少し言うと、午前のそれは単純明快で、100点満点を80問で割れば1問1.25点とすぐわかるし、実際そのように明示されている。
    • 加えて、基本情報技術者試験というのは問題用紙を持ち帰れるので、その問題にきちんとメモしておけば、後からでも自分がどう回答したのか確実にわかるし、それにより午前問題の自己採点というのはかなり正確に算出できる。
      • ただし、問題用紙のメモのとおりに回答用紙にマークしたかどうかまではわからないし、実際そういうマークミスは往々にして起こるのだけど。
    • さらには、試験を主催するIPAのサイトでは、問題はもとより解答も試験後すぐに公開してくれるので、非常に自己採点をしやすい環境になっている。
    • この点、以前に受験した日商簿記の試験では、問題を持ち帰ることができず、持ち帰れるものと言えばせいぜい計算に使うA4紙1枚だけで、さらには模範解答や配点なども一切公開されないため、ただひたすら結果発表を待つだけでつらいものだった。
      • まあ、その計算用紙に回答内容をメモしておけば、予備校などが公開してくれる予測解答と突き合わせることで、ある程度の結果はわかるのだけど、試験中にわざわざ計算用紙に転記する暇があれば問題を見直したい、という程度の実力だった自分にはやはり苦しいシステムだった。
  • 配点の話に戻ると、基本情報技術者試験の午前の方はそのように非常にわかりやすいのだけど、午後の方にはやや謎があって、7つの大問のうち、5つが12点満点で2つが20点満点ということまではわかっているものの、その中の枝問が1問につき何点か、というところまでは公開されていない。
  • だから、自己採点をする際にはその部分を想像で補う必要があり、前回の自己採点ではそれが実際とズレすぎて「53点」とかになっていたのだった。
    • 具体的には、それぞれの枝門に仮の配点をつけて、それを加減していたのだけど、たぶんここまでズレるということは、その「仮の配点」が信用ならないものだったのだろう。
  • しかし今回、その勝手配点の方法を少し変えて、単に各大問の正答率(問題数中のマルの数の割合)に大問の配点(12点または20点)をかける方式にしてみたところ、前回の午後問題の予想得点は「58.008点」になっていて、だいぶ精度が上がっている。
  • で、その方法で今回の予想得点を算出してみると、「63.316点」だった。
    • ……うかってるかも?
  • まあ、あまりに微妙なラインすぎて、まだまったく喜べないのだけど、とはいえ、今回の目標というのは「前回の結果を1点でも上回る」という、月並みかもしれないが「過去の自分に勝つ」ということだったから、その意味においては一応目的を達成できたのではないか、と思っている。
    • とはいえ、なにしろ前回が「あと0.5点」だったのだから、たとえ1点でも自分に勝てば、その段階でイコール「合格」になるわけだけど。
  • ちなみに、午前の方は自己採点で80点だった(80問中64問正解)。もう少し行ってるかと思ったが、そうでもなかった。

  • しかしあらためて、前回はたったひと月余りの勉強期間でそこまで行ったの、できすぎだった。
  • これはどう考えても、マークシートという選択問題ゆえの高得点であり、まぐれ当たりだったのだと思わざるを得ない。
  • 何しろ、選択肢のどれかを選べば、少なくとも正解率はゼロではなくなるのだから。
    • 午前問題に至っては、最低でも25%が保証されている。
  • 加えて、選択肢の中には、正答がわからなかったとしても「これは明らかに違うだろう〜」と思えるものが少なからず混ざっているものなので、その辺が見えれば当たる確率はさらに高まる。
  • ようは、設問に正面から回答できなかったとしても、そういう安いテクニックというか、チート力である程度の得点は重ねられてしまうところがあり、それが良くも悪くも前回は味方してくれたのだろう、ということ。
  • と同時にしかし、それに比べると、今回はそういった「まぐれ当たり」の要素は少なかったようにも感じている。
  • 午前が思ったより伸びなかったのもそうだけど、午後問題の多くも自分で「コレ」と思ったものが当たっており、「勘だけど……とりあえずコレか?」とか思ったのは普通に外れていたりする。
  • それはそれで爽快というか、もしこれで前回以上の得点になっているなら、満足感は得点以上のものになるだろう。

**

  • さて、じつはここで、今回の受験に備えて使用した参考書類を紹介しようと思っていたのだけど、考えてみたらまだ正式な結果は出ていないわけで、そういう謝辞みたいなことは結果が出てからの方が良いだろうから、またそのときに。
  • その代わりというわけでもないが、前回の試験を終えてから今回の受験までにつらつらと考えた「勉強」に関するメモを少し書いておく。
  • まず、日々の仕事に並行して、過去問なり教科書の例題なりを解きながら、何度もつくづく思ったのは、結局のところ勉強というのは、スポーツや武道とまったく同じで、体を使った技術を高めていく過程なのだな、ということだった。
  • これを言い換えると、勉強というのは、目的を達成するために必要な「反射神経」を磨くことだとも言えるだろう。
  • つい先日、日本ではボクシングの試合をやっていたけど、あれもまさに似たようなもので、素人だったら認識すらできないようなすさまじいスピードの相手のパンチを、プロの人たちは避けたり、ガードしたり、場合によってはそれと入れ違いに攻撃したりしている。
  • あるいはプロ野球でも、バッターは時速150キロとかいうものすごい速球を打ち返しているし、テニスでもバトミントンでもバレーボールでも、それに熟達した人は他の人には見えない打球に反応しては、対処している。
  • 試験における設問というのは、上記のパンチとか速球とかみたいなもので、最初のうちはまったく歯が立たないものだけど、何度もぶつかっては打ちのめされているうちに、だんだん相手の軌道というか、飛んでくる球筋のようなものが見えてくる。
  • そしてやがて、初めはめった打ちにされていた相手にも勝てるときがやってきて、さらにはその割合が増えてくる。
  • この問題が来たらこっち、それが来たらあっち、という具合に問題に目が慣れて、頭が働くようになっていく。というより、頭の中で意識せずとも、自動的に処理できる部分が増えてきて、その代わりに本来時間をかけて考えるべきところに頭を使えるようになっていく。
  • これはRPGゲームで自分がどんどんレベルアップしていく楽しさにも似ていて、というか、それは人生を使った自分のレベルアップそのものであって、一度味わってしまうと、こんなに面白いことは他にない。
  • ただし、それはリアルな人生を使ったものである以上、他の楽しみを諦めなければならないとか、それ相応の代償を払うものだから、こんなに面白いものが他にないとしても、こんなにやりたくないものは他にない、という気にもなる。
    • やはり結局のところ、人は、というか少なくともぼくは、面白いものや楽しいものより、ラクなものをつい取りたくなってしまうようだから。
  • しかしそれでも、そうした代償を払いながらやってみるレベルアップ・ゲームというのはかけがえのないもので、この楽しさ、つまりスポーツや武道のように、反射神経を高めながら、それまで出来なかったことがどんどん出来るようになっていく、という楽しみを持つものとして「勉強」を捉えてみると、やはりそれは大変魅力的なものであると思えてくる。

  • こうした勉強の楽しさ、面白さというものは、「やればやるほど必ず上達する」という単純さに起因しているようにも思える。
  • そのように考えてみると、自分がこれまでに様々な試験で失敗してきた理由というものが、結局のところ、勉強法が悪かったとか、性格が向いてないとか、運が悪かったとかいうことではなく、単に「勉強量が足りなかったから」というだけのことであるように思えてくる。
  • ここで思い出すのは、もう20年以上前のことになるが、美術大学の受験に2度失敗した、二浪の頃の自分である。
  • 自分で言うのもナンだけど、ぼくは現役(高校3年)の頃から「コイツは芸大に入る」と言われて、実際校内でもトップに近い結果を出していたのだけど、いざ受験となると、1年目も2年目もすべり止めにすら受からなかった。
  • 当時の自分としては、「プレッシャーに弱いから」とか、「本番に慣れていないから」とか、「そもそも受験に向いてないから」とか、いろいろな理由を思い浮かべては悩んでいたが、問題はそんなことではなかった。
  • もし今のぼくが、当時の自分に声をかけてあげられるとしたら、

受験会場ではただ一生懸命やればいい。後悔しないように、自分が一番好きな絵を描きなさい。
でも受験当日までは、とにかくできるかぎり練習しなさい。君が試験に落ちたのは、プレッシャーに弱いからではなく、実力がないからだ。
その実力をつけるために、毎日描いて描いて描きまくって、自分が好きな、描きたい絵を、限られた時間内に描ききれるように、繰り返し練習して充分な技術を身につけなさい。

  • と言いたい。
  • おそらく、当時のぼくが本当に必要としていたのは、そういう言葉だった。
  • ぼくは簡単に評価されて、すぐにいい気になって、その後は受験に必要な練習をまったくしていなかった。もちろん毎日のように予備校に通って、自分なりには一生懸命やっているつもりだったけど、その目的は大学合格ではなく、日々の単発的な評価を得ることにあった。
  • その頃のぼくの絵には、たしかに「おっ」と思わせる何かがあったかもしれないが、そんなものは少し視野を広げれば、そこいらじゅうにあるもので、溺れるほどの才能でもなければ、努力もせず大学に合格できるだけの能力でもなかった。
  • そのときのぼくに必要だったのは、そのささやかな能力が埋もれてしまわないように、目標を大学合格に定めながら、ひたすら自分に足りないところを補ったり、あるいは際立つところを伸ばしたりし続けることだった。
  • しかし実際の自分はと言えば、ぼんやりした目標とともに、ぼんやりした絵を描き続けて、多くの絵の具と、溶き油と、貴重な時間を無駄にしてしまった。
  • そのことを今さら後悔しているわけではないけれど、ああ、あの暗く重苦しい、たしかに色にたとえるなら「灰色」としか言いようのなかったあの時期に、そのようなまともな目標を見出すことができていたら、もっと日々を楽しめたかもしれないな、とも思わずにはいられない。
  • 40才を過ぎて(というかもうすぐ43だが)、ふとこのようなことに気づけたのは、やはり幸運なことだろう。
  • かえって大変な人生にもなりそうだが、そうでない場合に比べたら、より楽しい時間を過ごせるだろう。

Perlの正規表現における「先読み/後読み」に関する私的まとめ

はじめに

Perlの基礎文法をある程度身につけて、本職のプログラマーほどではないにせよ、趣味や自分の本業を助ける程度のことができるようになってくると、そのもう一段先、ぐらいのことを知りたくなってくる。

現在のぼくにとってそれは正規表現の「先読み/後(アト)読み」を習得することで、実際にはそんなに使わない可能性も高いものの、いつまでもぼんやり把握したままなのが気持ち悪いので、ここ数ヶ月で理解したところを自分なりにまとめておきたい。

普通の正規表現

まずは土台となる、シンプルな正規表現の例を作っておく。

#!/usr/bin/env perl
use strict;
use warnings;
use feature 'say';

my $fruits = 'applebananaorange';

if ($fruits =~ /banana/) {  #<= ココが正規表現
    say 'match!';
}
else {
    say 'not match.';
}

変数 $fruits の値には「banana」が含まれているから、これは「match!」を出力する。

先読み

それではさっそく、「先読み」を試してみよう。
(以後のサンプルコードではプラグマは書かない)

my $fruits = 'applebananaorange';

if ($fruits =~ /banana(?=orange)/) {  #<= 書き換え
    say 'match!';
}
else {
    say 'not match.';
}

このように書くと、まず「banana」にマッチした上で、その右側に「orange」もあれば真になる。
よって、ここでも「match!」が出力される。

もしこのようにすると、

my $fruits = 'applebananaorange';

if ($fruits =~ /banana(?=lemon)/) { #<= orange を lemon に変える
    say 'match!';
}
else {
    say 'not match.';
}

「not match.」が出力される。

後読み(戻り読み)

次に、「後読み」を試してみる。
これは「戻り読み」と言う人もいるらしい。

まずコード例。このようにすると……

my $fruits = 'applebananaorange';

if ($fruits =~ /(?<=apple)banana/) {  #<= ココ
    say 'match!';
}
else {
    say 'not match.';
}

「match!」が出力される。

ここでやっているのは、もし「banana」がマッチしていたら、その左側をチェックして、そこに「apple」があれば真を返す、ということらしい。

だから、以下のようになっていると、

my $fruits = 'applebananaorange';

if ($fruits =~ /(?<=strawberry)banana/) { #<= apple を strawberry に変える
    say 'match!';
}
else {
    say 'not match.';
}

「not match.」が出力される。

「先」と「後」がもたらす混乱

ということで、「先読み」「後読み」がやっていることというのは、けっこうシンプルであるように思える。

では何が問題なのかと言うと、「先読み」「後読み」という名称における「先」とか「後」とかいう表現が、時間の前後を表しているのか、それとも場所(位置)の前後を表しているのか、あるいはどっちにしても右のことなのか、左のことなのか、解釈の余地が多すぎて直感的にわかりづらい、ということがよく問題になっている。

たとえば、「後読み」の「後(アト)」という言葉は「金なら後で払うからさ〜」と言うように、「今より遅い時間」のことを示すこともあれば、「後に並んでください」と言うように、場所としての「後ろ」を示すこともある。

もし場所としての「後ろ」を示すのであれば、それに対応する反対語は「先頭」になるが、ではコードにおける「先頭」とは、果たして右側のことだろうか? それとも左側のことだろうか?

ぼくだったら、任意の1行のコードを指して、「その先頭はどこですか?」と聞かれたら、一番左を「先頭」としてイメージするだろう。

以下の例で言ったら、

applebananaorange

左端の「a」が先頭であり、「e」が最後尾である。
(これは「行頭」「行末」という表現にも対応する)

しかし実際には、上記の正規表現における「先」とは右側「e」の方を指しており、「後」は左側「a」の方を指している。

なるほど、混乱している。

結論は「進行方向」における先と後

こうした事態に対し、「日本語で考えるからいけないのだ。原語(英語)で考えればよいのだ」と主張したのが以下の記事で、書かれた頃には少し話題になった。

qiita.com

じつはこの本文にはちょっと勘違いがあって(記事冒頭でその旨の追記もある)、だから本文だけを読むとかえって混乱が増すのだが、論旨としては、

  • 英語なら「先読み」は lookahead、「後読み」は lookbehind と表現されており、誤解の余地が少ない。
  • だからみんなも「先」とか「後」とか言わないで、英語で表現しようよ。

みたいなことを言っている。(と思う)

しかし、この記事でより注目すべきはコメント欄の方で、そこでは、

  • 「先(前)」であれ「後」であれ、「進行方向」を基準に考えなければ混乱は避けられない。

みたいなことが言われている。

ここで言う「進行方向」というのは、文字が伸びていく方向であり、再びこの例で言うと、

applebananaorange

最右の「e」が先頭で、最左の「a」が最後尾を指す。

よって、これらを踏まえて言えば、

  • 「先読み」とは対象とする語句(上のサンプルコードだと「banana」)から見て、文の進行方向における前(右側)を読むことであり、
  • 「後読み」とは対象語句から見て、文の進行方向における後ろ(左側)を読むことである。

と説明することができるだろう。

なお、そのリンク先の記事でもやや説明が曖昧になっているようなのだけど、これを単に「時間の観点ではなく位置関係の問題として捉えればいい」と言ってしまうと、新たな(というか未消化の)問題が生じてしまう。

なぜなら、「時間の前後ではなく、位置関係の前後なのである」と言ったところで、たとえば人が待ち行列に並ぶときの「後ろ」とは、列がどんどん伸びていく方向を指しているが、マラソンランナーがフルマラソンを走るときの「後ろ」は、ランナーがぐんぐん進んでいく方向とは逆側(スタート地点側)を指している。

だから、「時間の前後ではなく位置関係の前後なのだ」という説明ではまだ足りない。

よって、その点も考慮しつつ説明するなら、

  • 「後読み」の「後」というのは、『時間』に関する「後でやっておくよ」の「後(アト)」ではなく、
  • また『位置関係』を示す場合の「後ろ(ウシろ)」とも言い切れず、
  • 『進行方向』における「後(ウシろ)」である。

などと言う必要があるだろう。

また、その辺と繋がるイメージで、同記事に関するこちらのブックマークコメントもわかりやすかった。

[コラム] 正規表現の先読み/後読みは、どう考えても名前が悪いので、呼称禁止令を出してルックと気軽に呼んでみませんか。 - Qiita

「チラ見」と「後方確認」を推したい。

2017/06/04 23:40
b.hatena.ne.jp
(「チラ見」は先読み、「後方確認」は後読みを指すのだろう)

ということで、本題は以上だが、せっかくなのでこの機会に、上記に関連する正規表現をいくつかまとめておきたい。

否定先読み

まずは、「先読み」かつ「否定」するやつ。

これはつい最近までほとんど使っていなかったが、何かの機会に一度使ったら、けっこう便利だと感じた。

my $fruits = 'applebananaorange';

if ($fruits =~ /banana(?!lemon)/) {
    say 'match!';
}
else {
    say 'not match.';
}

この場合、「banana」の後に「lemon」が無ければ真なので、「match!」が出てくる。

否定後読み

上とほとんど同じやつ。

my $fruits = 'applebananaorange';

if ($fruits =~ /(?<!strawberry)banana/) {
    say 'match!';
}
else {
    say 'not match.';
}

これも「banana」の左に「strawberry」がなければ真なので、「match!」が出てくる。

幅を持つもの/持たないもの

さて、ここまでに挙がっていない話題として、上に挙げたパターン群には「幅を持たない」という特徴がある。

「幅」というのは、手元のPerl入門同人誌『雅なPerl入門 第3版』によると、

マッチした文字が消費する文字幅のことさ。. っていうのは、1文字だから1幅。\Aなどは、マッチしても幅は持たないから、0幅なんだ。

とのこと。
(同書は会話形式で進んでいくので、こういう話しぶりになっている)

この特徴は、以下のような例で示すことができる。

まず、通常の正規表現ならば、このような置き換えが可能だが、

my $fruits = 'applebananaorange';

$fruits =~ s/applebanana/xyz/;  #<= applebanana を xyz に置換

say $fruits; #=> xyzorange

たとえば「先読み」を使って以下のようにすると、

my $fruits = 'applebananaorange';

$fruits =~ s/apple(?=banana)/xyz/;

say $fruits; #=> xyzbananaorange

というふうに、括弧内の「banana」は置換されない。

これは上の『雅なPerl入門』からの引用にもあるように、「先読み」の記法が「\A」、つまり「アンカー」と同様に機能することを意味している。

この「アンカー」という観点から、正規表現の先読み・後読みを解説した記事としてはこちらがわかりやすかった。
abicky.net

冒頭部分だけ引用すると、こんな感じで説明されている。

この内容を理解するためには「先読み・後読みはアンカー」という考え方が必要になってきます.
アンカーとは文字列内の特定の位置を表す物であり,文字列の先頭を表す ^ や末尾を表す $ がそれにあたります.普通の正規表現では文字に対してマッチしますが,アンカーは位置に対してマッチします.

また、この特徴は「後読み」や「否定先読み」「否定後読み」にも共通する。

# 後読み
my $fruits = 'applebananaorange';

$fruits =~ s/(?<=apple)banana/xyz/;
say $fruits; #=> applexyzorange
# 否定先読み
my $fruits = 'applebananaorange';

$fruits =~ s/apple(?!lemon)/xyz/;
say $fruits; #=> xyzbananaorange
# 否定後読み
my $fruits = 'applebananaorange';

$fruits =~ s/(?<!lemon)banana/xyz/;
say $fruits; #=> applexyzorange

(?:PATTERN) との違い

以上、「幅」についても一貫した法則があるようで、煩雑ではあるものの難しくはない、という感じだが、その法則を外れているのが、ここで新たに登場する「(?:PATTERN)」という書き方である。
(「PATTERN」は説明のために便宜的に使用する仮の文字列)

この記法は上に挙げた「先読み」とか「後読み」のように使うわけではなく、通常の括弧でくくった場合とほとんど同じ働きをする。

しかしもちろん、通常の括弧とまったく同じ働きならば、そもそも存在する理由もないわけで、じゃあどこが微妙に違うのかと言ったら、通常の括弧はくくった文字列をキャプチャ(捕獲)するのだが、この記法ではキャプチャをしない。

「キャプチャ」とは、その括弧でくくった部分を後から再利用するために一時記憶することで、たとえば以下のように使う。

my $fruits = 'applebananaorange';

# 括弧でくくった apple が $1 に入る
if ($fruits =~ /(apple)/) {

    # $1を使った文が出力される
    say "I like an $1!"; #=> I like an apple!

}

一方、ここで「(?:PATTERN)」を使うと、

my $fruits = 'applebananaorange';

if ($fruits =~ /(?:apple)/) { #<= 変更
    say "I like an $1!";
}

以下のように、「$1」には何も入ってないよ! と怒られる。

Use of uninitialized value $1 in concatenation (.) or string at (略)
I like an !

ふむ、ふむ。
……えーと、でも、それがなんなのだ?! 一体これにどんなメリットがあるのだ? という感じだが、前掲の『雅なPerl入門』ではまさにその疑問と、回答が示されている。

どういう時に使うんですか? 普通の( )でいい気がします。
 
メモリ消費を減らす目的かな。マッチする文字が大きいとメモリ消費も大きくなるし。

なるほど……。(わかったような、わからんような)

さてしかし、じつはこの「キャプチャしない」という性質自体は、上記の「先読み」「後読み」にも共通している。
だから、問題はそのことではなく、先述のとおり、

「幅」についても一貫した法則があるようで、煩雑ではあるものの難しくはない、という感じだが、その法則を外れているのが「(?:PATTERN)」という記法である。

この「(?:PATTERN)」が幅を「持つ」ということが問題なのである。

見た目や、他の挙動はほとんど同じなのに、そこだけが違う。混乱する。

たとえば、「先読み」を使った置換コードはこのように動くが、

my $fruits = 'applebananaorange';

$fruits =~ s/apple(?=banana)/xyz/;

say $fruits; #=> xyzbananaorange

同じことを「(?:PATTERN)」でやると、こうなる。

my $fruits = 'applebananaorange';

$fruits =~ s/apple(?:banana)/xyz/;

say $fruits; #=> xyzorange

出力結果を並べると、前者は「xyzbananaorange」だが、後者は「xyzorange」であり、前者では括弧でくくった「banana」がそのまま残っているが、後者では「banana」も「apple」と一緒に「xyz」に置き換えられている。

言い換えると、前者(先読み)は、括弧内が幅を持たない(=アンカーである)から置き換えられないが、後者は幅を持つ(=アンカーではない)から置き換えられてしまう、ということのようである。

うーむ、まぎらわしい……。
まあ、それで困る場面がとくにない、ということなのかもしれないが。

幅・キャプチャに関するまとめ

ということで、ここまでに触れてきた「幅」「キャプチャ」について、サンプルコードを列記して簡単にまとめておこう。

my $text = "abc";

# 幅の研究
$text =~ s/a(?:b)/x/; #=> xc
$text =~ s/a(?=b)/x/; #=> xbc
$text =~ s/a(?!z)/x/; #=> xbc
$text =~ s/(?<=b)c/x/; #=> abx
$text =~ s/(?<!z)c/x/; #=> abx

# キャプチャの研究
# 以下、すべてエラー($1に値が入らないため)
$text =~ s/a(?:b)/$1/;
$text =~ s/a(?=b)/$1/;
$text =~ s/a(?!z)/$1/;
$text =~ s/(?<=b)c/$1/;
$text =~ s/(?<!z)c/$1/;

まとめ 〜「前後」がもたらす深い謎〜

以上、Perl正規表現の「先読み」「後読み」、およびそれに関連する書き方を、今把握しているかぎり書き出し&整理してみた。

正直、ここまで書いてみても尚、これらをどんな状況で便利に使えるのか、ということはよくわからない。

少なくとも自分が書く程度の規模や内容であれば、ごく基本的な正規表現だけで足りそうではある。

しかし、とくに後半でまとめたような、キャプチャや幅に関する知見については、後から思い出そうとしてもなかなか思い出せないだろうから、記憶が鮮明なうちにこうしてまとめておくことには、一定の意味もあるだろう。

今回の記事作成にあたり、その「先読み」「後読み」ならではの使いどころというか、特徴や使い方を細かくまとめているブログ記事をいくつか見たので、合わせて記録しておく。

前者の記事では、なぜ通常の正規表現ではなく「先読み」を使うのか、みたいなことが少し書いてあって、参考になった。

それから、前半で話題にしたQiita記事のコメント欄からリンクされていた以下の記事。

考えはじめると頭が割れそうなほどに哲学的というか、面倒な難問ではあるが、すこぶる面白かった。

果たして、「前」とは、「後ろ」とは、何を指しているのだろうか?

「前を向いて生きろ! 後ろを振り返るな!」と言うときの「前」は未来であり、「後ろ」は過去を指しているように思えるが、「前にこんなヒドイことがあってさあ〜」と言うときの「前」は明らかに過去である。

ゾゾ〜……。(←わからなさに震えているところ)

まあ、簡単に整理してしまえば、任意の出来事が遠い過去から現在に向かって、10年前、5年前、去年、昨日……と切れ目なく生まれては、どんどん1本の行列に並んできたのだと考えた場合、その一番「前」にいるのは最も古い過去だから、過去を「前」と呼ぶのは空間的な位置関係(たとえば商店や病院などの待ち行列)における「前後」のイメージと合致するが、そうした俯瞰的な見方ではなく、一人の人間が主体となって「俺はこれから前へ進んでいくぞ!」といった文脈における「前」は、未知の空間へ入っていくイメージを持っているから、時間の概念においても「過去」ではなく「未来」を指している。

つまりそのように、これは「前」である(あるいは「後ろ」である)、と判断する主体がいる場所によって、「前後」の意味が変わるのだ、と言うことはできるかもしれないが。

しかしいずれにせよ、こうした言葉(というか訳語というか)を考えるというのは、なんとも重い責任を持つことであり、なかなか大変そうである。

とくに、この「先/前/後」のような両義的な言葉を使うのは、非常に高難度な所業であるように思える。

実際のコーディングにおいては、勉強や経験を積むほどに、「そんなのどっちでもいい。というか、どっちでも問題ない」みたいな感じになるのかもしれないが、学習の途上にある人や、まだ経験の少ない人にとっては、ちょっとした障害になることも確かだろう。

上にも書いたが、ひとまずはこれが未来の(前の?後の?)自分の役に立てば幸いである。

関連資料

Perl入学式でいつもお世話になっています @xtetsuji さんから、同件に関してご自身がどう理解しているか、手書きのメモを書いてシェアしてもらいました!
これ、むっちゃ面白い&わかりやすい。

上に書いたような基礎をある程度理解してからの方がよりわかりやすいかもしれないけど、とりあえず今まで見たどんな解説より「あ、そうなんだ」という感じになりました。

@xtetsuji さん、ありがとうございます!

バイリンガルニュースとGoogle音声入力による文字起こしでリスニング練習

最近、バイリンガルニュースというポッドキャストをよく聴いている。
bilingualnews.libsyn.com

ホスト/ホステスのマイケルとマミさんが、英語と日本語を混ぜこぜにして、世界の様々な分野の時事ネタを中心に話しているプログラム。

音声だけでも面白いけど、スマホMacのアプリから各回の文字起こしテキストを有料で読むことができて、それがすごく充実していて面白い。

文字起こしといえば、このブログでもけっこう馴染みのネタで、一応仕事としてもそういうことをやりがちなぼくとしては、つい同業者的視点からそれを見てしまうのだけど、英語と日本語が入り混じって90分ぐらい続く上に専門用語みたいなのもよく出てくる内容なのに、いつも音声エピソードの公開から2日ぐらい? で文字起こしが公開されていて、ええ、これどうやってんだろ? 普通に耳で聴いて手で打ち込んでるのかなあ、すごすぎだろ、とか思っていた。

で、これもこのブログの馴染み的なネタとして、以前からGoogleの音声入力機能を使った文字起こしというのを時々やっているので、もしかしたらそれ(音声入力ベースの文字起こし)をやってるのかなあ、とか思いながら、自分でも試してみたのがこちら。

www.youtube.com

サンプル的に使わせて頂いたのは、現時点での最新回である #BN274。(2分程度)

上の動画は、Googleドキュメントのメニューバーから「ツール/音声入力」で操作しているところ。

自分では一切打ち込んでなくて、時々カーソルを動かしてマイクのボタンをクリックしたりしている。

音声の方はSoundflowerというMacアプリでMac自体に音を聴かせて、それだけだと人間(私)には何が流れているかわからないので、進捗をモニターするためにLadioCastというアプリも一緒に動かしてる。

詳しくはこの辺にまとめています。

で、やってみた感想としては、「そんなに早くは終わらなそう」(笑)。
(まあ、ぜんぶ自力でやるより体はラクだと思うけど)

というのも、そのGoogleドキュメントの音声入力を使う場合、動画にもあるように、英語を入力したければ英語設定に、日本語をテキスト化したかったら日本語設定に、そのつど切り替えないと読み込んでくれないので、その切り替え作業をやっていると、その間に喋ったことが漏れてしまう。

なので、もしこれを使うなら、最初に英語は英語だけでざーっと拾ってしまって、その後に日本語は日本語設定でざーっともう一周聴かせて、それぞれのテキストをマージする、という感じになりそうだと思った。

で、長くなったけどここまでが前置きで、以下が掲題の件。

そこまでやってふと思ったのは、そうやって「英語のトークは英語設定で拾う」というのをやっても尚、やっぱり同音異義語とか、喋るスピードに機械が追いつかないとかの理由でどうしても拾い間違いが出てくるので、逆にというか、その拾い間違えた英語の部分を「間違い探し」的に当てていく、というリスニングの練習ができそうだなと思った。

具体的には、

1. まず上記の手順でざっくりテキストを作って、
2. その「とりあえず起こせてるっぽい英文」を眺めながらポッドキャスト音声を聴いてみる。
3. すると、たぶん耳で聴いたのとはちょっと違う、拾い間違えたテキストが出てくるはずなので、そこを自分が聴き取った言葉にどんどん直していく。
4. 「これ以上はどこが違うかわからないな」というぐらいまで直したら、上記のアプリから「正解」の文字起こしテキストを見て「答え合わせ」をしてみる。
5. ぜんぶ合ってたら次に行く。

みたいな。

面白そう!

と思ったんだけど、実際にはいろいろセッティングしたり、とりあえず最初の問題文(拾い間違いを含んだ英文テキスト)を作るだけでもけっこう手間がかかりそうなので、まあ非現実的か……。 :sweat_smile:

おまけ

リスニング練習とは関係ないけど、上の流れでGoogle翻訳を使っていたら、ここでも音声入力が使えることに気づいて、ちょうどその自動文字起こしセットを動かしていたので、戯れに似たようなことをやってみた*1
www.youtube.com
終わり。

*1:使用したエピソードは最初の動画の続き。

carvo update

f:id:note103:20170705123353g:plain

  • Macだと音声も出るようになっているので、音声付き動画も作りました。
    • 6秒目ぐらいで急に音が出ます。

www.youtube.com

  • 以下、つれづれに。
  • 昨日も別のところに書いたのだけど、今回はメンテナンスの邪魔になりがちなファットな要素(=便利だと思っていたけど本質的な要素とはちょっとズレた機能)をザクザク外すのが主な変更。
  • テストもちょっとだけ入れつつ。
  • 未解決の問題として、どうもどこかの段階で、ファイルへの回答記録記入の動作に不具合が生じてしまった模様。
    • ゲームの途中で単語カードを切り替えた場合、切り替える前の記録が消えてしまう。
    • 正答/誤答数などを記入する `result.txt` の方は無事なようだけど、詳細に英単語などを記録する `log.txt` の方が上書きされてしまうみたい。
    • 以前は何回切り替えてもすべて保持していたのだけど……。
    • よって、ヘルプから記録に関する文言を一旦カットして、忘れないように自分でIssue登録しておいた。
  • 今後のTODOとしては、上記バグ修正のほか、残りのテストを充実させること、その流れでTravis CIとの連携を復活させたいところ。
    • CI連携に関しては、以前に少し頑張ってカバレッジ可視化サービスと繋げて表示したりもしていたのだけど、なにしろ拙い&多めのコードを全然カバーしきれず効率悪かったので一旦やめていた。
    • まだまだ自分の中でも整理できないまま動かしている部分が多々あるので、その辺を整理しつつテストも補って、というふうに進めていきたいところ。
  • 最後にREADMEを貼っておきます。

    
      続きを読む