イマドキのプラグイン記述法

これはとてもいい記事ですね。
おさらいにもなるし、新しい知識もいろいろ仕入れられました。

プラグインが、自身のファイルパス(currentScript)を取得できるのは知らなかった!
ファイル名の衝突に関する競合対策はバッチリですね!
 

fspace

ユーザー
ありがとうございます。

currentScriptも本来の使い方ではないので本当はアンチパターンに入れたいくらいなんですが、他に方法がないので仕方なく受け入れている感じではあるんですよね。パラメータを見出しに使ったりしてるのも似たような理由だと思うので、ツクールMZでこういうとこが直ってるといいんですが。
 

タシラカ

ユーザー
良いまとめですね
初期に色々調べてから新作発表があるまでMVから離れていて最近の事情に疎いので参考になります
RPGツクールXP発売時点ではスクリプト素材という概念もありませんでしたけど
今は不特定多数の人間がシステムをこねくり回す事まで想定しないといけない時代になったんですよね

何故かというと、プラグインの改造依頼としてよくそういうプラグインが持ち込まれてとてもツラいからです。 解読する手間やリスクを含めて請求すると「高くない?」となり、 新規に作り直しましょうと提案すると「なんで?」となり、 これは改造できませんと断ると「実力がないのかしら?」となります。 だからといって「クソコードなんでムリです(意訳)」なんて言えば角が立つのは避けられないわけで……。 少しでもこういう事態が減ればいいなと思って書きました。
自語りになりますけど昔自分もスクリプト制作の有償請負で痛い目を見た経験があるので気持ちは分かりますよ。
MITの最大の利点が責任を負わない事なら有償請負の最大のリスクは責任と保証を求められる事。
99%問題なく動くとしても1%の動作不良の可能性を潰す為に解析と理解を進めて
発生し得るレアケースを精査し納品後もアフターフォローに心を砕かなければならない。
その1%の仕事に対する価値が実装者と依頼者の間で深い溝があると感じました。
ツクール作品の有償販売が増えるにつれ、そういった案件もまた増えると思いますけど今後このテの摩擦が減る事を祈ります。
 
最後に編集:

しぐれん

ユーザー
全体的に同意できる内容です。
Importedに頼る実装は邪悪です。
海外では基本的なマナー扱いされていますが、日本ではあまり見ない気がします。

オレ用オブジェクトアンチパターンは海外の二大競合プラグインでよく見ます。
というより、Yの付くプラグインがそれを基本として広めてしまったのが悲劇の始まりでしょうか。

オレ用オブジェクトパターンは、一括で初期化するとエディタで調べやすくなるので使い方次第。
海外系プラグインはアンチパターンの書き方が多くて吐き気がします。

「おわりに」にあるプラグイン改造の話は強く強く同意します。
複雑なプラグインは制御不能であることが多いです。
特にATBプラグインは最大クラスの公害だと思ってました。
 

げれげれ

ユーザー
新規に学ぶ側の立場として、既存プラグインを参考にすることもあるのですが、
「この書き方、本当に信用していいんだろうか」と疑問に思うことも多くありました。

こちらの記事はここでの数々の反応も含めて、全面的に信頼できる内容であると認識しました。
基本指針とさせていただきます。有益な記事をありがとうございます。
 

fspace

ユーザー
コメントありがとうございます。

愚痴気味に書いた改造の話は気分を悪くする人もいるかもしれないと思っていたので、予想外の同意をもらって少し驚いてます。みんな同じような経験をしてるんですね。

RPGツクールXP発売時点ではスクリプト素材という概念もありませんでしたけど
今は不特定多数の人間がシステムをこねくり回す事まで想定しないといけない時代になったんですよね

自分はMVから始めたのでこんな感じなのか、と思ってましたが以前は違ったんですよね。もう少しプラグインに制限とかがあれば競合解決も楽だったんでしょうけど、みんなが高機能すぎるプラグインに慣れてしまった今となっては夢物語ですね。

オレ用オブジェクトアンチパターンは海外の二大競合プラグインでよく見ます。
というより、Yの付くプラグインがそれを基本として広めてしまったのが悲劇の始まりでしょうか。

あまり一般には知られていないのかもしれませんが、有名どころのプラグインは意外とプログラムとしての質があまりよくなかったりしますよね。一方で、機能の作りこみは大したもので、なかなか代わりを提供できないのがまた悩ましいところだったりします。

オレ用オブジェクトパターンは、一括で初期化するとエディタで調べやすくなるので使い方次第。

オブジェクトに入れたほうが調べやすいことって何かありましたっけ?

こちらの記事はここでの数々の反応も含めて、全面的に信頼できる内容であると認識しました。
基本指針とさせていただきます。有益な記事をありがとうございます。

ツクールプラグインは絶対的に正しい書き方を提供してくれる人がいないので、私も含めてみんな探り探り書いています(多分)。書き方が正しいかどうか疑問に思ったときにはぜひ自分でいろいろ試して考えてみてください。
 

しぐれん

ユーザー
オブジェクトに入れたほうが調べやすいことって何かありましたっけ?
私が使っているプラグインパラメータの格納方法ですが、オレ用オブジェクトではあります。
JavaScript:
const setting =(function(){
    //プラグインパラメータを一度展開。
    const param = getParam();
    //一度にオブジェクトにまとめる
    return {
        value:Number(param.value),
        name:String(param.name),
    };
})();
パラメータを手動で探す手間が減って、settingから入力補完でパラメータを探せます。
 
