前回の
移植時に活用すべき機能
移植方針の目処が立ったら、
(2)
正規表現
Perlでは、
たとえば、String
クラスのeach_
インスタンスメソッドを呼び出すことで実現できます。
text.each_char do |ch|
# chを使って処理する
end
Perlでは、
for my $ch ($text =~ /./g) {
# $chを使って処理する
}
g
フラグを付けた正規表現マッチは、
my @ch = 'abcd' =~ /./g;
# => ('a', 'b', 'c', 'd')
配列を処理する関数・ライブラリ
ある配列の各要素を加工した配列を作ったり、
[1, 2, 3].map { |x| x * 2 } # => [2, 4, 6]
Perlの配列は、for
ループで要素をpush
していく方法と、map
やgrep
といった配列を操作する組込み関数を使う方法が考えられます。
単純な処理であれば、twice
を、for
ループで各要素を2倍にしてpush
する方法と、map
を使う方法とで実装した場合の速度を比較してみましょう。以下に、twice.
と筆者の環境
twice.pl
use Benchmark qw(:all);
sub twice_map {
return map { $_ * 2 } @_;
}
sub twice_push {
my @ret;
push @ret, $_ * 2 for @_;
return @ret;
}
my $count = 100000;
my @input = (1..10000);
cmpthese($count, {
twice_map => sub {
twice_map(@input);
},
twice_push => sub {
twice_push(@input);
},
});
% perl twice.pl Rate twice_push twice_map twice_push 1371/s -- -48% twice_map 2630/s 92% --
map
を使うほうが、for
ループでpush
していくよりも2倍近く高速に処理できていることがわかります。
map
やgrep
といった組込み関数よりも高度な処理を行いたい場合は、
移植時に注意すべきこと
せっかくライブラリをPerlに移植できても、
本節では、
文字コード
操作する対象の文字列を、
プログラミング言語によっては、utf8
フラグの有無によって文字列であるかバイト列であるかが変わります。また、
たとえば、length
の返す結果は、
use utf8;
my $x = '日本語'; # $xはUTF-8文字列
warn length $x; # => 3
{
no utf8;
my $y = '日本語'; # $yはバイト列
warn length $y; # => 9
}
Perlの文字列の内部表現や、utf8
フラグについての詳細は、
YAMLライブラリの選定
YAML::Tiny、
YAML::Tinyはその名のとおり、
YAML::PPはYAML 1.
Twitter::Textの実装にあたっては、
関数がリファレンスを返すかどうか
Perlでは、
# 配列を返す場合
sub func1 {
my @array;
...
return @array;
}
# 配列リファレンスを返す場合
sub func2 {
my $array = [];
...
return $array;
}
どちらを選択するにせよ、
配列そのものを返す場合
配列そのものを返す場合、map { bar($_) } func1()
のように、
# baz()は値の組を返す関数
my ($foo, $bar) = baz();
一方で、
# 配列そのものをネストさせることはできない
my @arr1 = (1, 2);
my @arr2 = (0, @arr1, 3);
# => (0, 1, 2, 3)
# bar()が(1, 2)という配列を返す場合、
# func('foo', 1, 2)と等価になる
func(foo => bar());
配列リファレンスを返す場合
配列リファレンスを返す場合は、
# 配列リファレンスはネストできる
my $arr1 = [1, 2];
my $arr2 = [0, $arr1, 3];
# => [0, [1, 2], 3];
# bar()が[1, 2]という配列リファレンスを返す場合、
# func('foo', [1, 2])と等価になる
func(foo => bar());
一方で、map { bar($_) } @{func2() }
のようにデリファレンスが必要になります。また、undef
を返してしまい、
sub func {
# 配列リファレンスを初期化していない
my $array;
# 初期化せずにearly returnした場合、undefが返る
return $array unless ...;
...
}
真偽値
Perlでは、
- 未定義値
undef
- 数値の
0
- 文字列の
'0'
- 空文字列
''
文字列の'0'
も偽値として扱われることに注意しましょう。デフォルトでは、
たとえば、func(foo => 'bar')
のように、foo
という名前付き引数に文字列を渡すか、$args{foo}
のデフォルト値を補完しようとすると、'0'
を引数に渡したとき、$args{foo}
の値が意図せずデフォルト値になってしまいます。
sub func {
my (%args) = @_;
# 間違い
$args{foo} ||= 'default value';
...
}
次のように、exists
組込み関数を使って$args{foo}
が渡されているかどうかを判定してからデフォルト値を補完すると安全です。Perl 5.//=
演算子を使うこともできます。
sub func {
my (%args) = @_;
# 正しい
$args{foo} =
exists $args{foo}
? $args{foo}
: 'default value';
# Perl 5.10以降なら次のようにも書ける
$args{foo} //= 'default value';
...
}
意図しない警告
use warnings
していると、undef
を連結したときなどにエラーにならず警告が出力されます。警告の数が多いと、
移植したライブラリを使う際に、
use Test2::V0;
use Test2::Plugin::NoWarnings;
my $x = undef;
# 警告が出るのでテストが失敗する
print $x . ' is awesome';
ただ、no warnings
とすることで警告を無効化できます。
use warnings;
{
no warnings 'utf8';
# このスコープ内ではutf8に関する警告が無効化される
}
# スコープ外ではutf8に関する警告が出る
まとめ
他言語で実装されたライブラリをPerlに移植する際の考え方や、
さて、
本誌最新号をチェック!
WEB+DB PRESS Vol.130
2022年8月24日発売
B5判/
定価1,628円
ISBN978-4-297-13000-8
- 特集1
イミュータブルデータモデルで始める
実践データモデリング
業務の複雑さをシンプルに表現! - 特集2
いまはじめるFlutter
iOS/Android両対応アプリを開発してみよう - 特集3
作って学ぶWeb3
ブロックチェーン、スマートコントラクト、 NFT