今回は、筆者の趣味に偏っています。
そもそもオブジェクト指向はこうだった
以下は、Smalltalk での真偽値の実装の一部です。
trueAlternativeBlock
や falseAlternativeBlock
は Perl 6 ならそれぞれ &true-alternative-block
、&false-alternative-block
と表現されるものです。そして、trueAlternativeBlock value
という式は、Perl 6 なら &true-alternative-block()
に当たります。^
は return
です。
Smalltalk には条件分岐のための構文はありません。Smalltalk で真偽値を得る式を評価すると、True
か False
のシングルトンインスタンスが返ります。これらのインスタンスは、以下に定義されたように振る舞い、結果的に条件分岐が実現されます。
True>>ifFalse: alternativeBlock
^nil
True>>ifFalse: falseAlternativeBlock ifTrue: trueAlternativeBlock
^trueAlternativeBlock value
True>>ifTrue: alternativeBlock
^alternativeBlock value
True>>ifTrue: trueAlternativeBlock ifFalse: falseAlternativeBlock
^trueAlternativeBlock value
True>>not
^false
False>>ifFalse: alternativeBlock
^alternativeBlock value
False>>ifFalse: falseAlternativeBlock ifTrue: trueAlternativeBlock
^falseAlternativeBlock value
False>>ifTrue: alternativeBlock
^nil
False>>ifTrue: trueAlternativeBlock ifFalse: falseAlternativeBlock
^falseAlternativeBlock value
False>>not
^true
Perl も負けていない
同じことは Perl で (Perl 5 でも)実現できます。Perl にはクロージャーがあるからです。
真クラス (T)、偽クラス (F)、両者の定義 (B.pm6)
use v6;
class T { ... }
class F { ... }
my ($t, $f) = (T.new(), F.new());
class T
{
my $._ = $t;
method _if_(:&t = { self; }, :&f = { self; })
{
&t();
}
method not() { return $f; }
}
class F
{
my $._ = $f;
method _if_(:&t = { self; }, :&f = { self; })
{
&f();
}
method not() { return $t; }
}
真偽値クラスのテスト
#!/usr/bin/env perl6
use v6;
use B;
use Test;
plan *;
is T._._if_, T._;
is T._._if_(:f({ 'ng' })), T._;
is T._._if_(:t({ 'ok' })), 'ok';
is T._._if_(:t({ 'ok' }), :f({ 'ng' })), 'ok';
is F._._if_, F._;
is F._._if_(:t({ 'ng' })), F._;
is F._._if_(:f({ 'ok' })), 'ok';
is F._._if_(:f({ 'ok' }), :t({ 'ng' })), 'ok';
$ perl6 -I. a.basic.t6
ok 1 -
ok 2 -
ok 3 -
ok 4 -
ok 5 -
ok 6 -
ok 7 -
ok 8 -
整数クラス
use v6;
use B;
class I;
has Int $.Int;
my $._0 = I.new(0);
my $._1 = I.new(1);
method new($native)
{
self.bless(*, :Int($native));
}
method pred()
{
self.WHAT.new($.Int.pred);
}
method add(I $with)
{
self.WHAT.new($.Int + $with.Int);
}
method times(I $by)
{
self.WHAT.new($.Int * $by.Int);
}
multi method equals(I $cmp)
{
self.equals($cmp.Int);
}
multi method equals(Int $cmp)
{
($.Int == $cmp ?? T !! F)._;
}
例によって階乗
これらが本当に機能するか、おなじみの I.factorial を定義して試してみましょう。I クラスに次を追加します。
method factorial()
{
my $one = I._1; # レキシカルスコープのデモ。
self.equals(0)\
._if_(
:t({ $one; }),
:f({ self.times(self.pred.factorial); })
)
;
}
テスト
#!/usr/bin/env perl6
use v6;
use I;
use B;
use Test;
plan *;
is I._0.factorial.equals(1), T._;
is I._1.factorial.equals(I._1), T._;
is I.new(2).factorial.equals(I.new(2)), T._;
is I.new(4).factorial.Int, 24;
$ perl6 -I. b.factorial.t6
ok 1 -
ok 2 -
ok 3 -
ok 4 -
次回は、以上のプリミティブを使ってループを定義してみます。