とても参考になります。
今まで「本当にこの書き方でいいんだろうか」と思っていたことの大半が書かれていたのですっきりしました。

私はMVからJavaScriptを触り始めたので、始めは「動けばいいや」くらいの気持ちで作ったものばかりです。
私が作ったATBプラグインはその筆頭ですね。
単純に「JavaScriptに慣れるため」「MVの戦闘システムの構造を理解するため」「こういうこともやろうと思えばできるよ的な例」として作ったのですが、時間がたつにつれまずいことには気づきました。
間違いなく一番他人に迷惑かけてるプラグインだろうなとは思ってましたが、こちらの記事を読んで確信に変わりました。
まあ手本がないから(古いプラグインは)仕方ないと割り切ってますが。

ちょっと気になったのは、ES2015からの機能についてです。
最近「ツクールのバージョンが1.5なのでプラグインが動かない」というコメントをよくいただきます。
たいていは「アップグレードして」で解決するのですが、それができない方もいるわけでして・・・。
もうMVではES2015からの機能が使えない方に合わせるのが正解なのではないかと思い始めています。
ここの意見見る限り、そちらは対応できなくても仕方ないといった感じなのでしょうか?
 

タシラカ

ユーザー
間違いなく一番他人に迷惑かけてるプラグインだろうなとは思ってましたが、こちらの記事を読んで確信に変わりました。
プラグイン制作者も一介のユーザに過ぎないのであまり気に病む事も無いと思いますよ。
なんというか…興味がない人には全く関係ない話なんですけど
立ち絵や一枚絵、歩行グラ等の画像素材オーダーメイドの相場は大体数千円から数万円程度だと思うんですよ。
それならプラグイン素材もそれにケが生えた程度出せば十分だろ、勿論バグなんて出したら許さね~からな~と
同人依頼慣れしているゲーム作者ほど相場を見誤ってトラブルに発展するケースはそこそこ聞きます。

スクリプト素材が出回り始めた当初はA(ゲーム作者)とB(素材作者)の二人だけの話でしたけど
次第にCDEFGHと複数人の素材作者のプラグインを手当たり次第ぶち込んでゲームシステムを地雷原化するのが当たり前になり
更にZ(システム担当者)にプロジェクトのシステム管理を押し付ける人まで出てきてこういった話が出てくるようになったのかなと。
何にせよ素材提供者が悪い訳ではないけど気に掛けてくれたらZが楽になる、程度の話ですよ。

ここの意見見る限り、そちらは対応できなくても仕方ないといった感じなのでしょうか?
そらそうでしょう。
ES5に拘る理由なんて一部の化石ブラウザでもプレイ出来るようにする為、くらいじゃないですか?
Steam版は勿論、パッケージ版もアプグレ可能だし
それが出来ないけど素材作者に注文出来る状況って理解に苦しみます。他のプラグインとの兼ね合い?
ボランティアでやってる趣味までそんな意見気にしてたらハゲますよ。自分の毛根は大切にしてください。
 

fspace

ユーザー
コメントありがとうございます。

パラメータを手動で探す手間が減って、settingから入力補完でパラメータを探せます。

なるほど、パラメータの変数名(プロパティ名)を覚えていなくても、そのオブジェクトの変数名さえ覚えていれば入力補完で探せるということですね。オブジェクトにすると未使用値の解析が効かなかった気がするので、そのあたりとのトレードオフですかね。

単純に「JavaScriptに慣れるため」「MVの戦闘システムの構造を理解するため」「こういうこともやろうと思えばできるよ的な例」として作ったのですが、時間がたつにつれまずいことには気づきました。
間違いなく一番他人に迷惑かけてるプラグインだろうなとは思ってましたが、こちらの記事を読んで確信に変わりました。

自分自身、というかおそらく誰もが、クソコードを書いてはそれがダメだと気付いて次は気をつけよう、ということを繰り返していると思うので、その時点でしっかりと考えて書いたのであればそれはそれでいいと思います。今回私が書いた記事にしても、一年後の自分は違う意見を持っているかもしれません。過去にやったすべてのことに責任を感じていてはキリがありません。

「おわりに」に書いたことは愚痴以外の何ものでもなくて、本来は理由をしっかりと説明して毅然とした態度で請求なり提案なりすればよいだけの話です。実際、納得してくれる人もいます。もちろん、改変対象がきれいに書かれていればうれしいですが、そうでないからといって作者を責めるつもりは一切ありませんし、すべきとも思いません。

ちょっと気になったのは、ES2015からの機能についてです。
最近「ツクールのバージョンが1.5なのでプラグインが動かない」というコメントをよくいただきます。
たいていは「アップグレードして」で解決するのですが、それができない方もいるわけでして・・・。
もうMVではES2015からの機能が使えない方に合わせるのが正解なのではないかと思い始めています。
ここの意見見る限り、そちらは対応できなくても仕方ないといった感じなのでしょうか?

ツクールがアップデートできないというのはかなりのレアケースなので、切り捨ててしまって構わないと私は思います。もちろん、切り捨てるかどうかの判断は作者の自由ですので、対応したければしてもいいとは思います。ツクールプラグインがES5の文法のみで書かれていたとしても、それをひどいコードだと感じることはありません。

