Skip to content

Instantly share code, notes, and snippets.

@gfx
Last active September 17, 2017 21:57
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gfx/7973689 to your computer and use it in GitHub Desktop.
Save gfx/7973689 to your computer and use it in GitHub Desktop.
正規表現リテラルがあると便利な例。これを文字列操作メソッドで解決するとなるとかなり難しいと思われますがどうでしょう。 また、正規表現リテラルがなくてもじっさいにはあまり変わりないのですが、 \S ではなく \\S と書かなければならなかったりして注意しなければならない点が増えます。
#!perl
use 5.10.0;
use strict;
use warnings;
my $localhost = join '|', map { quotemeta } 'localhost', '127.0.0.1';
my $s = "http://localhost/foo http://127.0.0.1/bar http://127_0_0_1/foo/bar/baz";
my @matched = $s =~ m{ http:// (?:$localhost) / \S+ }xg;
say "@matched"; # => http://localhost/foo http://127.0.0.1/bar
@methane
Copy link

methane commented Dec 15, 2013

from __future__ import print_function  # For (legacy) Python 2.
LOCALHOST_PREFIXES = ('http://localhost', 'http://127.0.0.1')

s = "http://localhost/foo http://127.0.0.1/bar http://127_0_0_1/foo/bar/baz".split()
matched = [w for w in s if w.startswith(LOCALHOST_PREFIXES)]
print(*matched)  # http://localhost/foo http://127.0.0.1/bar

@gfx
Copy link
Author

gfx commented Dec 15, 2013

s がHTML documentだとするとどうでしょう?

@methane
Copy link

methane commented Dec 15, 2013

Perl 側で、 m{ ... } 形式の正規表現とか、 (?:$localhost) の左右にスペースがあっても無視される (S+ なのかな?)とか、たくさんの規則、構文を、正規表現のためだけに覚えてる人には便利だと思います。

一方 Python の内包表記リテラルや引数の glob は、文字列処理以外にも汎用的に使えます。
startswith がタプルを受け取るのは若干トリビアですが、これも isinstance の第二引数など
多くのメソッドが同じ方式で複数の OR 条件を持てるようになっているので再利用性が高いです。

汎用的な、よく使う部分はちゃんと簡潔な方法を用意して、 Domain Specific な部分のためには
構文を汚さずライブラリで対応するのが Python のポリシーですし、正規表現リテラルを持たない
多くの言語がそのような判断をしていると思います。

@methane
Copy link

methane commented Dec 15, 2013

s がHTML documentだとするとどうでしょう?

さすがにその時は正規表現使うか、HTMLのパーサー使いますね。

@methane
Copy link

methane commented Dec 15, 2013

Perl の正規表現が魔法過ぎて何やってるのか全く読めないので、同じことを Python で書いたらどうなるかはちょっと待って下さい。

@methane
Copy link

methane commented Dec 15, 2013

from __future__ import print_function  # For (legacy) Python 2.
import re

LOCALHOST = '|'.join(['localhost', '127.0.0.1'])
LOCALHOST_PATTERN = re.compile(r'http:// (' + LOCALHOST + r')/\S+', re.X)

s = "http://localhost/foo http://127.0.0.1/bar http://127_0_0_1/foo/bar/baz"

matched = LOCALHOST_PATTERN.finditer(s)
print(*[m.group(0) for m in matched])  # http://localhost/foo http://127.0.0.1/bar

できるだけ Perl のサンプルコードと同じように書いてみました。
これを比べても、学習コストを増やして構文汚してまでリテラルがほしいとは思えません。
加えて、このように正規表現が使いたい場面自体、月に数回遭遇する程度です。

@gfx
Copy link
Author

gfx commented Dec 15, 2013

LOCALHOST_PATTERN = re.compile(r'http:// (' + LOCALHOST + r')/\S+', re.X)

この一行が象徴的なのですが、まさにこれが正規表現リテラル+変数展開が必要な理由に見えます。
つまり、本質的にできることは変わりませんが、変数があるたびに文字列リテラルを閉じて+を書いて変数を書いてい+を書いて文字列リテラルを開始して、というのがちいち面倒なんです。面倒だから簡単に書きたい、そのほうが生産性があがる、という話です。

@methane
Copy link

methane commented Dec 16, 2013

閉じて + というのは、

r'http:// (%s) /\S+' % LOCALHOST

とも書けますが、そうではなく埋め込みがしたいと。
それはもう完全に正規表現リテラルではなくて変数埋め込みの利点ですよね?

変数埋め込みも、あれば便利だと思いますが、 Python ではたった数タイプの削減のために
構文を複雑にはしないと蹴られました。

余談ですが、今開発中のプロジェクトでは現在 120 ファイルくらいあって、 import re しているのは
3ファイルだけでした。
もちろん、 import re とたった数タイプの面倒さを回避するために、正規表現ですれば簡単な事を
面倒な文字列操作にしたりはしていません。
startswithinsplit などの基本的な文字列操作でほとんどのケースをカバーできてます。

@gfx
Copy link
Author

gfx commented Dec 16, 2013

正規表現リテラルにおける変数展開の重要性についてはたびたび出していたつもりでしたが、その点説明不足であったのならすみません。たとえばJavaScriptは正規表現リテラルがあって変数展開のない言語ですが、これは非常に使いづらくリテラルだけあっても意味がないとぼくは考えます。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment