Masteries

技術的なことや仕事に関することを書いていきます.

小ネタ: Perlのhashとデフォルト値

関数の引数をhashで受けて, 未定義の場合はデフォルト値を設定したい... という場合があるとします. まあだいたい, defined-orを使って次のように書くのではないでしょうか:

sub myfunc {
    my %args = @_;

    return $args{foo} // 'default';
}

myfunc(foo => 'yeah'); # => 'yeah'
myfunc(); # => 'default'

一方で undef を渡せるようにしたいという場合, この実装だとデフォルト値に上書きされてしまいます.

myfunc(foo => undef); # => 'default' XXX: undef が返ってきて欲しいのに...

そういうときは, exists を使って次のように書くという手が使えます:

sub myfunc {
    my %args = @_;

    return exists $args{foo} ? $args{foo} : 'default';
}

myfunc(foo => 'yeah'); # => 'yeah'
myfunc(); # => 'default'
myfunc(foo => undef); # => undef;

もし$argsがhashのリファレンスならこうですね: exists $args->{foo} ? $args->{foo} : 'default';

テストヘルパーとか作る時によく使うsnippetって感じで覚えておけると良さそうです.

Class::Accessor::Typed 0.03_02 で Type::Tiny 対応を試しています

papix.hatenablog.com

先日, 0.03を出したClass::Accessor::Typedですが, Type::Tiny対応を試した 0.03_02 をリリースしました. Development releaseなので, cpanm とかでインストールする時はバージョンを指定してインストールするようにしてください:

cpanm Class::Accessor::Lite@0.03_02

Class::Accessor::Typedはインスパイア元であるSmart::ArgsにならってMouse::Util::TypeConstraintsを使っているのですが, Smart::Args::TypeTiny派はType::Tinyを使いたいよね, となるのは自明でした. というわけで, Smart::Args::TypeTinyの実装を参考にしつつ, Mouse::Util::TypeConstraintsを使うか, Type::Tinyを使うかを差し替えられるようにしています.

デフォルトは従来どおりMouse::Util::TypeConstraintsで, 次のようにすればType::Tinyが使われます(Type::Tinyはオプションとして, Class::Accessor::Typedの依存モジュールにしていないので, 別途インストールする必要があります).

use Class::Accessor::Typed (
    type => 'TypeTiny',
    ...
);

ただ, これだと都度typeを指定する必要があるので面倒ですね. 書き忘れて意図せずデフォルト実装が使われることもありそうです. そこで, 次のエントリで紹介したような, パッケージを別途用意する方法だとシンプルに書けておすすめと思います:

papix.hatenablog.com

package MyClassAccessorTyped;

use Class::Accessor::Typed;

BEGIN { $Class::Accessor::Typed::TYPE_CLASS = "Class::Accessor::Typed::TypeTiny" }

sub import {
    goto \&Class::Accessor::Typed::import
}

1;

この辺りの, Type::Tinyを指定する方法はなんかいい方法がしっくり来ていなくて, 正式に対応するバージョンになるであろう0.04で使い方が変わる可能性がありそうです. 「こういう風になっていると嬉しそう!」というご意見などありましたら, TwitterやGitHubのIssueでコメント頂ければと思います.

github.com

Kichijoji.pmで2回くらい話していました

...が, そういえばブログエントリ書いてなかったので, 発表資料を紹介しておきます.

Kichijoji.pm #28

Kichijoji.pm #29


特に「Kichijoji.pm #28」の「ワーケーションに関する考察」については, 自分らしい(旅行が趣味なエンジニアらしい)発表になったんじゃないかな, と思っていて, ワーケーションに関する知見をうまくまとめられたと思っています. コロナ禍の影響でだいぶ「ワーケーション」という文化が普及してきた感じがあって, 「試してみたい!」という人に届いたらいいなと思います.

Kichijoji.pm, 自分にとっては実家のような場所で, いろいろなネタを持っていっても暖かく受け止めてくれる(?)ので, いい勉強会だなって思います. これからも, Kichijoji.pmを通して技術に限らないエンジニアとしてやっていく上でのいろいろな話題を共有していけたらいいなって思います.

小ネタ: Perlのsplitの第3引数

超〜〜〜小ネタです. Perlにはsplitという関数がありまして, 次のように使えます:

my @list = split(/,/, "a,b,c"); # @list = ('a', 'b', 'c');

第1引数に正規表現を, 第2引数に文字列を渡すと, 第2引数の文字列を第1引数の正規表現にマッチした部分で分割してくれます.

で, 実はsplitには第3引数が渡せるのはご存知ですか? ご存知でしたら, このエントリで語りたいことの本題はそれなので, これ以上読み進める必要はありません(?).

さて, splitに第3引数を渡すと, 次のような挙動になります:

my @list = split(/,/, "a,b,c", 2); # @list = ('a', 'bc');

要するに, 第3引数で渡した数まで分割してくれる, という挙動をします.

なので, 例えば hogehoge.jpg といった文字列を hogehogejpg に分割したい, という時は, split(/\./, 'hogehoge.jpg') と書いてもいいのですが, split(/\./, 'hogehoge.jpg', 2) のように書いてあげると, 「2つに分割したいんだな」というのが明示できてお得かもしれません.

更に, 「分割した結果のうち, 片方しか必要ない」という場合は, 次のようにして受けるのが個人的には好みです:

# $basename = hogehoge となる
# jpg の部分は不要なので, undef で受ける
my ($basename, undef) = split(/\./, 'hogehoge.jpg', 2);

こうしておけば, 「splitで文字列を2つに分割したい」, 「分割した2つのうち, $basenameの部分(分割後の1つ目)だけ必要」ということを伝えられていいんじゃないかな, と思っています.

もっのすごい小ネタですが, まあたまにはこういうネタでもブログ書いてもいいじゃん, ということで...

Class::Accessor::Typed 0.03をリリースしました

Class::Accessor::Typedの0.03をリリースしました. 前回の0.02は2019年のリリースなので約2年ぶりですね...

papix.hatenablog.com

今回の0.03では, optional オプションの追加をしました. 次のように使えます:

use N;

# optional なので, `rw2` は指定しなくてもよい
my $obj = N->new(rw1 => 'RW1');
is $obj->rw1, 'RW1';
is $obj->rw2, undef;

package N;

use Class::Accessor::Typed (
    rw => {
        rw1 => 'Str',
        rw2 => { isa => 'Int', optional => 1 },
    },
);

Smart::Argsのように, optional が真の場合, new するときにそのパラメータを渡さない... ということができます(その場合, undefになります).

metacpan.org

宜しければ使ってみてください.