立ち絵や一枚絵、歩行グラ等の画像素材オーダーメイドの相場は大体数千円から数万円程度だと思うんですよ。
それならプラグイン素材もそれにケが生えた程度出せば十分だろ、勿論バグなんて出したら許さね~からな~と
同人依頼慣れしているゲーム作者ほど相場を見誤ってトラブルに発展するケースはそこそこ聞きます。

そもそもツクール関連の仕事自体ボランティア価格が当たり前のようになっていますが、プラグインはそれに加えて、どんな作業をしているのか一般の人が想像できないために軽んじられている感はありますね。コーディングが作業の一部でしかないと言われて、一体どれだけの人が納得するのだろうかと。
 
読みました。

・概要
ES5対応の目的は主にIE対応のために行われるものですが、
モダンブラウザ以外のブラウザを完全に切り捨てるなら、最低でもES6でいい気がしますね。

・プラグインコメント
ライセンスに関しては、MITライセンスのことを何のことだか分かってないで使ってる人も多そうな感じ。
僕なんかは、著作権表示すらどうでもいい時はWTFPLをつけてますね。

・全体構成
プラグインの有無の判定とパラメータのパースはツクールのエディタ側が悪いよなって気がしますね。プラグイン側に委ねた結果こうなってる感じ。
バージョン文字列を比較しちゃうのはプログラミング初心者にありがちですね。
evalは素人が使うと危険なのは間違いないですね。むやみに使わず、危険性をきちんと理解して運用していれば問題ないと思います。
JSON.parse(JSON.stringify(...))は、確かにパラメータのパースに使うのは結構微妙ですが、replacerを使って再帰処理を書く場合は、普通に書くよりも安全に書けたりします。

・ゲーム処理の記述
俺用オブジェクトに関しては、煩雑にさえならなければわりと容認派です。ただ、プラグイン1つのために大量にグローバル変数が量産されるのは頂けないですね。
Graphics.frameCountの誤用は、そのままUnity感覚で動作確認せずやってしまうケースが多いんだろうなあって感じしますね。

・プログラミング一般
基本的にプラグイン書く人は全員、Airbnb JavaScript スタイルガイドを読んでほしい感はありますね。
あとまあ、PrettierBeautifyのようなオートフォーマッターを使ってほしいですね。この2つが守られてれば最低限読みにくいコードにはならんと思います。
let/constに関しては、イミュータブルを意識してなるべくletも使わないようにしてほしいですね。
polyfillに関しては、環境によってはpolyfill.ioも選択肢の一つに入るかもしれないですね。

・ユーティリティ
同値比較はlodashでいいのではと思ってしまった・・・

---

おそらくこの記事のターゲットとしては、ピュアなJavaScriptをそのまま書く人を想定してるのかもしれないですけど、もし可能ならどんどんNode.jsのフレームワークを使いまくるべきだと思います。
今はTypeScriptBabelESLintJSDocのような便利なツールもあるわけですし。
まあでもやっぱ、技術レベル的に厳しいものがあるのかなって感じもしますね・・・

まあなんというか、僕的にはこの記事のとおりに厳密にやらなくても、
最低限Airbnbのスタイルガイドを意識していて、オートフォーマッターが通っていれば特に文句なしですね。
 

fspace

ユーザー
コメントありがとうございます。

ES5対応の目的は主にIE対応のために行われるものですが、
モダンブラウザ以外のブラウザを完全に切り捨てるなら、最低でもES6でいい気がしますね。

そうですね。特殊な場合を除いて、さすがにもうIE対応はいらない気がします。特にゲームは個人以外での利用はあまりないでしょうから、最新の安定版に近いバージョンを仮定してもいいんじゃないかと思ってます。

プラグインの有無の判定とパラメータのパースはツクールのエディタ側が悪いよなって気がしますね。プラグイン側に委ねた結果こうなってる感じ。

同意見です。MZでいくらか改善されそうなので期待してます。ただMVに関してはどうしようもないので、その中での最善を考えるしかないですね。

evalは素人が使うと危険なのは間違いないですね。むやみに使わず、危険性をきちんと理解して運用していれば問題ないと思います。

使ってはならないというわけではないですが、「JavaScriptのコードを実行したい」というそのままな要求でもない限りは、常に妥協的選択であることは認識しておきたいですね。

JSON.parse(JSON.stringify(...))は、確かにパラメータのパースに使うのは結構微妙ですが、replacerを使って再帰処理を書く場合は、普通に書くよりも安全に書けたりします。

JSON.stringifyがよしなにしてくれるから安全というのは何かおかしな感じがします。結局は想定されるすべての入力に対して正しい出力を定義しなければならないはずで、そもそも別の目的を持った関数をもってきて特殊なケースはいい感じにしてくれるはず、とはならないのでは?

俺用オブジェクトに関しては、煩雑にさえならなければわりと容認派です。ただ、プラグイン1つのために大量にグローバル変数が量産されるのは頂けないですね。

俺用オブジェクトは特に使いづらいというほどでもないのですが、そう書く理由も見当たらなかったためアンチパターンに含めておきました。基本的には(ブロックスコープの)変数で十分なはずなので、どちらかというとこっちがデフォルトかなと。

