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', *@module, 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.95

2016年10月22日発売
B5判/184ページ
定価(本体1,480円+税)
ISBN978-4-7741-8432-6

  • 特集1
    [実装例でわかる!]実践アクセシビリティ
    読み上げ,タッチ,キーボードetc. 多様な操作に対応
  • 特集2
    Goによる並行処理
    複雑な処理をスイスイ書こう!
  • 特集3
    試して学ぶHTTP
    最新仕様まできちんと理解!
  • 一般記事
    機械学習×サーバ管理
    将来予測,異常検出による先進的インフラ運用
  • 一般記事
    Androidアプリの国際化
    数詞も日時もレイアウトも,さまざまな言語に対応させよう!

著者プロフィール

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

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

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

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

GitHub:https://github.com/skaji

コメント

コメントの記入