RPGツクールMZのプラグイン制作予習編(仮)

タシラカ

ユーザー
誤報に関してはTouchFuzzy氏や我々の早とちりだったって事で責める気もありませんが(公式も明言していないし)
全面書き直しであればclass構文を敢えて使わない理由が思い浮かばないので、
やはり流用をベースに一部(戦闘とコアを中心に)改造した、と解釈した方が自然ですか

ES6で書き直してくれって要望は3年以上前からあって
当時は化石ブラウザの後方互換性とプラグイン作者の学習コストを理由に却下されたんですよね(正確には本家ではなくアツマール向けですが)
2020年、MZ発売はこれ以上ない機会だったんですが…残念ですね。

Core APIのコードに次のようなコードをみかけて、もう不安しかないです。
確かに破壊的なメソッドをMZで新しく作る理由がよく分かりませんね…
JavaScript:
// 要素elementを全て削除
array = array.filter(e => e !== element);
で事足りますし別に処理効率が上がる訳でもなさそう。
というか一要素削除する度に配列生成って逆に非効率過ぎる…プラグイン作成希望の方は重いarray.removeを使わずに↑を使用してください。
VXAceのコアスクリプトではfor文を一つ残らず排除したのに…全検索掛けてfor文がない事に気付いた時は感心しましたよ。
MVで中途半端に復活したfor文も全てeach系メソッドに駆逐されると楽観していましたが…これも皮算用でしょうね。
Archeia氏のあの書き方をみるとコアスクリプト内でアロー関数を一つも使ってない可能性まであります。

唯一朗報があるとしたらHudell氏のこのまとめですか。
あのドキュメントからここまでMVとの比較情報を集められるのは頭が下がりますよ。
MVのドキュメントにもCacheの記述が無かったのでキャッシュハンドラを削除した、と結論付けるのは早計な気がしますがね。
PIXI5に合わせてかなりrpg_core.jsの実装を改善しているみたいですね。
 
最後に編集:

fspace

ユーザー
確かに破壊的なメソッドをMZで新しく作る理由がよく分かりませんね…
JavaScript:
// 要素elementを全て削除
array = array.filter(e => e !== element);
で事足りますし別に処理効率が上がる訳でもなさそう。
というか一要素削除する度に配列生成って逆に非効率過ぎる…プラグイン作成希望の方は重いarray.removeを使わずに↑を使用してください。
一応、in placeなのでメモリ確保まではしていないと思いますが、毎回要素の移動は起きているでしょうね。それよりもindexOfで毎回先頭から検索しているのが問題で、最悪計算量のオーダーがO(n^2)になっている気がします。まったく使い道がないとはいいませんが、これをremoveという簡素な名前で何の説明もなしに配列に生やすのは誤用が怖いですね。そもそもポリフィル以外で組み込み型のプロトタイプを変更すること自体よくないとされていますし、主観かもしれませんがremoveという名前だと先頭要素ひとつの削除のように思えます。まあ、ほとんどのケースでfilterでいいですね。
 

しぐれん

ユーザー
MZのコアスクリプト、クラス構文じゃないらしいですね……。

ES6で書き直したと宣伝していたのにクラス構文すら使っていないということで、海外のフォーラムが荒れてるみたいです。
海外フォーラムが荒れているといっても、あそこは毎回誰かがクレームを付けている印象。
コアスクリプトがES6のクラス構文になっていないとして、誰が迷惑を受けるのでしょう?
prototypeベースのクラスをclass文で継承することは可能なのですから、何の問題もありません。

prototypeベースのクラスはclass文を使用した内容に変換するのは機械的に可能です。
それをしなかったということは、何らかの考えがあっての事でしょう。

またarray.each()はforと比べると処理が遅いので、あえてforを使う選択肢も十分あります。
配列の要素削除による重さの違いは、evalや画像の処理と比べれば誤差レベルです。
 

タシラカ

ユーザー
海外フォーラムが荒れているといっても、あそこは毎回誰かがクレームを付けている印象。
お互いの主観に依る部分もあるので向こうの話はコメントを差し控えましょうか。
少なくとも自分にとってMZ購入を決定づけた最大の動機はES6準拠によるコアスクリプトの書き直しでした。

またarray.each()はforと比べると処理が遅いので、あえてforを使う選択肢も十分あります。
机上の空論でしょう。いくら単体の効率性が高くても人力で最適解を構築しなければ意味がありません。
上記のremoveの話は効率性を意識しないコーディングが目に見えて先行きが不安という話でこれ単体を問題視している訳ではありません。
ツクールMVのコアスクリプトもメモリ管理バグを始めとする重大な見落としから
ループ内での処理の重複と言った地味に重くなる初歩ミスまで大小様々なコーディングミスが報告されてきました。