Graphics.frameCountの誤用は、そのままUnity感覚で動作確認せずやってしまうケースが多いんだろうなあって感じしますね。

これを書いたのは自分が似たことをやらかしたことがあるからなんですが、実行してみても結構わかりづらいんですよね、これ……。実行環境依存で理由をしらないと再現性にも乏しいので。そもそもコアスクリプトが描画フレームしかカウントしていないのが問題のような気がしてます。

基本的にプラグイン書く人は全員、Airbnb JavaScript スタイルガイドを読んでほしい感はありますね。
あとまあ、PrettierBeautifyのようなオートフォーマッターを使ってほしいですね。この2つが守られてれば最低限読みにくいコードにはならんと思います。

従うかどうかは別にしても、何らかのスタイルガイドで書き方と何故そうすべきかを知っておくことは大事ですね。フォーマッタは読む側でもかけられるのでそれほど困ってはいませんが、よくタブとスペースのインデントが混在するコードを見て「あ……」とは思います。

let/constに関しては、イミュータブルを意識してなるべくletも使わないようにしてほしいですね。

自分も関数型的な書き方を意識してほぼconstで書きますが、レガシーなオブジェクト指向のツクールでそれを要求するのは少し酷な気もします。

同値比較はlodashでいいのではと思ってしまった・・・

同値比較のためだけにlodashを入れてライセンスが複雑になるよりはいいかなと思ってます。

あとはもともとの動機として、ツクールのオブジェクトは参照として比較したかったというのもあります。

おそらくこの記事のターゲットとしては、ピュアなJavaScriptをそのまま書く人を想定してるのかもしれないですけど、もし可能ならどんどんNode.jsのフレームワークを使いまくるべきだと思います。
今はTypeScriptBabelESLintJSDocのような便利なツールもあるわけですし。
まあでもやっぱ、技術レベル的に厳しいものがあるのかなって感じもしますね・・・

トランスパイラは変換後のJavaScriptが読みづらいのでツクールではあまり使用する気になれないのですよね。MZでTypeScriptがデフォルトとかになればうれしいですが、多分ならないでしょうね……。
 
JSON.stringifyがよしなにしてくれるから安全というのは何かおかしな感じがします。結局は想定されるすべての入力に対して正しい出力を定義しなければならないはずで、そもそも別の目的を持った関数をもってきて特殊なケースはいい感じにしてくれるはず、とはならないのでは?
プログラミング初心者が下手に無限ループのバグを引き起こしやすいアルゴリズムを自前で書くよりも、このような致命的なバグが発生しにくい方法を使ったほうが保守性等の観点から安全だという意味です。
再帰処理についてきちんと理解があり、ループ文の書き方をしっかり心得ているなら、分かりやすく書くことを意識したうえで自前で書いていいと思います。

JavaScript:
const array = [1, 2, 3, [4, 5, 6, [7, 8, 9]]];

// 再帰関数をちゃんと書いたコード
const before = (a) => {
  let data = [];
  const recursiveFunc = (target) => {
    for (let value of target) { // for (let i=0; i<target.length; i++) value=target[i] だとより煩雑に
      if (Array.isArray(value)) {
        recursiveFunc(value); // 一歩間違うと無限ループバグを引き起こす
      } else {
        data = [...data, value];
      }
    }
  };
  recursiveFunc(a); // recursiveFunc の呼び出し先が 2 回とも異なる
  return data;
};

// JSON.stringify で再帰関数を書いたコード
const after = (a) => {
  let data = [];
  JSON.stringify(a, function (key, value) {
    if (!Array.isArray(this[key])) {
      data = [...data, this[key]];
    }
    return value;
  });
  return data;
};

console.log(before(array), after(array));

// (まあこのサンプルだと array.flat(2) でも同じ結果だけど・・・)
実際、このように JSON.stringify で書かれていた方がコードの数が少なく、どういった問題があるのかどうかまで比較的早く知ることが出来るので、自前で書くよりも既にあるものを使って書いたほうが分かりやすいこともあるかと。

自分も関数型的な書き方を意識してほぼconstで書きますが、レガシーなオブジェクト指向のツクールでそれを要求するのは少し酷な気もします。
そもそも、ES6以降の構文を使うなら関数型言語的にメソッドチェーンを多用しない理由もないと思います。
実際に関数を用いてループを行ったほうが無限ループバグが起こりにくいので、使わない手はないかと。

JavaScript:
console.log(
  $dataActors
    .slice(1) // または .filter(Boolean)
    .filter(({ classId }) => classId === 3)
    .map(({ nickname }) => nickname)
);
例えば「職業IDが3のアクターのニックネームを一括で取得したい」なんてケースはこれでいいわけですし。スッキリして読みやすいかと。
こういう処理はfor文やwhile文で書くと複雑化するので、forEach・map・filter・reduceを積極的に使うべきだと思いますね。

同値比較のためだけにlodashを入れてライセンスが複雑になるよりはいいかなと思ってます。
ちなみにlodashはMITですよ。あくまで僕個人の考えですが、同じ処理なのに人によって全く書き方が異なるという処理は少ない方がいいと思います。

