Perl Hackers Hub

第39回 Perl 6の歩き方―15年越しでリリースされた新バージョン(2)

この記事を読むのに必要な時間:およそ 2 分

前回の(1)こちらから。

関数─⁠─Perl 6の特徴的文法

Perl 6には本当にたくさんの機能があります。ページは限られているので何を紹介すべきか迷いましたが,本稿では関数を取り上げます。

Perl 6の関数にはいくつかの種類があります。そして,それらにいろいろな機能が備わっています。

関数の種類

関数には,Sub,Block,WhateverCodeがあります。そしてそれらをまとめるCallableロールがあります。

Sub─⁠─普通の関数

普通の関数Subは,subキーワードで定義します。

sub hello($name) {
  "hello, $name!".say;
}
Block─⁠─実行されうるもの

Blockである{}を用いても,⁠実行されうるもの」を定義できます。

# トピック変数$_で引数を使える
my $is-even = { $_ %% 2 };

# -> 引数1, 引数2, ... の形で引数を宣言可能
my $distance1 = -> $x, $y {
  ($x ** 2 + $y ** 2).sqrt
};

# $^記号で明示的に宣言せずに引数を利用可能
my $distance2 = {
  ($^x ** 2 + $^y ** 2).sqrt
};

# for,if,whileなどもこのブロックが使われている
for 0..9 -> $x, $y {
  say $x ~ $y;
}

関数Subと,このBlockの違いはreturnの扱いにあります。関数内でreturnするともちろんその関数から復帰しますが,Block内でreturnすると外側の関数から復帰しようとします。すなわち,

sub even-array() {
  (^10).map: { return 2 * $_ };
}

return 2 * $_mapに渡したBlockから復帰するのではなく,外側の関数even-array()から復帰しようとします。この例では,おそらくそれは意図と違うでしょうから,

sub even-array() {
  (^10).map: { 2 * $_ };
}

と書く必要があります。

WhateverCode─⁠─さらにカジュアルな実行されうるもの

WhateverCodeである*によって,さらにカジュアルに「実行されうるもの」を定義できます。

my $half = * / 2; # -> $x { $x / 2 } と等価
$half(10); # 5

# 「偶数を引数にとる」を簡潔に表現できる
sub foo(Int $even where * %% 2) { ... }
Callable─⁠─関数をまとめるロール

ここまで紹介したSub,Block,WhateverCodeは,Callableロールのもとにまとまります。ロールとはほかの言語でのインタフェースに相当するもので,振る舞いを定義します。そしてSub,Block,WhateverCodeは,⁠実行されうる」という振る舞いを表すロールであるCallableを実装しているのです。

さて,実のところ任意のクラスをCallableロールを実装して定義すれば,それは「実行されうるもの」として扱えます。

class Foo does Callable {
  method CALL-ME($arg) { ... }
}

my &foo = Foo.new;
&foo("arg"); # CALL-MEが呼ばれる

クラスを用いれば,メンバー変数で状態を持った関数を作る,再利用可能な関数を作るなど,いろいろな可能性が出てきます。

引数,戻り値の宣言

すでにいくつかのコードに出てきましたが,Perl 6では関数を定義するときに引数を宣言できます。また,引数の型,戻り値の型も宣言できます。

# 引数を宣言
sub foo($n, $s) { ... }

# 引数を型とともに宣言
sub foo(Int $n, Str $s) { ... }

# 戻り値の型も宣言
sub foo(Int $n, Str $s --> Callable) { ... }

この(Int $n, Str $s --> Callable)のような関数の引数,戻り値の情報を,シグネチャと呼びます。

多重ディスパッチ

multiキーワードを使えば,同じ名前でシグネチャが異なる関数を複数定義し,それらを呼び分けられます。

multi foo(Str $s) { ... }
multi foo(Int $n) { ... }
multi foo($other) { ... }

foo(10); # foo(Int $n)が呼ばれる
foo({ say "hello" }); # foo($other)が呼ばれる

この多重ディスパッチが特に威力を発揮するのはMAIN関数の場合です。

まず,MAIN関数について説明します。MAIN関数はプログラムが実行されると自動的に呼び出される関数で,コマンドライン引数からオプションをパースするという便利な機能が付いています。たとえば次のように定義すると,

script.p6

sub MAIN(Bool :$verbose, Str :$prefix = "/opt") {
  say "-> verbose mode" if $verbose;
  say "-> prefix is $prefix";
}

以下に示すようにオプションを簡単に扱えます。

オプションがパースされる

$ perl6 script.p6 --verbose --prefix /usr/local
-> verbose mode
-> prefix is /usr/local

自動的にヘルプメッセージも生成される

$ perl6 script.p6 --help
Usage:
  script.p6 [--verbose] [--prefix=<Str>]

このMAIN関数は,多重ディスパッチを用いることでさらに便利になります。たとえば先ほど使ったモジュール管理ツールpandaには,次のようなMAIN関数が定義されています。

# pandaから一部改変して抜粋
multi MAIN('install', [email protected], Str :$prefix) {
...
}
multi MAIN('list', Bool :$installed) {
...
}

こうすることで,いわゆるサブコマンドによってMAIN関数を呼び分けられます。すなわちpanda install...ならmulti MAIN('install', ...)が呼ばれ,panda list ...ならmulti MAIN('list', ...)が呼ばれます。自分でサブコマンドによる分岐を書くより,プログラムがすっきりします。

関数のラップ

関数をラップして,その関数の前処理,後処理を付け加えることができます。

sub distance($x, $y) {
  ($x ** 2 + $y ** 2).sqrt;
}

say distance(3, 4); # 5

# distanceをラップ
my $squashed = &distance.wrap: -> $x, $y {
  # callwithによってオリジナルの関数(distance)を
  # 呼び出す
  callwith($x * 2, $y / 2);
};

say distance(3, 4); # 6.32...

# アンラップしてもとに戻す
&distance.unwrap($squashed);

say distance(3, 4); # 5

もしあるブロック内だけ関数をラップしたいのであれば,ブロックを抜けたときの処理を指定できるLEAVEとともに使うとよいでしょう。

{
  my $squashed = &distance.wrap: -> $x, $y {
    callwith($x * 2, $y / 2);
  };
  LEAVE &distance.unwrap($squashed);
  say distance(3, 4); # 6.32...
}
# ブロックを抜けると自動的にもとに戻る
say distance(3, 4); # 5

Perl 6の関数には魅力的な機能がまだまだたくさんあります。詳しくはSynopsis 6: Subroutinesを参照してください。

<続きの(3)こちら。>

WEB+DB PRESS

本誌最新号をチェック!
WEB+DB PRESS Vol.98

2017年4月22日発売
B5判/152ページ
定価(本体1,480円+税)
ISBN978-4-7741-8920-8

  • 特集1
    [調べ方から身につける]Web開発 基礎の基礎
    開発環境の整備,フレームワークで開発,エラーに対処
  • 特集2
    これからはじめるDocker
    最新インフラ構築の一部始終を体験!
  • 特集3
    AWSコスト削減
    半額だって夢じゃない!
  • 一般記事
    良いPHPコードを保つ技術
    規約と指針を整備し,静的解析ツールを活かす
  • 一般記事
    技術系カンファレンスに行こう!
    参加する方法,発表者になる方法

著者プロフィール

鍛治匠一(かじしょういち)

1984年神奈川県生まれ。ヤフー株式会社所属。

広告配信システムの開発,運用をしている。

いつも,すきあらばPerlで開発しようと画策している。

GitHub:https://github.com/skaji

コメント

コメントの記入