効率性は重要ですが、多くの衆目に触れるプログラムであれば可読性と保守性はそれ以上に重要です。
簡潔で分かりやすいコードであれば、それだけで自分のミスにすぐに気付けるし、他人が見ても気付きやすくなります。
クラス構文とアロー関数を取り入れるだけで可読性・保守性が改善するのなら取り入れない手はありませんよ。
もっともfspace氏の言うようにコストカット、
即ち外的要因に拠り十全に仕上げるだけの納期を確保出来なかったというのなら苦渋の決断も合点がいきますがね。
まぁ、真意に関しては希望的観測から邪推まで入り乱れるのが目に見えるので詮索しない方が無難です。

最後に、非常に個人的な感想を言えば良い悪い以前にES5コードを見てると気が滅入るんですよ。
読めない、ではなく読みづらい。趣味だからこそ積極的に触りたいとは思えません。
例えるなら100年前の大正小説よりも21世紀の現代小説を普通に読みたい。そんな感覚です。
 

しぐれん

ユーザー
最後に、非常に個人的な感想を言えば良い悪い以前にES5コードを見てると気が滅入るんですよ。
読めない、ではなく読みづらい。趣味だからこそ積極的に触りたいとは思えません。
例えるなら100年前の大正小説よりも21世紀の現代小説を普通に読みたい。そんな感覚です。
最後の一文にすべてが書いてありますね、ES5のコードが嫌いだと。
個人的に自分の好き嫌いのためにもっともらしい理屈を書き連ねるやり方は嫌いです。
ここで「嫌い」と書いたのは人格を否定・攻撃する意図は無く、嫌いというのを説明するために理由を書くよりも、これは個人の主観であるとして争いをそこで打ち切るためです。

私はES5までのprototypeベースのクラスについて、自分が書くのでなければ何の文句も言いません。
ツクールMVのプラグインについてはMVのprototypeで書かれたクラスをclass構文で継承して動かしていますから。
また、VScode用の入力補完ファイルを使っているので、コアスクリプトを直接読む機会は稀です。
もちろん、これを根拠にES5を避けていると主張することは可能だと思いますが、その点はどうでもいいです。
 

WTR

ユーザー
コアスクリプトがES6のクラス構文になっていないとして、誰が迷惑を受けるのでしょう?
prototypeベースのクラスをclass文で継承することは可能なのですから、何の問題もありません。
何の問題もない、というより
問題はあるけどスキルと環境があれば無視できる、と言っているように見えます。

別のスレッドにも書いたような気がしますが
コアスクリプトを教科書だと思って真似ると実はよくない、っていうのがあるとすると、あまり嬉しくないですね。
他に資料あるんだから自分で勉強すればいいっていうのもわかりますけど
あくまでRPGツクールのためにJSを勉強したいだけなので、ちょっと温度差を感じるわけです。

もっとも、愚痴を言ったところで今更コアスクリプトが書き変わるわけじゃないし
建設的とは言い難い、というのは、まぁそうかなと思います。
悪いところを論うより、自分で書くときはこうしたほうがいいよ、っていうのが議論されるほうが
真似するだけの人にはありがたいです。
 

げれげれ

ユーザー
何の問題もない、というより
問題はあるけどスキルと環境があれば無視できる、と言っているように見えます。
えーっと、この場合、本当に「問題がない」んですよね。
定義方法がclass構文であれprototype形式であれ、プラグインを記述する分には
特に何も影響しません。

クラス定義の点に絞って、ちょっと整理させてもらいますね。