トランスパイラは変換後のJavaScriptが読みづらいのでツクールではあまり使用する気になれないのですよね。
「トランスパイル後のスクリプトだけしか公開しない」という前提がそもそもおかしいんです。
ソースコードもまとめてGitHubなりに公開するべきで、トランスパイル後のコードは動作軽量化のためにMinifyされているのがベストだと思います。実際にPIXI.jsのリポジトリもそのように運用されていますし。
それに、ソースマップをコードの中に埋め込むという方法だってあります。
 
最後に編集:

fspace

ユーザー
プログラミング初心者が下手に無限ループのバグを引き起こしやすいアルゴリズムを自前で書くよりも、このような致命的なバグが発生しにくい方法を使ったほうが保守性等の観点から安全だという意味です。

無限ループって致命的なバグでしょうか。ハマれば確実に気付くのであまり深刻な事態になることは少ない気がします(初心者がパニくるバグであることは間違いないですが)。あと再帰の場合にはスタック上限を超えて落ちるので、JSON.stringifyの循環構造のエラーで落ちるのとあまり変わらない気がします。

実際、このように JSON.stringify で書かれていた方がコードの数が少なく、どういった問題があるのかどうかまで比較的早く知ることが出来るので、自前で書くよりも既にあるものを使って書いたほうが分かりやすいこともあるかと。

コードが短いことと簡潔であることは同じではないと思います。JSON.stringifyを使うにはその仕様をしっかりと理解する必要があるので、むしろコードとしては複雑化しています。余計なこと(toJSONなど)をされて悩むことにもなりかねません。実際、記載いただいたコードも再帰で書かれた関数はオブジェクトの配列に対してflatとして動作しますが、JSON.stringifyで書かれた関数はflatとして動作しません。

そもそも、ES6以降の構文を使うなら関数型言語的にメソッドチェーンを多用しない理由もないと思います。
実際に関数を用いてループを行ったほうが無限ループバグが起こりにくいので、使わない手はないかと。

もちろん、配列のメソッドで対応できる範囲であれば積極的にそれを使った方がいいと思いますが、本格的にletを排除しようと思うと記事中に示したような処理の細かな関数化が必要になってきます。条件分岐やループのために新しく関数を定義するというのは、オブジェクト指向(というか手続き型)ではあまり一般的ではないように思うので、そのレベルまで要求するのはやめた方がいいかなと。……書いていて気付きましたが、おそらく『なるべくletを使わない』で想像する度合が違っただけみたいですね、すみません。

ちなみにlodashはMITですよ。あくまで僕個人の考えですが、同じ処理なのに人によって全く書き方が異なるという処理は少ない方がいいと思います。

MITライセンスなので著作権表示が必要となって、作者自身の著作権表示と両方の記載が必要となるという意味でした。lodashとは何かを知っている人やライセンスの取り扱いに慣れている人であれば特に問題ないと思うのですが、そうでない人からすると一つの作品に二つ以上の著作権表示が必要というのは混乱しそうだなと。少し考えすぎかもしれませんが。

同じ処理に対して複数の書き方があるのは私は特に問題ないと思っています。仕様さえ定義されていれば実装はあまり気になりません。小さな処理に逐一パッケージを使用するJavaScriptの文化には反対派ですね。

「トランスパイル後のスクリプトだけしか公開しない」という前提がそもそもおかしいんです。
ソースコードもまとめてGitHubなりに公開するべきで、トランスパイル後のコードは動作軽量化のためにMinifyされているのがベストだと思います。実際にPIXI.jsのリポジトリもそのように運用されていますし。
それに、ソースマップをコードの中に埋め込むという方法だってあります。

「トランスパイル後のスクリプトだけしか公開しない」のがおかしいというのはその通りだと思います。ただ、それぞれのプラグインがそれぞれの方法でトランスパイルするソースコードをまとめて管理するとなると、それもそれでなかなか面倒なものがあります。統一的な方法が欲しいところですね。
 
なんというか、fspaceさんとは宗教的な違いがあるようですね・・・。おそらく、どうしても互いに譲れない部分がありそうです。
まあでも、記事の最後の方に書かれていた「私論として話さざるを得ない部分が多く」の部分はそういうことですよね。

無限ループって致命的なバグでしょうか。ハマれば確実に気付くのであまり深刻な事態になることは少ない気がします
僕は致命的なバグだと思いますね。確かにハマれば確実に「無限ループが発生している」ということに気づくとは思いますが、「なぜ無限ループが発生しているのか」に関しては再帰関数では気づきづらいものだと思います。(ただこれに関しては過去の経験の話に直結しそうなので、深追いしないでおきましょう。)

コードが短いことと簡潔であることは同じではないと思います。JSON.stringifyを使うにはその仕様をしっかりと理解する必要があるので、むしろコードとしては複雑化しています。余計なこと(toJSONなど)をされて悩むことにもなりかねません。
とりあえず、「安全かどうか」に関しては食い違いが大きいようなので、ひとまずこれについては置いておきましょうか。
僕の思う簡潔なコードとは、無駄のないシンプルなコードです。コードゴルフみたいなのは簡潔なコードとは言えません。
そもそも論として、再帰関数を直で書こうがJSON.stringifyを使おうが、どんな処理の流れになるのかをしっかり分かっていないと使えないし書けないわけですよね。何もJSON.stringifyで再帰関数を書くことが悪いとは言い切れないと思うんですよ。
どちらの書き方にも気を付けるべきポイントはあるし、メリットもデメリットもあるんですから、コードを書く人間がその良し悪しをどう使い分けるかという話かと思いますけどね。

