2015年5月23日土曜日

モナドまとめ

これまで List,State,Maybe の3種類のモナドを見てきたが、結局のところモナドでは表向きは

m >>= f >>= g >>= h
 
と単純に左から右に値を流しているように見せかけて(ドットによる通常の結合と逆向きなのは何か深い意味があるんだろうか)実のところは bind によりリストの展開とか、状態の受け渡しとか、条件による処理の変更などを行っている。

こういった関数の間の繋ぎをするのがモナドで、やはり目的としては「どう繋がるか」ではなく「何と何が繋がっているのか」を明確にしたいというのがあるのだろうか。そのために、「どう繋がるか」の部分はモナドにより ">>=" 記号の中に埋め込まれるというのが「文脈に意味を持たせる」ということなのだろう。

本当に状況によっていろんなことが出来るので、一口にモナドはこんなもんだと簡単に説明できないのはよくわかった。

まだ遅延評価や型などもあるが、とりあえずこんなもんで関数型言語についてなんとなくは理解できたかな。
しばらくは Haskeller になるつもりはないが、最近C++やC#なんかでもラムダ式とかに対応し始めたので、うまく使えそうな機会があれば活用してみようと思う。

2015年5月20日水曜日

Maybe モナド

エラーを扱う

MaybeモナドはStateモナドに比べるとやりたいことはシンプルだ。

ある関数fに入力がある範囲なら処理結果を出力させそれを後続の関数gの入力とし、範囲外ならエラーとして処理を打ち切りたいとする。



普通に考えればせっかくタプルがあるのだから、片方に結果を入れて、もう片方に結果が有効かを示す情報を格納すればよい。さすがに関数型でも手続き型でも後続の関数gの中で入力エラーをチェックするのはナンセンスなので、fgの接合部分でエラーチェックをしてgを呼び出すか処理を終了するか決めるのが自然だ。

そうするのが自然なのだが、Cでドライバのコード書いた経験があるならわかると思うが、この手のエラーを出力するかもしれない関数を並べると、エラーチェックのためのif文が大量に並んで非常にコードが見づらくなる。普通の神経の人ならぶち切れてマクロを定義して無理矢理1行に納めるが、それでも見栄えはよくない。

C++やJavaのように例外が扱えるとこの手のコードはきれいに書けるが、関数型言語では例外は扱わないようだ。本では例外が危険だからと書いてあったが、個人的には前述の「制御を分割して部品化する」といった都合上、例外が扱いにくい(下位にどんな関数が来るかわからないし、制御を分割しているのでどこで例外を待てばいいのか決めにくい)仕様になっているんじゃないかと思っている。

そこで、Maybeモナドの出番となる。

2つのbind関数

ListモナドとStateモナドはそれぞれ"List", "State"のキーワードによりバインド関数が生成されるが、Maybeモナドでは1つのモナドで"Just"と"Nothing"の2つのキーワードを持っている。(逆にMaybeのくせに"Maybe"ではバインド関数は生成されない)

下の図のように、Justの場合はバインドされた関数gを取り込んでfの戻り値を渡すbind関数を生成する。Nothingの場合はバインドされた関数は無視してそのままNothingを返すbind関数を生成する。
最終的な出力にはNothingを含むのでこれもMaybeモナドとなり、この後にさらに別の関数をバインドしていってもNothingの場合はひたすらNothingが伝搬していくことになる。

理屈がわかったところでこれまで同様JavaScriptで書いてみる。

var Maybe = {
  return: function(a) {
    return Maybe.Just(a)
  },
  Just: function(a) {
    return {
      bind: function(f) {
        return f(a);
      },
      inner: a
    }
  },
  Nothing: function() {
    return {
      bind: function() {
        return Maybe.Nothing();
      },
      inner: 'Nothing'
   }
  }
}

var f = function(a) {
  if(a < 0) return Maybe.Nothing();
  else return Maybe.Just(a * a);
}

var g = function(a) {
  return Maybe.Just(a * 2);
}

document.write(f(1).bind(g).inner+'<br>');
document.write(f(-1).bind(g).inner+'<br>');

fの引数が0以上ならJustにより生成されるbind関数でfの結果がgに渡される。0未満ならNothingにより何もせずにNothingを返すbind関数が生成され、Nothingが継承されていく。