[class構文で定義、extendsでの書き換えを試みる場合]
クラスの再定義不可。(MDNにも明記されている
extendsによるクラスの再生成、再代入を行っても、大元のクラスが再定義されるわけではないので、
サブクラスのprototypeチェーンの参照が変わらずに問題が起こる
それに対する明確な打ち手も今のところなし。

[prototype形式での定義、書き換え]
従来通り。
(可読性の点はいったん置いておいて、挙動として)問題なし

[class構文で定義されたクラスをprototypeで書き換え]
class構文で定義されたクラスも、従来式のprototype経由で書き換えることは可能。
そういう意味では仮にclass構文で定義されていても問題なし
(結局、コアがどちらで書かれててもプラグイン作成側には影響しない

[ツクールMZのコアスクリプト]
従来のprototype形式での定義。
書き換えは従来通りのprototype形式で実施すれば問題なし

[プラグイン記述について]
プラグインでのclass構文使用を禁じられているわけではないので、
class構文を使いたい人は使ってよい。使いたくない人は使わなければいい。問題ない


可読性の視点もあるかとは思いますが、そこは各自の言語的出自等によっても
大きく変わってくる部分なので一概に言えないのかと思います。(行き着く先は宗教戦争)
Rubyのようながっつりクラスベースの言語に軸足を置いている方からすれば
JSのprototypeベースの記述がどうにも受け付けがたいのは理解できます。

私としては、上記の情報と課題、そして対応策を整理できた点でも大変有意義なスレッドでした。

(補記)
Arrayの新規メソッドを使わずにfor文を回してる点については今もいささか疑問に感じます。
for文が軽いと言っても、それが配列の先頭から要素数分だけ何度もなぞるような処理の重さを
カバーできるほどの軽さなのか、そして初めから用意されているメソッドを使えば1行で済むものを
わざわざ何行も費やして複雑にしているのは、やはり筋悪ではないかと。
ここは今後のアップデートに期待したいです。


(1点、重要な部分についての記述が抜けていたので08/02、21時に一部追記
 →[class構文で定義されたクラスをprototypeで書き換え])

(08/03、0時 全体的に再度修正)
 
最後に編集:

WTR

ユーザー
えーっと、この場合、本当に「問題がない」んですよね。
問題ない、というのが class構文の話だったのを拡大解釈しすぎてしまったみたいです。すみません。
直前の話題を引き摺って Array.remove の件みたいな、コアスクリプトのちょっと怪しげなところも含めた話題のつもりでいました。
自分で書いたものは常に疑って掛かるので見直すたびに発見があるんですが
人の書いたものはそういうもんか、と安易に考えてしまうクチなんで
これだけコアスクリプトがいまいち…って言われてると教科書の信頼性として心配はあるな、と

[プラグイン記述について]
プラグインでのclass構文使用を禁じられているわけではないので、
class構文を使いたい人は使ってよい。使いたくない人は使わなければいい。問題ない
個人的にはどっちでもいいという事実がややこしくて…
よくわからないけどおまじないだと思って言われたまま書いとくか、みたいなところがないわけじゃないので
違う書き方をされているプラグインがあると
本当にどっちでもいいのか、実は意味が合って違うのか判断つかないんですよね。
なんなら宗教戦争を起こせるくらい宗派に自信を持ちたいものですが
何分理解が浅くてそのとき参考にしたものに逐一影響されるので統一感がなくなるのが悩みです。
 
最後に編集:

剣崎宗二

ユーザー
私の言いたい事は殆どげれげれさんが代わりに言って下さったので本来は更なる言を重ねるつもりは御座いませんでしたが、一点だけ。
今回に於けるArray.removeと、元からあるArray.filterの動作結果は同じでは御座いません。
具体的に言えば、Array.removeは元のArrayに影響を与え、Array.filterは影響を与えません。(これの良し悪しの論は更なる宗教戦争なのでさて置きます)

サンプルコードと実行結果を置いておきます。

JavaScript:
Array.prototype.remove = function(element) {
    for (;;) {
        const index = this.indexOf(element);
        if (index >= 0) {
            this.splice(index, 1);
        } else {
            return this;
        }
    }
};

var base = [1,2,3,2,1]

var a = base.filter(e => e !== 2);
console.log(base);
console.log(a);

console.log('---------------')

var b = base.remove(2);
console.log(base);
console.log(b);
1596358700520.png
 
[class構文で定義]
クラスの再定義不可。(MDNにも明記されている
extendsによるクラスの再生成、再代入を行っても、大元のクラスが再定義されるわけではないので、
サブクラスのprototypeチェーンの参照が変わらずに問題が起こる
それに対する明確な打ち手も今のところなし。
さっき思いついた方法です。
再定義対象のクラスを継承したダミークラスを定義して、ダミークラスのプロトタイプの全プロパティを再定義対象のクラスにマージするという方法です。
多分これはこれで別の問題がありそうなので、普通に従来通りの方法を使ったほうがいいと思います。

【追記】
よく見たら前の書き込みでsetPrototypeOfを使って対応できるってありましたm(_ _)m

【追記2】
>>> 再定義対象のクラスを継承したダミークラスを定義して
よく考えたら、変更したメソッドを再定義対象のクラスにマージするのが目的なので、ダミークラスに再定義対象のクラスを継承させる必要はないですね。

JavaScript:
const Prt1 = function(val) {
    this._val = val;
};
Prt1.prototype.met = function() {
    return this._val;
};
const Prt2 = function(val, val2) {
    Prt1.call(this, val);
    this._val2 = val2;
}
Prt2.prototype = Object.create(Prt1.prototype);
Prt2.prototype.met = function() {
    return Prt1.prototype.met.call(this) + this._val2;
};
Prt1.prototype.met = function() {
    return this._val * 10;
};
class Cls1 {
    constructor(val) {
        this._val = val;
    }
    met() {
        return this._val;
    }
}
class Cls2 extends Cls1 {
    constructor(val, val2) {
        super(val);
        this._val2 = val2;
    }
    met() {
        return super.met() + this._val2;
    }
}
class tmpCls1 {
    met() {
        return this._val * 10;
    }
}
const assingAllPropertyDescriptor = (target, source) => {
    for (let data of Object.entries(Object.getOwnPropertyDescriptors(source))) {
        let key = data[0];
        target[key] = source[key];
    }
    return target;
}
assingAllPropertyDescriptor(Cls1.prototype, tmpCls1.prototype);

const obj1 = new Prt1(100);
console.log(obj1.met()); // => 1000
const obj2 = new Prt2(100, 200);
console.log(obj2.met()); // => 1200
const obj3 = new Cls1(100);
console.log(obj3.met()); // => 1000
const obj4 = new Cls2(100, 200);
console.log(obj4.met()); // => 1200
 
最後に編集:

げれげれ

ユーザー
これだけコアスクリプトがいまいち…って言われてると教科書の信頼性として心配はあるな、と
本当にどっちでもいいのか、実は意味が合って違うのか判断つかないんですよね。
なんなら宗教戦争を起こせるくらい宗派に自信を持ちたいものですが
何分理解が浅くてそのとき参考にしたものに逐一影響されるので統一感がなくなるのが悩みです。
最終的に何を信じて良いのかわからない、要求される学習コストが果てしないというのは
本当にそうですよね…私も今、それを実感してる真っただ中です。
別スレでfspaceさんからいただいた言葉でもあるのですが、
自分で調べて、情報の突き合せをして、考えるのをやめない以上の
方法はないのかもしれません。

今回に於けるArray.removeと、元からあるArray.filterの動作結果は同じでは御座いません。
具体的に言えば、Array.removeは元のArrayに影響を与え、Array.filterは影響を与えません。(これの良し悪しの論は更なる宗教戦争なのでさて置きます)
ご指摘ありがとうございます!
そういえば確かに、破壊的メソッドか否かの差はありますね。
もしかしたらそこに実装上の意図があるのかもしれない…

さっき思いついた方法です。
再定義対象のクラスを継承したダミークラスを定義して、ダミークラスのプロトタイプの全プロパティを再定義対象のクラスにマージするという方法です。
多分これはこれで別の問題がありそうなので、普通に従来通りの方法を使ったほうがいいと思います。
ご提案ありがとうございます!
まだ未熟者なので Object.entries や Object.getOwnPropertyDescriptors 等、知らなかったメソッドが
幾つかあり、まだ完全には理解できてないかもしれないですが、良い勉強になりました。
(プロパティディスクリプターの概念、むつかすぃ・・・)
イメージとしては、仰る通り「ダミークラスのプロトタイプの全プロパティを再定義対象のクラスにマージする」感じですね。
これで下記のような支障が出ないようであれば一つの方法な気がします。
(「全プロパティ」だから[[prototype]]も書き換えてしまうのかしら?
 すみません、今の私の力量では判断つかないです:kaoswt2:

【追記】
よく見たら前の書き込みでsetPrototypeOfを使って対応できるってありましたm(_ _)m
fspaceさんが示してくださった方法ですね。[[prototype]]を強制的に繋ぎ変えてしまう方法。
問題として、これをやってしまうと動作がとても低速になるとMDNにもデカデカと警告が出ている点ですね…
これだけ強めに警告を出されると、実用するのはちょっと気が引けるなぁ、というところです。
 
(「全プロパティ」だから[[prototype]]も書き換えてしまうのかしら?
Object.getOwnPropertyDescriptors()は、__proto__を返さないので、[[prototype]]は書き換えないと思います。多分。。。(自信ない)
でも、この方法でも動的にプロトタイプのプロパティを書き換える都合上、最適化は期待できそうにないので、従来通りの方法が一番ベストだと思います。
 

fspace

ユーザー
コアスクリプトがES6のクラス構文になっていないとして、誰が迷惑を受けるのでしょう?
prototypeベースのクラスをclass文で継承することは可能なのですから、何の問題もありません。
prototype形式の問題点は、エディタのサポートが受けづらいことと可読性かなと思っています。エディタにもよるかもしれませんが、例えば、現在は定義元へのジャンプや参照箇所の検索が効きません。変更による影響をよく確認する必要のあるツクールプラグインでは結構重要な機能だと思います。可読性についてはそれぞれの慣れがあるというのはその通りですが、現在プロトタイプベースの記法を主に使用している人というのは少数派だと思います。多数派に合わせるべきという主張は自然なものではないでしょうか。

prototypeベースのクラスはclass文を使用した内容に変換するのは機械的に可能です。
それをしなかったということは、何らかの考えがあっての事でしょう。
まぁ、真意に関しては希望的観測から邪推まで入り乱れるのが目に見えるので詮索しない方が無難です。
この辺りはその通りとは思いつつ、モヤモヤするところですね……。技術的な理由にしろ、経営的な理由にしろ、教えてくれればいくらか納得できるでしょうし、もっと先の議論ができると思うんですが……。

配列の要素削除による重さの違いは、evalや画像の処理と比べれば誤差レベルです。
要素削除の重さの違いは対象配列の内容次第ですね。小さな配列であったり、削除対象が少なければ誤差レベルでも済みますが、大きな配列にたくさんの削除対象があれば誤差では済まなくなります。そういった意味で、説明もなしに配列のメソッドとして生やすべきではないと考えています。

最後の一文にすべてが書いてありますね、ES5のコードが嫌いだと。
個人的に自分の好き嫌いのためにもっともらしい理屈を書き連ねるやり方は嫌いです。
私は「嫌い」と言うことが別に問題とは思いません。嫌いと感じたからこそ、その理由が何かを考えてそれを主張したり議論したりするわけで、何も感じなければ何も始まりません。きっかけを書いただけでその主張ごと信用ならないというのはあまりにも暴論かと。

えーっと、この場合、本当に「問題がない」んですよね。
このスレッドの一ページ目から読むとわかるのですが、むしろclass構文で定義すると「問題が起こり」ます。

クラス定義の点に絞って、ちょっと整理させてもらいますね。

[class構文で定義]
クラスの再定義不可。(MDNにも明記されている
extendsによるクラスの再生成、再代入を行っても、大元のクラスが再定義されるわけではないので、
サブクラスのprototypeチェーンの参照が変わらずに問題が起こる
それに対する明確な打ち手も今のところなし。

[prototype形式での定義]
従来通り。
(可読性の点はいったん置いておいて、挙動として)問題なし

[ツクールMZのコアスクリプト]
従来のprototype形式なので問題なし

[プラグイン記述について]
プラグインでのclass構文使用を禁じられているわけではないので、
class構文を使いたい人は使ってよい。使いたくない人は使わなければいい。問題ない
class構文が問題で、prototype形式ならば問題ないということであれば、少し違うかなと思います。

例えば、himeworksさんが書いている通り、class構文であってもsuperを使わなければ正しく動作する場合もありますし、逆にprototype形式であっても次のような書き換えは動作しません。

JavaScript:
// コアスクリプト
function Base() { };
function Derived() { };
Derived.prototype = Object.create(Base.prototype);
Derived.prototype.constructor = Derived;

// プラグイン
Base = class extends Base {
    foo() { return "foo"; }
};

// foo is not a function
console.log(new Derived().foo());
結局は親クラスを設定値から直接参照するか、グローバルスコープを介して間接的に参照するかの違いですので、あまりclass構文かprototype形式かというのは関係ありません。どちらの場合であっても、setPrototypeOfを使わない限りは親クラスを変更できませんので、コアスクリプトですでに子クラスが存在するクラスについては、従来の形式で書き換えるしかありません。

今回に於けるArray.removeと、元からあるArray.filterの動作結果は同じでは御座いません。
具体的に言えば、Array.removeは元のArrayに影響を与え、Array.filterは影響を与えません。(これの良し悪しの論は更なる宗教戦争なのでさて置きます)
もちろんそれは理解しています。破壊的な挙動だということを前提としたとしても実装に問題がありますし、破壊的な操作が要求されるケースがごく限定的であることを踏まえて、ほとんどのケースでfilterでいいと考えています。

再定義対象のクラスを継承したダミークラスを定義して、ダミークラスのプロトタイプの全プロパティを再定義対象のクラスにマージするという方法です。
マージするためのユーティリティを定義するという方法は私も以前考えたんですが、書き換える前のクラスのメソッドを使用して新しいメソッドを定義するきれいな方法が思いつかなくて諦めたのですよね……。

Object.getOwnPropertyDescriptors()は、__proto__を返さないので、[[prototype]]は書き換えないと思います。多分。。。(自信ない)
気になったので調べてみたところ、(V8では)Object.prototypeのプロパティとして__proto__が定義されているみたいでした。getOwnPropertyDescriptorsは対象自体に定義されたプロパティディスクリプタしか列挙しないので、__proto__は列挙されないようですね。
 

げれげれ

ユーザー
class構文が問題で、prototype形式ならば問題ないということであれば、少し違うかなと思います。

例えば、himeworksさんが書いている通り、class構文であってもsuperを使わなければ正しく動作する場合もありますし、逆にprototype形式であっても次のような書き換えは動作しません。
ご指摘ありがとうございます!

そうですね、定義側の問題でなく、そのクラスを書き換える側の記述の問題ですよね。
(或いは、定義時にclass構文のsuperを使用しているか否か)
それを踏まえて早急に修正しておきます。
 

タシラカ

ユーザー
個人的に自分の好き嫌いのためにもっともらしい理屈を書き連ねるやり方は嫌いです。
同感です。だから敢えて非常に個人的な感想を最後に書きました。客観的な意見なんてあり得ませんからスタンスを明確にしたんです。
そして自分にはまさに貴方がそれをしているように見えます。
フォーラム新参でロートルの私が今のツクール公式やMVを腐す事が前から気に喰わなかったんですよね。
いろいろ雑談スレに書き込んでいた時から貴方の言外の態度はちゃんと伝わってましたよ。

私はclassが愛おしく、その魅力を皆に伝えたいというエゴからこのトピックを立てて語ってきました。
しかし貴方の発言からprototypeに対する強い熱意、説得力を感じません。あるのは私個人に対する幼稚な反発です。
「それをしなかったということは、何らかの考えがあっての事でしょう。」?
前向きな理由があるのならどうぞ私と同様にES5に拘る利点を、貴方の予想を、prototypeの魅力を存分に書き綴ってください。
海外フォーラムが荒れているといっても、あそこは毎回誰かがクレームを付けている印象。
プラグイン作者の多くはKADOKAWA協力ボランティアでもなければ無償の愛に溢れた聖人でもなく利己的なユーザに過ぎません。
人に感謝がされたいから、クリエイターの活動に寄与したいから、スキルアップがしたいから、金銭を得られるから、
思い通りのゲームを作りたいから、そしてコーディングが楽しいから、具体的な理由は違えど皆自分の為に活動しているんですよ。
社会人として紳士的な言動は心掛けたいですが、赤の他人から「大人の我慢」を強いられる筋合いもないです。
一購入予定者としてコーディングを楽しみにしているユーザが快適なコーディングサポート環境を築いてほしい、
という気持ちを持つ事の何が悪いのでしょうか?

RPGMakerWebに書き込んでる方達も念願が叶い喜びを分かち合っていたところに「勘違いでした」と内定が突然白紙に戻され、
行き場の無い不満と「言っても仕方がない」「間違いは誰にでもある」という理性や調和と葛藤している事が自分には分かります。
彼らの根底にある物は失望です。
同じ気持ちを持つ一ユーザとして無神経なレッテルを張り彼らの名誉を貶めた事に対し貴方に撤回を求めます。

この辺りはその通りとは思いつつ、モヤモヤするところですね……。技術的な理由にしろ、経営的な理由にしろ、教えてくれればいくらか納得できるでしょうし、もっと先の議論ができると思うんですが……。
コアプログラムを大部分流用していた事例はPC版だとRPGツクール2003まで遡りますね。
あの頃はアスキーからエンターブレインに移ったばかりで結構バタバタしていた印象があります。
今回は例の新会社や上半期決算が近いですから8/20という発売日から逆算して開発計画を立てたのではないかなぁと見てますね。
並行的に業務発注していくのなら一番尺を取るのがコアのプログラミング、及び動作テストでしょうし短納期ならば真っ先に削るでしょう。

結局は親クラスを設定値から直接参照するか、グローバルスコープを介して間接的に参照するかの違いですので、あまりclass構文かprototype形式かというのは関係ありません。どちらの場合であっても、setPrototypeOfを使わない限りは親クラスを変更できませんので、コアスクリプトですでに子クラスが存在するクラスについては、従来の形式で書き換えるしかありません。
そういう事ですね。
ネックとなっているのは「Base = class extends Base」の部分だけなのでコアスクリプト側の文法の違いは関係無い。
ツクールMVのコアスクリプトもオーバーライドを省略しているメソッドはいくらでもあります。
他プラグインとの競合まで考えると「Base = class extends Base」は使わない方が良い、で話はおしまいです。
コアスクリプトにクラス構文を採用する事による新しいデメリットと言うのはちょっと思い浮かびません。

もちろんそれは理解しています。破壊的な挙動だということを前提としたとしても実装に問題がありますし、破壊的な操作が要求されるケースがごく限定的であることを踏まえて、ほとんどのケースでfilterでいいと考えています。
そもそもMVにない新規追加されたメソッドですからね。
補足をするのならRubyには今回のremoveと似た挙動のArray#deleteが存在し、RGSS3ではステート配列やスキル配列等に一部使用していました。
しかしMVでは既にindexOf,spliceを使用した処理に代替されています。今回もしも使用するとしたらTPB絡みでしょうか?
頻繁に使うのなら実装の非効率性が問題だし、使わないのなら組み込みクラスを無闇に拡張するべきではないし
定義目的が類推出来ず「不気味」としか言えません。
 
最後に編集:

剣崎宗二

ユーザー
机上の空論でしょう。いくら単体の効率性が高くても人力で最適解を構築しなければ意味がありません。
最後の一文にすべてが書いてありますね、ES5のコードが嫌いだと。
個人的に自分の好き嫌いのためにもっともらしい理屈を書き連ねるやり方は嫌いです。
ここで「嫌い」と書いたのは人格を否定・攻撃する意図は無く、嫌いというのを説明するために理由を書くよりも、これは個人の主観であるとして争いをそこで打ち切るためです。
フォーラム新参でロートルの私が今のツクール公式やMVを腐す事が前から気に喰わなかったんですよね。
いろいろ雑談スレに書き込んでいた時から貴方の言外の態度はちゃんと伝わってましたよ。
しかし貴方の発言からprototypeに対する強い熱意、説得力を感じません。あるのは私個人に対する幼稚な反発です。
お互いクールダウンする事をお勧めいたします。
こう言ったプログラムに関する討議(どちらも目的は達成できるが、その過程に好みがあり、利弊がある。所謂「宗教戦争」)は往々にしてヒートアップしがち(それ故に私は避けがち)なのですが、それを考慮しても相手の意図の邪推や、人格攻撃に近い所まで進むのはさすがに行き過ぎと考えます。
議論を見ている第三者からこれがどう見えているのか、一度お考えになって頂き、本当にそれが自分が意図して伝えたかった事、達成したかった事なのか、をご確認して頂けたらと思います。
(少なくとも私は「この議論面倒くせぇ…そろそろ辞めるかな…」に近い感覚になっております)

海外フォーラムが荒れているといっても、あそこは毎回誰かがクレームを付けている印象。
同じ気持ちを持つ一ユーザとして無神経なレッテルを張り彼らの名誉を貶めた事に対し貴方に撤回を求めます。
私も本来は北米の某所を住処とする、英語メインの外国人なのでございますが、(飽くまでも北米の感覚で)言わせて頂きますと、あちらの方々は物言いが極めてストレートで日本的な文化から見ると「無礼」「失礼」に見える事が多々あります。
そこら辺に「どちらが正解」等はなく、単に文化の違いでそう見える(また、そう見えても仕方ない)と考えます。
実際私は日本暮らしがそれなりに長いのでこうして「郷に入っては郷に従え」が出来て(少なくとも自分ではそう考えている)のですが、あちらでやっていたノリでここに書き込んでいたら恐らく今フルボッコにされているのは私でしょうね…(笑

もちろんそれは理解しています。破壊的な挙動だということを前提としたとしても実装に問題がありますし、破壊的な操作が要求されるケースがごく限定的であることを踏まえて、ほとんどのケースでfilterでいいと考えています。
逆に、こういった前提であるのであれば、私はArray.removeの追加に問題はないと考えます。
ごく限定的と言ってもその様なケースはある事ですし、Array.removeを追加する事によってArray.filterの使用が妨げられたりする事もございませんので、単に「使い分ければ良い」と言う事ではないでしょうか。
完全に「使わない物」を実装したり既存の物と同一動作の「車輪の再発明」をするならば問題ですが、実動作に差異があるのであれば、意味合いはあるとの考えです。

問題を提起するにしろ、完全なコアスクリプトで使用先が見えてから、「この使い方ならばArray.filterで十分では」と言う形で行うべきではないでしょうか。
 

WTR

ユーザー
空気読まずにテキトーに前から気になっていたことを聞いてもいいですか
ES6とか関係ないんで主題から外れるかもしれませんが…
JavaScript:
 * Draws the window shape into PIXI.Graphics object. Used by WindowLayer.
 */
Window.prototype.drawShape = function(graphics) {
    if (graphics) {
        const width = this.width;
        const height = (this.height * this._openness) / 255;
        const x = this.x;
        const y = this.y + (this.height - height) / 2;
        graphics.beginFill(0xffffff);
        graphics.drawRoundedRect(x, y, width, height, 0);
        graphics.endFill();
    }
};
MZのドキュメントを眺めていまして…
上記は新しいメソッドだと思いますが
width / height / openness といったプロパティを参照しています。
width も height も openness も、"_" 付きであってもなくても同じ値を返すと思うのですが
なぜ this.openness ではなく this._openness なんでしょう。

命名規則として "_" は外部から参照するかどうかで…と聞いた覚えがあるのですが
同じメソッドの中で this.width と this.height に対して this._openness という参照の仕方なのがキモチワルイです。
何かルールが読み取れるでしょうか? どーでもいい感じですか?
 
 本来は変数の範囲(スコープ)を指定したいのだけど、JSにそのような機能はないので _ 接頭辞のものはクラスに閉じた変数ですよー、ということにして擬似的に範囲を設けましょう、という意味だとは思うんですが、実際の運用はかなりテキトーな印象です。
 プロパティに変数の値を変える以外の副作用がある場合、は副作用を発生させるべきかそうでないかで使い分ける、とかはありそうです。
 
  • Like
Reactions: WTR

WTR

ユーザー
やっぱりテキトーですか!
自分で名前つけるときもなんとなーくにしててどうしたものかと思ってましたが
Myルールが整合とれていれば深く考えなくてもいいのかな。
 

fspace

ユーザー
自分の発言が発端ですし、自分もわりと似たことする人間なので、すごく言いづらいんですが……いろいろと棚に上げて言わせてもらうと、海外フォーラム云々については剣崎さんと同意見です。まあ、私は「郷にいながら郷に従ってない」のでその意味では逆ですが……(海外的なスタイルの方が好きなので)。海外フォーラム内ですら問題になっていないことをここでどうこう言っても仕方ないかなと。

逆に、こういった前提であるのであれば、私はArray.removeの追加に問題はないと考えます。
ごく限定的と言ってもその様なケースはある事ですし、Array.removeを追加する事によってArray.filterの使用が妨げられたりする事もございませんので、単に「使い分ければ良い」と言う事ではないでしょうか。
完全に「使わない物」を実装したり既存の物と同一動作の「車輪の再発明」をするならば問題ですが、実動作に差異があるのであれば、意味合いはあるとの考えです。
元々は一例として挙げただけで、問題点についてあまり詳しく書かなかったので、一度問題点について整理しますね。

一つ目の問題点は、最悪ケースの計算量が多くなってしまっていることです。例えば、配列の長さがnで、前半分の要素がすべて0、後半分の要素がすべて1の配列を対象とした場合を考えます。この配列に対してremove(1)を実行すると、indexOfで約n/2回の同値比較が行われた後にspliceで要素がひとつ削除されます。これを約n/2回繰り返すことになるので、トータルの比較回数は約(n^2)/4回になります。一方、filterで同様の処理を実行した場合、比較回数はn回です。n=1,000とすると、removeは250,000回、filterは1,000回の比較なので250倍(n/4倍)の差がつきます。ここで、filterは最小回数の例として出しただけで、indexOfでも開始インデックスを指定すれば同じになります。

二つ目の問題点は、組み込み型である配列のプロトタイプを変更していることです。これはJsExtensionsに含まれるすべての関数について同じことが言えます。組み込み型のプロトタイプというのは誰もが変更できてしまうため、複数の人が同じ名前で異なる挙動をする関数を定義してしまう可能性があり、また、JavaScriptのアップデートによって同名の新しい関数が定義される可能性もあります。こういったことが起こると、既存の挙動が壊れてしまうため、組み込み型のプロトタイプは変更してはならないというのが一般的な見解です。また、そういった理由からあまり使われないため、JavaScriptに慣れない人に混乱をもたらしがちでもあります。例えば、配列に対してremoveが使われているコードをみて、挙動を知るために配列のAPIを調べたがWeb上のどこにも見つからない、ということが起こります。

三つ目の問題点は、一致する要素を"すべて"削除するということがどこにも説明されていないことです。removeという名前の関数はJava、.NET、Python、Rustなど多くのプログラミング言語で一致する"最初の"要素を削除するAPIの名前として使われています。何も説明がなければ、最初の要素のみが削除されると勘違いする人は多くいると思われます。

四つ目の問題点は、配列に対する要素の削除操作を間違った方法に誘導しがちなことです。配列の末尾以外の要素削除は要素の移動を引き起こします。そのため、複数要素を一度に削除する場合には、一括で削除して要素移動を一度で済ますのが効率的なやり方になります。removeを用いて要素を削除しようとすると、必要以上に要素移動を引き起こしがちです。ただ、これに関しては仕様を見れば想像のつくことなので、使う側の責任と言ってしまってもいいかもしれません。

以上から、indexOfに開始インデックスを指定する実装に変更し、組み込み型のプロトタイプ以外の場所に関数を定義し、すべての一致要素を削除することをドキュメントに記述すれば、removeを実装すること自体に問題はないと思います。

ちなみに、JavaScriptの配列は本当にメモリ上連続した配列として実装されているとは限らないため、上記が当てはまらない場合もあることは補足しておきます。

問題を提起するにしろ、完全なコアスクリプトで使用先が見えてから、「この使い方ならばArray.filterで十分では」と言う形で行うべきではないでしょうか。
上記のいくつかの問題点を修正した上での話にはなりますが、「filterで十分」という主張に対してこれは一理あると思います。ただ、破壊的挙動を必須とした設計はアンチパターンのことも多いので、少し懐疑的にはなってしまいますね……。
 
トップ