実際、記載いただいたコードも再帰で書かれた関数はオブジェクトの配列に対してflatとして動作しますが、JSON.stringifyで書かれた関数はflatとして動作しません。
flatで代用できるということに関して深い意味はありません。サンプルコードがちょっと悪かったですね。
件のサンプルコードでは、単純に配列の展開しか行っていなかったので、同じ結果を得るためには実はflatでも出来るんだけどねっていう話だったんですが。
多分、array変数がこのように、オブジェクトパラメータから親子関係を作っているタイプのものなら真意が正しく伝わったのかなって感じがします。
JavaScript:
const array = [
  {
    name: "ユーザー", type: "フォルダ",
    content: [ { name: "ぬこ.png", type: "ファイル", content: "猫の画像" } ],
  },
  {
    name: "デスクトップ", type: "フォルダ",
    content: [
      {
        name: "ログ保管庫", type: "フォルダ",
        content: [
          { name: "20180304.log", type: "ファイル", content: "ごにょ" },
          { name: "20180305.log", type: "ファイル", content: "ごにょごにょ" },
          {
            name: "後で消す", type: "フォルダ",
            content: [
              { name: "20180312.log", type: "ファイル", content: "ごにょごにょごにょ" },
              { name: "20180832.log", type: "ファイル", content: "ごにごにごにごに" },
            ],
          },
        ],
      },
      { name: "todo.txt", type: "ファイル", content: "今日の夕方、牛乳を買う。" },
    ],
  },
];

条件分岐やループのために新しく関数を定義するというのは、オブジェクト指向(というか手続き型)ではあまり一般的ではないように思うので、そのレベルまで要求するのはやめた方がいいかなと。
fspaceさんの仰る一般的なオブジェクト指向の概念に、JavaScriptそのものの文化がどれだけ含まれているのだろう、というのが少し疑問ですね。
そもそもJavaScript自体、他の一般的なオブジェクト指向言語とは異なる文化の流れがあり、また配列や関数のメモリ上の扱いも特殊と言われています。
(例えば new Array(1) と new Array(999999999) で、実際に確保されるメモリは大差ないとか。C言語などでは大量の配列を使ってループを作るなんてメモリの無駄以外の何物でもないですが、JSではそもそも仕組みが違うので問題にならないという。)
なので、その辺りまで含めて「条件分岐やループのために新しく関数を定義するのは一般的ではない」としているのかな?と、思いました。
結構、メソッドチェーンで毎回無名関数を使うのも、空の配列を作ってループを回すのも、GitHubで高スターを得ているJS/TSのOSSプロジェクトではよく見るんですけどね。
例えば: Vue(1680000スター), bootstrap(1420000スター), pixi.js(300000スター)

小さな処理に逐一パッケージを使用するJavaScriptの文化には反対派ですね。
最近のWEBフロントエンド開発ではむしろJSだけでなく、パッケージマネージャーを用いた開発が一般化していますし、それを否定してしまうと時代に逆行してしまうのでは・・・?と僕は思いますけどね。
Pythonにはpip、Rubyにはgemがありますし、Goにも似たようなのありますからね。

ただ、それぞれのプラグインがそれぞれの方法でトランスパイルするソースコードをまとめて管理するとなると、それもそれでなかなか面倒なものがあります。
MZではプラグイン設定でリンクを貼れるようなので、そこにGitHubのリンクを貼っておけばよさそうです。
まあこのあたりも、「エディターが悪い」論ですね。
 
最後に編集:

fspace

ユーザー
なんというか、fspaceさんとは宗教的な違いがあるようですね・・・。おそらく、どうしても互いに譲れない部分がありそうです。
まあでも、記事の最後の方に書かれていた「私論として話さざるを得ない部分が多く」の部分はそういうことですよね。

そうですね、絶対的な正解というものがない以上は意見が合わない部分はあると思います。ただ、個人的には議論して考えることに意味があると思っていて、意見が合わずとも考えを述べることが重要かなと。もちろん、無理やり付き合わせることは本意ではありませんので、面倒であれば打ち切ってもらっても結構です。

そもそも論として、再帰関数を直で書こうがJSON.stringifyを使おうが、どんな処理の流れになるのかをしっかり分かっていないと使えないし書けないわけですよね。何もJSON.stringifyで再帰関数を書くことが悪いとは言い切れないと思うんですよ。
どちらの書き方にも気を付けるべきポイントはあるし、メリットもデメリットもあるんですから、コードを書く人間がその良し悪しをどう使い分けるかという話かと思いますけどね。

JSON.stringifyで書くメリットがいまいち理解できていないというのが正直なところです。おそらく再帰関数の無限ループ云々の話になるのだと思いますが、こちらは深追いしないようにとのことなのでこれ以上はやめておきましょう。

flatで代用できるということに関して深い意味はありません。サンプルコードがちょっと悪かったですね。
件のサンプルコードでは、単純に配列の展開しか行っていなかったので、同じ結果を得るためには実はflatでも出来るんだけどねっていう話だったんですが。
多分、array変数がこのように、オブジェクトパラメータから親子関係を作っているタイプのものなら真意が正しく伝わったのかなって感じがします。

