SVX日記

2004|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|
2021|01|02|03|04|05|06|07|08|09|10|11|12|
2022|01|02|03|04|05|06|07|08|09|10|11|12|
2023|01|02|03|04|05|06|07|08|09|10|11|12|
2024|01|02|03|04|05|06|07|08|09|10|11|

2023-11-10(Fri) CoffeeScript2にてMixinを試す

  ここのところ、ものスゴい学習欲と、それを越えるほどの課題が湧き上がってきて頭を休める間もない。WebAssemblyからSIMD命令(MMX/SSE/AVX)、浮動小数点演算、CPUキャッシュに飛び火し、なぜか線形代数の学び直しから、3DCG、AIに至るまで。

  こうなったら、ポリゴンのレンダラを経て、レイトレーシングのレンダラまで書いてみたくなってきてしまっているのだが、まずは当面の「WebAssemblyによるイメージ操作」を片付けなければならない。

  しかし最終的にアニメーションさせたいなら、以前に作ったシューティングゲームの既存のフレームを使ったほうがいい、ということになり、久々に引っ張り出してくると……動かねぇ。

  どこが引っかかっているのかと思ったらMixinだ。なぜか以前に書いたMixinの仕組みが動かない。まぁJavaScriptとしても、CoffeeScriptとしても、Mixinが正式な言語仕様として組み込まれているわけではないからな、と思いつつ、調べていくと意外と根が深い。

  そもそもJavaScriptにはクラスが存在しない。いや、最近まで存在しなかった。CoffeeScriptで書いていたので気づかなかったが、クラスが正式な言語仕様として組み込まれているわけではないところに、prototypeとかいう仕組みを通じてCoffeeScriptによりコネあげられていたのだ。ところが、最近になってJavaScriptにクラスの仕組みが組み込まれたので、CoffeeScript2からはコネあげをやめ、その仕組みをそのまま利用するようになった。

  それだけならよかったのだが、クラスでメソッドを定義するとprototypeに登録されないようなのだ。先のMixinの仕組みはprototypeを通じてコネあげられていたので、それができなくなってしまった、と。

// コネあげ版クラス(JavaScript)
const Animal = (function() {
    function Animal(name) {
        this.name = name;
    }
    Animal.prototype.walk = function() {
        console.log(this.name, 'walking.');
    };
    return Animal;
})();
// ネィティブクラス(JavaScript)
const Animal = class Animal {
    constructor(name) {
        this.name = name;
    }
    walk() {
        console.log(this.name, 'walking.');
    }
};
// どちらも同様に動くのだが
console.log(Animal.prototype);
const pochi = new Animal('Pochi');
pochi.walk();
// コネあげ版クラスだとprototypeにwalkがあるのに
$ node diff_func.js
Animal { walk: [Function] }
Pochi walking.
// ネィティブクラスだとprototypeがカラ
$ node diff_class.js
Animal {}
Pochi walking.

  じゃ、どーすればいいのかというと、こうすればいいらしい。

const Flying = (base) => class extends base {
    fly() {
        console.log(this.name, 'flying.');
    }
}
class Bird extends Flying(Animal) {};

  以前は「mixOf base, mixin」という以下のような書き方だったが「Flying(Animal)」って書き方も悪くはないなぁ。従来のmixOf関数の定義は必要なくなる。上記の定義自体がMixinを行う関数になっている。

class Bird extends mixOf Animal, Flying

  つうわけで、CoffeeScriptで書き直すとこうだ。

'use strict'
 
class Animal
    constructor: (name) ->
        @name = name
    walk: ->
        console.log(@name, 'walking.')
 
class Human extends Animal
    talk: ->
        console.log(@name, 'talking.')
 
Flying = (base) -> class extends base
    fly: ->
        console.log(@name, 'flying.')
 
Ejecting = (base) -> class extends base
    eject: ->
        console.log(@name, 'ejecting.')
 
class Bird extends Flying(Animal)
 
class Pilot extends Ejecting(Flying(Human))
 
pochi = new Animal('Pochi')
pochi.walk()
 
taro = new Human('Taro')
taro.walk()
taro.talk()
 
pichan = new Bird('Pi-chan')
pichan.walk()
pichan.fly()
 
tom = new Pilot('Tom')
tom.walk()
tom.talk()
tom.fly()
tom.eject()

  実行結果は以下。

$ node mixin.js
Pochi walking.
Taro walking.
Taro talking.
Pi-chan walking.
Pi-chan flying.
Tom walking.
Tom talking.
Tom flying.
Tom ejecting.

  完全に「ヤクの毛刈り」である。しかし、なんとか刈り終えた。先に進むとしよう。