Maybe.JustやMaybe.Nothingはなんか語呂が悪いので、Maybeの外に出すついでにinnerではなくvaluOfを利用するとすっきり書ける。

もちろん前回のdo記法もどきもちゃんと使える。


var Maybe = {
  return: function(a) {
    return Just(a)
  }
}
var Just = function(x) {
  return {
    bind: function(f) {
      return f(x);
    },
    valueOf: function() {
      return x;
    }
  }
}
var Nothing = function(x) {
  return {
    bind: function() {
      return Nothing();
    },
    valueOf: function() {
      return 'Nothing';
    }
  }
}

var f = function(a) {
  if(a < 0) return Nothing();
  else return Just(a * a);
}

var g = function(a) {
  return Just(a * 2);
}

document.write((function() {
  var a, b;
  return MonaDo(f(2),
  [
    function(x_) { a = x_; return g(2); },
    function(x_) { b = x_; return Maybe.return(a + b); }
  ]);
}())+'<br>');


2015年5月18日月曜日

State モナド

何が問題なのか

  1. 状態だけを入出力する関数を作っても意味が無い
    なんとなく状態を入力としてそれに基づいて新しい状態を出力する関数を作って繋げていけば関数型プログラムでも状態の変化を表現できそうだが、関数型プログラムは副作用を及ぼさないため本当に状態だけぐるぐる回ることになりこれでは意味が無い
  2. タプルは直接繋げられない
    じゃあ、タプルを使えば状態と入出力データをペアで扱えると思うが、f→[タプル]→gと直接繋げることは出来ずf→(a,s)→gと一旦変数に格納しないといけない
  3. 変数の破壊的代入が出来ない
    別にタプルを変数に格納しても問題ないように思えるが、関数型プログラムでは変数の破壊的な代入が出来ないため、f→(a,s)→g→(b, s')→h→(c,s'')→と毎度状態を示す変数sをリネームしていかないといけなくなる

裏で状態の受け渡しを行う

そこで、Stateモナドでは表向きは入出力データの受け渡しのみを行い、その裏でStateモナドが状態の受け渡しを行う。
具体的な動作を理解するため、前回と同じく同様の動作をするコードをJavaScriptで書くと以下のようになった。

var State = {
  return: function(a) {
    return State.State(function(s) {
      return Touple(a, s);
    });
  },
  State: function(x) {
    return {
      bind: function(f) {
        return State.State(function(s) {
          var tmp = x(s);
          return runState(f(tmp.lhs), tmp.rhs);
        });
      },
      inner: x
    }
  }
}

var runState = function(state, a) {
  return state.inner(a);
}

var Touple = function(left, right) 
{
  return { lhs: left,rhs: right }
}

基本的には前回のListモナドと同じだが、Stateモナドの場合コンストラクタの引数(?)は関数で、これは値aと状態sを引数としてこれから新しい値a'と新しい状態s'のタプルを生成する関数f
f(a,s) -> (a',s')
があったとして、これを第一引数aでカリー化した関数f'
f'(s) -> (a',s')
を与えて生成する。
例えば、

var f = function(a) {
  return State.State(function(s) {
    return Touple(a+s, s+1);
  });
}

var g = function(a) {
  return State.State(function(s) {
    return Touple(a*s, s+a);
  });
}

var t = runState( f(1).bind(g), 2);

とすれば、
(a=1, s=2) →f()→(a'=a+s=3, s'=s+1=3)→g()→(a''=a'*s'=9,s''=s'+a'=6)
と期待通りの結果が得られる。

また、runStateはbindにより生成されたStateモナド内部の関数を実行して出力と状態を得る関数になる。

作りそのものはListモナドとあまり変わらないのだが、動きは非常に複雑なので頑張って図示すると以下のようになる。

まず関数fを入力aでカリー化してsを入力として(a',s')を出力する関数f'を内部関数として持つStateモナドを生成する。
これに関数gをバインドして新しいStateモナドを生成する。このモナドはbind関数の中で生成される関数(仮にinnerとする)を内部関数として持ち、innerはf'とgを内部に持っている。
このモナドに対して状態sを与えてrunStateすると、inner関数が呼び出される。inner関数では、状態sをf'に適用して、a'とs'を得、今度はa'をgに適用する。
gはfと同じくa'でカリー化したg'を持つStateモナドを生成する。
inner関数ではこの生成されたStateモナドに対して先ほどの状態s'を与えてrunStateすると最終的にg'にs'が適用されることになる。

このように、表向きはfとgの間では入出力だけをやりとりしているように見せつつ、StateモナドがrunStateを使って裏で状態の受け渡しを行っている。

今回もわかりやすいようにStateオブジェクトにbindをぶら下げたが、関数オブジェクトにbindをぶら下げればちょっとシンプルにかける。

var State = function(x) {
  x.bind = function(f) {
    return State(function(s) {
      var tmp = x(s);
      return runState(f(tmp.lhs), tmp.rhs);
    });
  }
  return x;
}
State.return = function(a) {
  return State(function(s) {
    return Touple(a, s);
  });
}

var runState = function(state, a) {
  return state(a);
}

もののついでなので、get/put/gets/modify/push/popも実装してみた。大体ルールがわかってきたので、Haskellのコードをそのまま置き換えれば動いた。

var get = function() {
  return State(function(s) {
    return Touple(s, s);
  });
}

var put = function(s) {
  return State(function() {
    return Touple(null, s);
  });
}

var gets = function(f)
{
  return get().bind(Combine(State.return, f));
}

var modify = function(f)
{
  return get().bind(Combine(put, f));
}

var Concat = function(val, arr) 
{
  var newArr = new Array(arr.length+1);
  newArr[0] = val;
  for(var i = 0; i < arr.length; i++) {
    newArr[i+1] = arr[i];
  }
  return newArr;
}

var Head = function(arr) {
  return arr[0];
}

var Tail = function(arr) {
  var newArr = new Array(arr.length-1);
  for(var i = 0; i < newArr.length; i++) {
    newArr[i] = arr[i+1];
  }
  return newArr;
}

var push = function(a)
{
  return modify(function(arr) 
  {
    return Concat(a, arr);
  });
}

var pop = function()
{
  var value;
  return MonaDo(gets(Head),
  [
    function(x_) { value = x_; return modify(Tail); },
    function(x_) { return State.return(value); }
  ]);
}

var t = runState( pop(), [1,4,5]);

popについては展開するのが面倒なのでさらについでにdo記方も作ってみたが、これもほぼほぼHakellの定義通りで出来る。(letはそのままは無理だがそれなりに書きようはある)

var MonaDo = function(m, fs) {
  if(fs.length > 2) {
    return m.bind(function(a) {
      return MonaDo(fs[0](a), fs.slice(1));
    });
  } else {
    return m.bind(function(a) {
      return fs[0](a).bind(fs[1]);
    });
  }
}


2015年5月16日土曜日

モナドとは

関数を繋ぐ

読んだ本にはモナドとは「文脈を持つ計算を扱う」ための仕組みとあるが、何を言っているのかさっぱりわからない。

ネットでもいろいろ勉強した結果、今のところの自分の理解だとモナドは関数を繋げるためのものだと解釈している。(間違っているかもしれないが)

単に関数を繋げるだけならHaskellにはすでに前回のCombineに相当する演算子が用意されている。これを使えば、単純に「 g . f x 」と書けば「 g(f(x)) 」と等価な処理に変換してくれる。

この場合は、単純に下図のように関数fの出力をそのまま関数gの入力として利用する。
 しかしながら、必ずしも毎回出力と入力が一対一対応するとは限らない。時には間に変換を挟んだりする必要が出てくる。そこでモナドが登場する。
 
イメージ的には下図のように先行の関数は出力の値を含んだモナドを出力して、モナドは必要により値を変換して後続の関数に渡す。
具体的な動きについて例としてListモナドを利用して見てみる。モナドの例としてはMaybeモナドがよく挙げられているが、Maybeモナドは動きがトリッキーなので個人的にはListモナドの方が入りやすいと思う。
 

Listモナド

Listモナドでは配列の要素を入力として配列を返す関数を想定している。例えば、値aを入力として、それを[a,a]という2要素の配列に変換する関数fがあったとして、これを前回のmapを利用して配列の各要素に適用すると、
 
f(1) → [1, 1]
map(f)([1,2,3])  → [[1,1],[2,2][3,3]]
 
このように、各要素にfを適用した戻り値の配列が配列として格納されるため、配列が入れ子になってしまう。
これの出力を別の関数gに同じくmapで適用するためには、入れ子を外して

[1,1,2,2,3,3]
 
という配列に変換する必要が出てくる。

Listモナドはこのmapと変換を一括してfとgの接続部分で行ってくれる。個人的にはこの変換に需要があるかはよくわからないが、Listモナドが用意されていると言うことは関数型言語の世界ではそれなりに需要があるのだろう。

Listモナドの挙動を理解するために、試しにJavaScriptで正確かどうかはわからないがそれっぽい挙動をするコードを書いてみた。

var List = function(array) {
  return {
    // (>>=) = concatMap に相当
    bind: function(f) {
      return List(concatMap(f)(array));
    },
    // 中身の配列を取得
    inner: array,
  }
}
// return = (:[]) に相当
List.return = function(a) {
  return List([a]);
}

var concatMap = function(f) {
  return function(array) {
    var newArray = [];
    for(var i = 0; i < array.length; i++) {
      var tmp = f(array[i]);
      newArray = newArray.concat(tmp.inner);
    }
    return newArray;
  }
}

これで、f,gを次のように定義して適用すると、期待通り入れ子を外した配列が出来上がる。

var f = function(a) {
  return List([a, a+1]);
}

var g = function(a) {
  return List([a, a*3]);
}

// f >>= g に相当
document.write( f(1).bind(g).inner + '<br>' );

自分の理解で作ったJavaScriptなのでHaskellと同じことになっているかはわからないが、これからListモナドの動作を見ていくと、まずListモナドでは前の関数の出力aを持ったbind関数を生成する。bind関数では右辺に来る関数を受け取ってこれと持っているaを組み合わせて出力(モナド)を生成する。
絵的に書くと下のようになるだろうか。
Listの場合はモナドを使わずにconcatMapだけで繋げることも出来るが、この場合は
concatMap(g)(concatMap(f)(1))

とconcatMapの方が目立ってしまうので、関数型言語ではモナドを使うことで間の変換処理を隠蔽してfとgの関係性を見えやすくしたということだと思う。

ちなみに、うえの説明ではわかりやすさのためにListオブジェクトにbind関数をぶら下げたが、Array.prototypeに追加することでよりシンプルに使えるようになる。

Array.prototype.bind = function(f) {
  return concatMap(f)(this);
}

var List = {
  return: function(a) {
    return [a];
  }
}

var f = function(a) {
  return [a, a+1];
}

var g = function(a) {
  return [a, a*3];
}

document.write( f(1).bind(g)+'<br>' );

モナドの大まかな挙動がわかったところで、今後はより複雑なMaybeモナドとStateモナドについても理解していく。

2015年5月13日水曜日

関数型言語をかじってみた

プラモのパーツが届かないこともあって、GW中の暇つぶしに「関数プログラミング実践入門」という本を買って関数型言語を勉強してみた。

関数型言語とは

いまさら書くまでもないが、関数型言語における関数は数学的な関数と同じで必ず入力により出力が一意に決まるものになっている。

CやJavaのような手続き型の言語における関数では関数内でstaticやglobal変数等アクセスすることが許されている。この結果として同じ入力を与えても出力が異なることが得る。このようにstaticやglobalな変数にアクセスすることを副作用と呼び関数型言語の関数ではこのような副作用を及ぼすことは許されていない。

このような考え方はオブジェクト指向目指す方向とは逆行しているように思える。オブジェクト指向におけるメソッド(関数)は基本的にはそのメソッドが属するオブジェクトのプロパティを参照もしくは変更するためのものなので、その関数から見て外部の状態を参照/変更するという副作用を及ぼすことを目的として作られているといっても過言ではない。

なぜオブジェクト指向がそうなっているかというと、端的に言えばデータの塊(オブジェクト)とそれに対する働きかけ(メソッド)をペアで取り扱うことで、複雑なデータ構造をよりシンプルな表現で操作できることを目指して作られているからと言える。

このため、手続き型言語で作った関数を単純に入出力を全て引数と戻り値で受け渡しするように変えれば関数型のプログラムになるかというと必ずしもそうではない。これだけでは単に引数と戻り値が大量にあって見づらいだけの手続き型プログラムの関数にしかならない。

制御を分割する

関数型言語では前述のような「副作用を及ぼさない」という性質が注目されがちだが、個人的には関数型言語で重要な点は制御の部品化にあると感じた。これについて、読んだ本の例をベースに自分なりの解釈も含めて例を書き直してみる。

お題となっている処理は、頂点座標の移動と回転を組み合わせる処理になる。これをJavascriptで従来通りの手続き型で書くとこのようになる。

for(var i = 0; i < points.length; i++) {
  var p = move(points[i]);
  newPoints[i] = rotate(p);
}

moveとrotateはそれぞれ頂点の移動と回転を行う処理だが、細かい説明をしなくても何の変哲も無いどこでも見かけるようなコードだと思う。

これに対して同じくJavascriptで関数型っぽく書くと以下のようになる。

newPoints = map(combine(rotate, move))(array);

ここで、mapとcombineは以下のように定義されている。

var map = function(f) {
  return function(array) {
    var newArray = new Array(array.length);
    for(var i = i; i < array.length; i++) {
      newArray[i] = f(array[i]);
    }
    return newArray;
  }
}

var combine = function(f, g) {
  return function(x) {
    var tmp = g(x);
    return f(tmp);
  }
}

関数を返す関数なので慣れないと直感的にわかりにくいかもしれないが、単にループと関数の結合を行う処理になっている。

手続き型のプログラムでは「処理Aの結果を処理Bに代入する」、「これを配列の全要素に行う」といった制御をコードとしてべた書きしていたものを、関数型のプログラムではそれぞれを関数(部品)として部品の組み合わせで目的とする制御を表現することが出来るようになっている。

そう言うと手続き型の関数だって部品じゃないのかと思うかもしれないが、手続き型の関数は言ってしまえば制御と処理をごちゃ混ぜにして一塊にしたもの、いわゆるカプセル化であって、関数型のように「ループ」、「処理Aの後に処理Bを行う」といった細かい制御の単位で部品化出来るわけではない。

実際問題上記の手続き型の例を手続き型プログラムの手法で部品化しようとしても、for文全体を一つの関数にするぐらいしかやりようがない。これでは中の処理を変えようとしたら関数の中身を変える必要があるし、for文の部分だけ再利用するなんてことは普通の手続き型プログラムでは出来ない。

部品化のメリット

このように制御を部品化する一つのメリットとしてはコードの再利用性が向上することがあげられる。おそらく手続き型言語のプログラマなら人生で数千回もfor文を書いていただろうが、これがmapという部品として再利用出来るようになる。

しかしながら、もっと大きなメリットとして検証の容易さがある。このように制御を小さくて単純な部品に分割していくことで、個々の部品に対する検証は容易になる。そして、品質が担保された部品を組み合わせていくことで全体として高品質なプログラムを組み上げることが出来る。

こう言うと勘のいい人なら「ちょっとまて」と思うかもしれない。検証や物作りの常識からいって高品質な部品を組み合わせたからといって高品質になるとは限らない。そこで重要になるのが最初に述べた「副作用を及ぼさない」という関数型プログラムの性質になる。

副作用を及ぼす部品を組み合わせるとお互いの副作用が干渉し合って単体では発生しなかった問題が発生する恐れがある。このため、副作用があることを前提とした世界では部品Aと部品Bを組み合わせて新しい部品Cを作ると、部品CはA,B含めた全体としてもう一度検証し直す必要がある。当然、どんどん部品を組み合わせていって大きな部品になってくるとその分検証の規模が増え中身も複雑になる。

一方で、関数型プログラムでは部品(関数)は副作用を及ぼさないため部品Aと部品Bを組み合わせても部品AとBの品質に変化は起きえない。このため、これらを組み合わせて部品Cを作っても、あくまでも部品Cとして新たに作った部分(たとえば部品の組み合わせ方は適切かとか、使う部品は合っているかとか)のみ検証すればよいことになる。

このように関数型プログラムでは、
  • 大きなプログラムを小さな部品に分割する
  • 小さくて単純な部品にすることで検証を容易にする
  • 単純な部品を組み合わせて複雑な処理を実現する
    (副作用を及ぼさないため、組み合わせても品質は落ちない)
という作り方をして初めて本当の意味で関数型プログラムと言えるのだと理解した。

で、どうやって部品を繋げていくのかという話からモナドについて書こうと思ったが、長くなりそうなのでまた別の記事で

2015年5月11日月曜日

武蔵を作る(完)

発注していた機銃がようやく届いたので仕上げに入る。
天下のAmazon先生でもゴールデンウィーク中は配達に時間がかかったようだ。


キットのパーツの場合は何も考えずに説明書に従って取り付けていけばいいが、自分でパーツ取り寄せてカスタマイズしようと思うと本当にこれでいいのか悩む。
とはいえ、資料調べてなんてやる根性もないのでとりあえずはこれでいいことにして進める。

んでまあ、さくっと完成
他の機銃もこれに換装しようかと思ったが、エアブラシもしまってしまったし今更面倒なのでこれで終了にする。
余った分は今度作るときに有効活用しよう。

せっかくなので手持ちの他の艦と比べてみるとやっぱりでかいね。

 重巡の高雄はともかく、同じ戦艦の伊勢(初期型)よりも二回りほど大きい。

ただ、大きいこともあってか甲板上はかなりすっきりしているイメージ。個人的には高雄の艦橋周りのごてごて感が好きだ。

最後に、キットを組んだ感想としては、初心者向けなのか上級者向けなのかよくわからない感じだった。純粋にキットだけを見れば精度は高いしディティールも細かいし、その割には部品点数も少ないので、今回はエッチングパーツ付きなので時間がかかったが、プラキットのみならもっと手軽に早く作れたと思う。

なので最初は有名な艦だし初心者向けのキットなのかなと思ったが、作っていくと塗り分けとか細かいところで説明書の指示に抜けているところが何カ所かあって、これまで幾つか作ってきた人なら経験とカンで乗り切れると思うが、初めて作る人にとっては不親切だと思った。加えて、パーツの番号が通し番号(普通はAの何番、Bの何番とかいう感じにライナー毎に分かれているものが全て通しになっている)になっているうえに番号順に並んでいるわけでもないので非常にパーツが探しにくかったのも厄介だった。

とは言え、出来映えには満足です。

2015年5月3日日曜日

武蔵を作る(完?)

連休中なのでラストスパート。

できあがった艦体をウェザリング。
ウェザリングは素人が下手にやると台無しになるので、タミヤウェザリングマスターの日本海軍艦船用を使用した。
これだけでも雰囲気は出るよねー。とはいえどこかでちゃんとしたやり方を練習せねば・・・

艦載機の塗装。
説明書には塗り分けの指示は見当たらなかったが、どうせいつもの色だと思うので、上面に暗緑色(三菱系)、下面に明灰白色をエアブラシで塗った後、キャノピーにつや消し白、エンジン部分につや消し黒、プロペラをシルバーで筆塗り。
デカール張りはマークソフターが大活躍。もっと前に買っておけばよかった。

できあがった艦載機を飛行甲板に配置。
ちょうどいいのでデカールが切れてしまったところをうまく隠すように配置してみる。
あれ?艦載機って前向きだっけ?後ろ向きだっけ?

写真では見えにくいと思うが、最後にデカールと張り線をつけて完成。
張り線にはメタルリギングの0.1号を使用。高いけど扱いやすい。

これで無事完成・・・と思いきや。
主砲上部の機銃が足りない。
どうやらエッチングパーツセットのくせに、エッチングパーツで追加された銃座用の機銃がついていない模様。
しょうがないので機銃を追加で発注中。これが届いてからが本当の完成です。

2015年5月2日土曜日

武蔵を作る(11)

いよいよ艦体にパーツを取り付けていく。
まずは手すりを接着。
長いものもあるので、小さく切ったマスキングテープで仮止しつつテープのないところに瞬間接着剤をつけていく。
取り付けてみたところ、明らかにA75のパーツの長さが足りない。
余ったA76の長さがちょうどぴったりだったので取り付けておいたが、果たしてこれでよかったのか。
 
その後、砲塔と艦橋を手すりに当たらないように気をつけつつ取り付け。
だいぶらしくなってきた。
主砲と副砲はポリキャップに突き刺すようになっているのだが、上にエッチングパーツがついていると非常に差し込みづらい。主砲は大きいのでなんとかなったが、副砲はあきらめて足を切って接着した。

残った細かいパーツ群の塗装。

ボートの方は説明書に特に指示がなかったので、デッキと同じセールカラーを塗った。

頑張って一気に取り付け。

 
スクリューもこの段階で取り付ける。
 
最後に菊の御紋を取り付ける。
 
これで本体部分はひとまず完成。
でもまだ艦載機とかウェザリングとか張り線とかが残ってる。