そもそもflatの挙動を再現するための関数ではなく、オブジェクトの配列は有効な入力ではないということでしょうか。早とちりしたようですみません。

fspaceさんの仰る一般的なオブジェクト指向の概念に、JavaScriptそのものの文化がどれだけ含まれているのだろう、というのが少し疑問ですね。
そもそもJavaScript自体、他の一般的なオブジェクト指向言語とは異なる文化の流れがあり、また配列や関数のメモリ上の扱いも特殊と言われています。
(例えば new Array(1) と new Array(999999999) で、実際に確保されるメモリは大差ないとか。C言語などでは大量の配列を使ってループを作るなんてメモリの無駄以外の何物でもないですが、JSではそもそも仕組みが違うので問題にならないという。)
なので、その辺りまで含めて「条件分岐やループのために新しく関数を定義するのは一般的ではない」としているのかな?と、思いました。

JavaScript独特の文化の話との繋がりがよく理解できていないので、もしかするとずれた話になるかもしれませんが、「条件分岐やループのために新しく関数を定義するのは一般的ではない」と書いたのは記事中にも書いた次のような書き換えが一般的ではないのではないか、という話になります。

JavaScript:
// let
let result;
if (conditions) {
    result = "TRUE";
} else {
    result = "FALSE";
}

// const #1
const check = conditions => {
    if (conditions) {
        return "TRUE";
    } else {
        return "FALSE";
    }
};
const result = check(conditions);


// let
let sum = 0;
for (let i = 0; i < xs.length; i++) {
    sum += xs[i];
}

// const #1
const rec = (xs, i, acc) => {
    if (i < xs.length) {
        return rec(xs, i + 1, acc + xs[i]);
    } else {
        return acc;
    }
};
const sum = rec(xs, 0, 0);

関数型言語だとそもそもletのような書き方は一般的ではなくてconstのような書き方が自然ですが、手続き型やその流れを汲むオブジェクト指向型言語ではletのような書き方のほうが自然(あるいは教科書的)なように思います。

結構、メソッドチェーンで毎回無名関数を使うのも、空の配列を作ってループを回すのも、GitHubで高スターを得ているJS/TSのOSSプロジェクトではよく見るんですけどね。

もちろん関数型的なmapやreduceはすでにJavaScriptでは一般的だと思います。これは以前にも書いた通り、積極的に使うべきだと思います。しかし、これらは配列のメソッドであって他のデータ構造では使えません。単方向リストや木構造のデータを定義し、それらを探索する際に、「letで現在ノードを持つのはよくないから再帰で書きなさい」とまではオブジェクト指向の環境では言えないように思う、というのが伝えたかったことになります。

あと最近のJavaScriptのプロジェクトはReactを始めとして関数型プログラミングの考えを積極的に取り入れているので、それをもってオブジェクト指向とするのはおかしな感じがします。

最近のWEBフロントエンド開発ではむしろJSだけでなく、パッケージマネージャーを用いた開発が一般化していますし、それを否定してしまうと時代に逆行してしまうのでは・・・?と僕は思いますけどね。

パッケージマネージャ自体は否定していません。自分で簡単に書ける数十行以内の関数、それひとつだけのためにパッケージを依存対象に加えること、より正確にはそれによる依存対象や管理ライセンス数の増加を問題と考えています。

MZではプラグイン設定でリンクを貼れるようなので、そこにGitHubのリンクを貼っておけばよさそうです。
まあこのあたりも、「エディターが悪い」論ですね。

そうですね。通常のWeb開発のような環境がツクールにデフォルトで構築されればいいんですが。
 

WTR

ユーザー
コードが短いことと簡潔であることは同じではないと思います。

加えて、簡潔であるかどうかは読み手の知識や技量にも依存する、と伝えておきたい。

JavaScript:
console.log(
$dataActors
.slice(1) // または .filter(Boolean)
.filter(({ classId }) => classId === 3)
.map(({ nickname }) => nickname)
);

なるほど簡潔だな、と今なら思いますけど
少なくとも1年前の私には、このコードの意図がサッパリ読み取れなかったはず。
配列をループで操作しようというときに for 以外の選択肢があることを知らなかったんです。

私と同等、あるいはもっと理解度が低い人は世の中に沢山いると思うのです。とくにRPGツクールユーザーには。
そもそもプログラムを書けないからRPGツクールに行き着いたわけで。

そこからようやく世界を広げようと一歩踏み出してみたのに
ループといえば for だろう、と思って書いたらお前の書き方は稚拙だと馬鹿にされ
コアスクリプトにもそう書いてあるし、おまじないだと思って真似して var って書いたら邪悪だと罵られ
いやそこまでは言ってない、と思われるかもしれませんが
この手の議論を見かけるたびにそれに近い間口の狭さを感じなくもない。

もっとも、私にわからないからレガシーな書き方にしろとは言えないんでただの愚痴なんですけども。

ところで、ここで聞いていいものか微妙な気はするんですが
Graphics.frameCount の話、実はコレ違うんじゃないかとうすうす感じながらも
書いたらそれっぽく動いちゃったんで使ってるん箇所があるんですけど何を使うのが正解なんでしょう。

