2012年8月4日土曜日

LL Decade 「君ならどう書く Online」 で書いたコード

本日(8/4)開催された、LL Decade では「君ならどう書く Online」という企画がありました(問題の内容はリンク先参照)。14:00 までという短い時間と告知がそれほど強いものではなかったこともあってか参加者は 14 名(+締め切り以降10名)(間違ってるかも)だったそうですが、おかげで商品をゲットすることが出来ました。問題発表前は C++ Template Meta Programming で書いて「コンパイルの必要もない Lightweight です(キリッ」とかやろうかと思ったのですが、文字列の validation ということで断念しました。で、順当に Perl で書いた訳ですがせっかくこういう場で書くんだから多少のひねりが欲しいところです。という訳で書いたのが以下のコード。文字列処理ということで正規表現さんにお出まし願ったわけですが、普段使わない機能を使ってみました。

#!/usr/bin/perl

# regexp 内でそれなりに頑張る

use utf8;
use strict;
use warnings;

local $/; # slurp
my $t = <STDIN>;
$t =~ s/^(
   (?<ipv4>(?&ipv4_))|
   (?<ipv6>(?&ipv6_))|
   (?<mac>(?&mac_))|
   (?<any>.*)
  )$
  |(?<spc>\s+)
  (?(DEFINE)
   (?<ipv4_1>1[0-9]{2}|2([0-4][0-9]|5[0-5])|[1-9]?[0-9])
   (?<ipv4_>(?:(?&ipv4_1)\.){3}(?&ipv4_1))
   (?<ipv6_1>[1-9a-fA-F][0-9a-fA-F]{0,3}|0)
   (?<ipv6_>(?:(?&ipv6_1):){7}(?&ipv6_1))
   (?<mac_1>[0-9a-fA-F]{2})
   (?<mac_>(?:(?&mac_1):){5}(?&mac_1)|(?:(?&mac_1)-){5}(?&mac_1)) # 格好悪い。backreference でいける?
  )/@{{
   ipv4 => '01',
   ipv6 => '10',
   mac => '00',
   any => '11',
   spc => '',
  }}{keys %+}/mxge;

print pack('B*', $t);

9,10 行目は Perl5 だと悲しい slurp。以降の(一応一つの)正規表現で 0 と 1 の文字列に変換してから pack() で普通の文字列に復元という流れになっています。18行目からの (?(DEFINE) で始まっている部分は正規表現パターンに対して名前を付けている部分です。(?<name>pattern) で pattern に対して name という名前を付け、(?&name) で呼び出している訳です。hoge_1 になっているのがアドレス構成要素 1 要素分、hoge_ になっているのがアドレス全体に対するパターンです。これを (?<name>pattern) で hoge として名前付きキャプチャしています(12行目から)。名前付きキャプチャの結果はハッシュ %+ に設定されるので、keys を使って有効なキャプチャ名(hoge)を取得し、ハッシュで値を置き換えています(25行目)。keys をリストコンテキストで評価したいので(ハッシュリテラルに対する)ハッシュスライスを使っています。わざわざハッシュリテラルを使っているのはその方がなんか格好良さそうだからです。spc 関連は改行削除用のものです。正規表現のオプションとしては、複数行に ^$ をマッチさせるための m、空白等を入れるための x、全体を置き換えるための g、eval するための e を指定しています。こうして変換した後は pack を使って変換して出力するだけです。

perlre とか見ながら即席で書いたにしてはなかなか面白みのあるコードに仕上がったんじゃないかと思います。IP アドレス等にマッチする正規表現そのものがあんまりいけてない感じなのがちょっと残念なところですが。もっと格好良い書き方がある気がします。

ちなみにもらった商品は、iOS プログラミング第2版(ISBN978-4-86401-049-8) なのですが、Mac 持ってないんですよね。iPad も会社支給(貸与)された今、MBP とかを買うべきと言うお告げだったりするのでしょうか。

0 件のコメント:

コメントを投稿