LoginSignup
1
0

More than 5 years have passed since last update.

Smalltalk 風 if

Last updated at Posted at 2012-12-26

今回は、筆者の趣味に偏っています。

そもそもオブジェクト指向はこうだった

以下は、Smalltalk での真偽値の実装の一部です。

trueAlternativeBlockfalseAlternativeBlock は Perl 6 ならそれぞれ &true-alternative-block&false-alternative-block と表現されるものです。そして、trueAlternativeBlock value という式は、Perl 6 なら &true-alternative-block() に当たります。^return です。

Smalltalk には条件分岐のための構文はありません。Smalltalk で真偽値を得る式を評価すると、TrueFalse のシングルトンインスタンスが返ります。これらのインスタンスは、以下に定義されたように振る舞い、結果的に条件分岐が実現されます。

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)

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; }
}

真偽値クラスのテスト

a.basic.t6
#!/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 - 

整数クラス

I.pm6
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); })
        )
    ;
}

テスト

b.factorial.t6
#!/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 - 

次回は、以上のプリミティブを使ってループを定義してみます。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0