ふわふわとずっと動き続けるカーソルを作ろうと思ってスプライトの update に
コード:
sprite.x = 8 * Math.sin(2 * Math.PI * (Graphics.frameCount % 120) / 120);
sprite.y = 8 * Math.sin(4 * Math.PI * (Graphics.frameCount % 120) / 120);
のような記述をしてる…
 
ただ、個人的には議論して考えることに意味があると思っていて、意見が合わずとも考えを述べることが重要かなと。もちろん、無理やり付き合わせることは本意ではありませんので、面倒であれば打ち切ってもらっても結構です。
まあこの場でJSの話で白熱してしまうと、いろんな人を置いてけぼりにしてしまうので ;^^)
ちょっと僕の返信も少々場違いだったかなーと少し反省してます。
続きはプライベートメッセージ(DM)でやりましょう!(こちらからは送れないようなので、許可するかそちらから送信お願いします。)

fspaceさんの主張を全体的に拝見する限り、極力「外部参照性を減らしたい」派なんでしょうね。
そういった考え方がベースにあるから、記事のようにされているのだろうなあ、と思いました。
その点は僕は正反対で、「便利なものがあるならそれを優先的に使えばいい」派なので、これまでの発言もその考え方がベースです。

僕は本業でNode.jsを使ったWEBフロントエンド開発を生業にしていて、OSSプロジェクトにも触れたりしますが、
fspaceさんの言う「一般的」とか「教科書的」というのは、僕の経験上ではちょっと違うなと思う事が多かったのは確かです。
もしかしたら、僕もfspaceさんと似たような記事を書いてみるのが一番なのかもしれませんね。やってみようかな?

加えて、簡潔であるかどうかは読み手の知識や技量にも依存する、と伝えておきたい。
極論を言ってしまえば、字が読めない人が文章を読んでも読めないとも言えます。下を見ればさらに下もいるし、上を見ればさらに上もいるわけです。
そんななかで、「こうして書かれたものが標準的な書き方である」という基準が定まっていれば、読み手にどのような知識や技量が必要なのかをはっきりさせることが出来るんじゃないですかね。
基準があるから、最低限何が必要なのかが分かるんです。

そこからようやく世界を広げようと一歩踏み出してみたのに
ループといえば for だろう、と思って書いたらお前の書き方は稚拙だと馬鹿にされ
コアスクリプトにもそう書いてあるし、おまじないだと思って真似して var って書いたら邪悪だと罵られ
いやそこまでは言ってない、と思われるかもしれませんが
この手の議論を見かけるたびにそれに近い間口の狭さを感じなくもない。
あくまでここで示されているのは「読みやすいソースコードの指標」ですから、義務感を感じる必要はないと思います。
プログラミングは自由に書けるんですから、自由に書いたらいいんです
読みやすいコードにするという配慮は、あくまでその指標を扱えるほどのレベルに達してから余裕があればやればいいのであって、余裕も経験も乏しいのに無理に指標に合わせる必要なんてないです。
だって、わかんないことをやれって言われてもできないじゃないですか。もっと気楽にいきましょう!

もっとも、私にわからないからレガシーな書き方にしろとは言えないんでただの愚痴なんですけども。
「新しい事を学んだほうが、より楽ができるようになる」という考え方をするといいと思いますよ。

ところで、ここで聞いていいものか微妙な気はするんですが
Graphics.frameCount の話、実はコレ違うんじゃないかとうすうす感じながらも
書いたらそれっぽく動いちゃったんで使ってるん箇所があるんですけど何を使うのが正解なんでしょう。
「ただ一つだけの正解」っていうのは無いと思いますが、いろいろなやり方があると思います。
(並列イベントで毎フレーム数字を増やす変数を使うとか、Number(new Date()) の差分をとるとか、新たなプロパティを生やすとか)
また記事にもあるように、Graphics.frameCount を「使ってはいけない」のではなく、「誤用に気を付けるべき」というだけなので、もし例えば一度に2以上増えたりしてもやろうとしている事の意図に一切の差異がなく、特に問題がない場合は、そのままの実装でも問題ないかもしれませんよ。
 
最後に編集:
  • Like
Reactions: WTR

WTR

ユーザー
そんななかで、「こうして書かれたものが標準的な書き方である」という基準が定まっていれば、読み手にどのような知識や技量が必要なのかをはっきりさせることが出来るんじゃないですかね。
標準的な書き方、という基準そのものが進化する概念なのが難しいですね。
初学者の、とくにRPGツクールユーザーにとっての基準はRPGツクールのコアスクリプトなのでは、と思っていたら
発売から5年以上経った今では標準的な書き方と呼べない代物になっていた、というような?

RPGツクールのプラグイン開発の手引きは、大抵どこの記事を読んでも
コアスクリプトのココを見てみましょう、から始まる一方で、ソレはいまいちなコードです、という裏がある…と思うとちょっと怖い。

そういう意味でもMZでコアスクリプトが一新されます、というのはいいことなんでしょうね。
教科書は信頼に足るものであってほしい。

Graphics.frameCount を「使ってはいけない」のではなく、「誤用に気を付けるべき」というだけなので
スプライトの更新に使うなら強ち間違いではない、ということなんですかね。
カウントが飛んでもスプライトは変なとこに飛んでったりはしないし…なんかそんな気もしてきた。
 
トップ