rakudo-and-nqp-internals-courseに挑戦した話 その１

こんにちは、3日目の投稿になります。

# Introduction

みなさんは[rakudo-and-nqp-internals-course](https://github.com/edumentab/rakudo-and-nqp-internals-course)を知っていますか?
rakudo-and-nqp-internalsはMoarVMの開発者である[Jonathan Worthington氏](http://www.jnthn.net)の作ったnqpとrakudoの内部構造に関するチュートリアルです。

スライドだけじゃなくて、エクササイズ問題も用意されています。
http://edumentab.github.io/rakudo-and-nqp-internals-course/

というわけで、詳しい話はスライドを見てもらうとして、今回のアドベントカレンダーでは私がエクササイズ問題に挑戦していきたいと思います。

3日目はExercise 1に挑戦します。

# Exercise 1

## 1.1 Hello, World

+ 初めの儀式、Hello, worldをしてみましょうという問題です。

```
!# nqp

say('Hello, world');
```

## 1.2 Variables

+ スカラ、配列、ハッシュに対して値を代入していきましょうという問題です
+ 代入(=)じゃなくて束縛(:=)つかっていくのがポイントです。

```
!# nqp

my $a := 10;
say($a);

my @b;

@b[0] := 10;

my @c;

@c := [1,2,3];

say(@c[0]);
say(@c[1]);
say(@c[2]);

my $scalar;

$scalar := [1,2,3];

my %h;

%h<key> := 10;
%h{'key'} := 10;
```

## 1.3 Loops

+ 配列、ハッシュに対してループ処理を書いてみましょうという問題です
+ 配列のループのところで一度に2つの値を見るのはこんな感じの解き方であってるのかなあというのだけ気になりました (#1)

```
!# nqp

my @a := [1,2,3];

for @a {
  say($_);
}

for @a -> $val {
  say($val);
}

my @b;
@b[0] := [1,2];
@b[1] := [2,3];

for @b { # (#1)
  say($_[0]);
  say($_[1]);
}

my %h<key> := 10;

for %h {
  say($_.key);
  say($_.value);
}

```

## 1.4 Subroutines

+ 階乗を表現する関数を書いてみましょうという問題です。
+ 特にnqp独特の部分はなさそうです。

```
!# nqp                                                                                                                                                      
                                                                                                                                                            
sub fac($a) {                                                                                                                                               
  $a == 0 ?? 1 !! $a * fac($a - 1)                                                                                                                          
}                                                                                                                                                           
                                                                                                                                                            
say(fac(10));  
```

## 1.5 Classes

+ 下記のような複数の問題になっています
  + BarTabクラスをつくって、newメソッドを書いてみる
  + `$!num-tables`, `@!items`といったアトリビュートを書いてみる
  + `$!num-tables`のゲッターの`table()`メソッドを書いてみる
  + `$name`と`$price`を受け取って`@!items`の中身を更新する`add_order`を書いてみる
  + `@!items`の中身を文字列で提示する`render_tab()`を書いてみる

+ `nqp::create()`, `nqp::list()`, `nqp::push()`が見慣れないかもしれません
  + [nqpのページにドキュメントがある](https://github.com/perl6/nqp/blob/master/docs/ops.markdown)ので、詳しくはそれを参照すると良いでしょう。

+ 問題文に`BUILD`を使いなさいとは書いてなかったかもしれませんが、オブジェクトを`nqp::create`で作るのは`new`に任せて、いろいろな初期化作業は`BUILD`にやらせる感じにしてみました。

```
!# nqp                                                                                                                                                      
                                                                                                                                                            
class BarTab {                                                                                                                                              
  has int $!num-tables;                                                                                                                                     
  has @!items;                                                                                                                                              
                                                                                                                                                            
  method new(:$table!) {                                                                                                                                    
    my $obj := nqp::create(self);                                                                                                                           
    $obj.BUILD(:table($table));                                                                                                                             
    $obj                                                                                                                                                    
  }                                                                                                                                                         
  method BUILD(:$table!) {                                                                                                                                  
    $!num-tables := $table;                                                                                                                                 
    @!items := nqp::list();                                                                                                                                 
  }                                                                                                                                                         
  method table() {                                                                                                                                          
    $!num-tables                                                                                                                                            
  }                                                                                                                                                         
  method add_order(str $name, int $price) {                                                                                                                 
    my %h;                                                                                                                                                  
    %h<name> := $name;                                                                                                                                      
    %h<price> := $price;                                                                                                                                    
    nqp::push(@!items, %h);                                                                                                                                 
  }                                                                                                                                                         
  method render_tab() {                                                                                                                                     
    my $total := 0;                                                                                                                                         
    for @!items {                                                                                                                                           
       say($_<name>);                                                                                                                                       
       say($_<price>);                                                                                                                                      
       $total := $total + $_<price>;                                                                                                                        
    }                                                                                                                                                       
    say($total);                                                                                                                                            
  }                                                                                                                                                         
}                                                                                                                                                           
                                                                                                                                                            
my $bar-tab := BarTab.new(table => 42);                                                                                                                     
say($bar-tab.table);                                                                                                                                        
$bar-tab.add_order("name", 1000);                                                                                                                           
$bar-tab.render_tab();                                                                                                                                      
```

## 1.6 Multi-methods

+ `add_order`をmulti-methodに変更しましょうという問題です。
+ `int`や`str`のような型を引数にしていするとうまく動かない(スライドにも書いてある)ので注意が必要です。

```
!# nqp                                                                                                                                                      
                                                                                                                                                            
class BarTab {                                                                                                                                              
  has int $!num-tables;                                                                                                                                     
  has @!items;                                                                                                                                              
                                                                                                                                                            
  method new(:$table!) {                                                                                                                                    
    my $obj := nqp::create(self);                                                                                                                           
    $obj.BUILD(:table($table));                                                                                                                             
    $obj                                                                                                                                                    
  }                                                                                                                                                         
  method BUILD(:$table!) {                                                                                                                                  
    $!num-tables := $table;                                                                                                                                 
    @!items := nqp::list();                                                                                                                                 
  }                                                                                                                                                         
  method table() {                                                                                                                                          
    $!num-tables                                                                                                                                            
  }                                                                                                                                                         
                                                                                                                
  proto method add_order($name, $price, $quantity?) { * }                                                                                                   
  multi method add_order($name, $price) {                                                                                                                   
    my %h;                                                                                                                                                  
    %h<name> := $name;                                                                                                                                      
    %h<price> := $price;                                                                                                                                    
    nqp::push(@!items, %h);                                                                                                                                 
  }                                                                                                                                                         
  multi method add_order($name, $price, $quantity) {                                                                                                        
    my $i := 0;                                                                                                                                             
    while $i < $quantity {                                                                                                                                  
      my %h;                                                                                                                                                
      %h<name> := $name;                                                                                                                                    
      %h<price> := $price;                                                                                                                                  
      nqp::push(@!items, %h);                                                                                                                               
      $i := $i + 1;                                                                                                                                         
    }                                                                                                                                                       
  }                                                                                                                                                         
  method render_tab() {                                                                                                                                     
    my $total := 0;                                                                                                                                         
    for @!items {                                                                                                                                           
       say($_<name>);                                                                                                                                       
       say($_<price>);                                                                                                                                      
       $total := $total + $_<price>;                                                                                                                        
    }                                                                                                                                                       
    say($total);                                                                                                                                            
  }                                                                                                                                                         
}                                                                                                                                                           
                                                                                                                                                            
my $bar-tab := BarTab.new(table => 42);                                                                                                                     
say($bar-tab.table);                                                                                                                                        
$bar-tab.add_order("name", 1000);                                                                                                                           
$bar-tab.render_tab();                                                                                                                                      
```

## 1.7

+ 解いてないです:)


以上、3日目の投稿でした。


# ライセンス
rakudo-and-nqp-internals-course is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.