aiscript icon indicating copy to clipboard operation
aiscript copied to clipboard

乱数系関数の提案

Open salano-ym opened this issue 1 year ago • 17 comments

  • [ ] Rand:choice(a: arr<value>, rand?: fn): value 配列からランダムに1個取り出す
  • [ ] Rand:choices(a: arr<value>, count: num, rand?: fn): arr<value> 配列からランダムにcount個取り出す(重複有)
  • [ ] Rand:sample(a: arr<value>, count: num, rand?: fn): arr<value> 配列からランダムにcount個取り出す(重複無)
  • [ ] Rand:shuffle(a: arr<value>, rand?: fn): null 配列をシャッフルする

salano-ym avatar May 20 '24 11:05 salano-ym

配列の組み込みプロパティにしたほうが書きやすそうな気も?

FineArchs avatar May 20 '24 11:05 FineArchs

配列の組み込みプロパティにしたほうが書きやすそうな気も?

あんまりプロパティに詰め込むと依存度も上がって良くないかなという考えです

salano-ym avatar May 20 '24 12:05 salano-ym

依存度

詳しく聞いてもいいですか?

FineArchs avatar May 20 '24 12:05 FineArchs

依存度

ニュアンスとしては乱数側に所属させてもっと抽象的にシーケンス的なものを対象にしたいという感じ rand.choice(v: sequence<value>): value

salano-ym avatar May 20 '24 12:05 salano-ym

ハードコードの組み込み以外で型を増やせる仕組みが欲しいところ…

salano-ym avatar May 20 '24 12:05 salano-ym

シーケンス的なもの=JSのiterableみたいなものですかね? それならシーケンス的なものにもれなく.choiseなどを継承させるやり方の方が私としては好みです チェインさせやすいですし、イメージ的にこれらの関数は乱数よりも配列が主体なように思います まあエイリアスとして両方用意してもいい気がします

FineArchs avatar May 20 '24 14:05 FineArchs

他の言語でもrand.choice(seq)の形式が多いように思います 各シーケンスの内部実装に依存させるのではなく汎化されたインターフェースを通じて処理する方針です

salano-ym avatar May 20 '24 14:05 salano-ym

他の言語でもrand.choice(seq)の形式が多いように思います

あら、そうですか?python以外ではあまりイメージがないですが、事例があれば教えて下さい。

各シーケンスの内部実装に依存させるのではなく汎化されたインターフェースを通じて処理する方針です

その点では、各シーケンス的なものの上位クラスで定義されたメソッドを下位でそのまま受け継いでも独自で拡張してもよいようにした方が柔軟性が高くなると思います。

FineArchs avatar May 20 '24 22:05 FineArchs

事例があれば教えて下さい。

調べた範囲ではchoice相当の関数が明確に配列側に所属しているのはRubyとSwiftだけでした。

配列からランダムに取り出す関数がどこに所属するか
  • 配列に所属
    • Ruby
    • Swift
    • Kotlin (.shuffle)
    • Dart (.shuffle)
  • 配列に所属しない
    • C#
    • Python
    • Rust
    • PHP
    • Julia
    • D
    • Nim
    • Scala (.shuffle)
    • C++ (algorithm::)
  • 対象関数が無い(添字を経由する等)
    • C
    • Java
    • JavaScript
    • Perl
    • Go
    • Lua

salano-ym avatar May 21 '24 05:05 salano-ym

各シーケンス的なものの上位クラスで定義されたメソッドを下位でそのまま受け継いでも独自で拡張

シーケンスごとの実装に任せるということは乱択アルゴリズム自体を任せてしまうということなので、等価なシーケンスでも結果が変わってしまう可能性があります。 乱数側で処理する方がシーケンス側の責任も減って結果の一貫性を保ちやすいかと思います。

salano-ym avatar May 21 '24 05:05 salano-ym

調べた範囲ではchoice相当の関数が明確に配列側に所属しているのはRubyとSwiftだけでした。

確かにそうみたいですね…

シーケンスごとの実装に任せるということは乱択アルゴリズム自体を任せてしまうということなので、等価なシーケンスでも結果が変わってしまう可能性があります。

任せるのではなく変えてもよいとする余地を作るのが継承です。 多くの場合は共通のアルゴリズムを使うのがよいでしょうが、シーケンスの仕組み次第では既存の仕組みが非効率になってしまうこともあるでしょう。

FineArchs avatar May 21 '24 06:05 FineArchs

任せるのではなく変えてもよいとする余地を作る

変更できてしまうのが問題と思います。 選択アルゴリズムを具体的に指定しない限り結果の一貫性を保つのは難しいです。

シーケンスの仕組み次第では既存の仕組みが非効率

結果を変えずに、乱択の段階で行う必要のある最適化が思いつきません。 例えばArrayListとLinkedListの参照効率の違いであれば最適化はイテレートの段階で行うべきです。 結果が変わるような最適化であればアルゴリズムを別途指定できるようにする方がいいと思います。

salano-ym avatar May 21 '24 07:05 salano-ym

実装されてないOOPに話が逸れましたが、 要するに基本的な対象である組み込み値にあれこれ生やしすぎたくないという気持ちです。 単にチェーンの形式で書きたいというだけなら特定のオブジェクトに所属させるよりもUFCSのような糖衣構文を導入する方が健全なように思います。

salano-ym avatar May 21 '24 07:05 salano-ym

選択アルゴリズムを具体的に指定しない限り結果の一貫性を保つのは難しいです。

元になるシーケンスの仕組みが違うのに結果に一貫性を持たせる必要性は薄いように思います。 どうしても結果に一貫性を持たせる手段を用意したいというのであればやはりrand.choisearr.choiseも両方用意するのが妥当かと思います。

結果を変えずに、乱択の段階で行う必要のある最適化が思いつきません。

抽象化されたシーケンスには長さが無限になるものもあると思いますが、そのようなシーケンスは通常のアルゴリズムで乱択すると時間が無限にかかってしまいます。

要するに基本的な対象である組み込み値にあれこれ生やしすぎたくないという気持ちです。

むしろ基本的な対象こそ様々な機能を生やして便利にすべきでは?だからこそこれまで組み込みプロパティを増やす方向で進めてきていたものと思っていましたが

単にチェーンの形式で書きたいというだけならUFCSのような糖衣構文を導入する方が健全なように思います。

それは以前からやりたいと考えていましたが、要するに組み込みプロパティをユーザー側でも定義できるようにするということですから健全性の観点でいうと特にメリットはないのではと思います。

FineArchs avatar May 21 '24 08:05 FineArchs

元になるシーケンスの仕組みが違うのに結果に一貫性を持たせる必要性は薄いように思います

ArrayListかLinkedListかのような内部実装が結果に影響するのが良いとは思いません。気軽に変更できてほしいです。 (全員で同じ問題をする日替わりゲーム等がリファクタリングで違うものが出てくると困るので)

抽象化されたシーケンスには長さが無限になるものもある

有限か無限かは停止性にも関わってくるので最適化というより根本的なアルゴリズム自体が変わると思います。 いくつか他言語の実装を見ましたが、有限でランダムアクセス可能なもの(≒配列)のみを対象にしていました。

基本的な対象こそ様々な機能を生やして便利にすべきでは?

個人的には1オブジェクトの責任は小さくあってほしい主義です。(プロジェクト自体の方針は把握してませんが……)

組み込みプロパティをユーザー側でも定義できるようにする

ただの関数をメソッド呼び出しのように書けるようにするものなのでプロパティに追加して所属させるのとは別物です(是非は別として)。

salano-ym avatar May 21 '24 09:05 salano-ym

まあ色々ありますが、それら全てをひっくるめてもチェインの形で書けるメリットが上回ると思います。 ネストが増えるのは悪です。

FineArchs avatar May 21 '24 09:05 FineArchs

ArrayListかLinkedListかのような内部実装が結果に影響するのが良いとは思いません。気軽に変更できてほしいです。 (全員で同じ問題をする日替わりゲーム等がリファクタリングで違うものが出てくると困るので)

確かにその点だけ見るとプラスマイナスでいえばマイナスでしょうが、マイナス幅は小さいと思います。 日替わりゲームの例で言うなら、そもそもリファクタリングするのにアルゴリズムが変わらない場合のほうが少ないのではないでしょうか?

有限か無限かは停止性にも関わってくるので最適化というより根本的なアルゴリズム自体が変わると思います。 いくつか他言語の実装を見ましたが、有限でランダムアクセス可能なもの(≒配列)のみを対象にしていました。

アルゴリズムが異なっていても、例えばchoiceなら「シーケンス状のものから乱数番目の要素を取得する」という共通部分を持った動作が同じ名前で呼び出せるというのは直感性・学習容易性において大きなアドバンテージになると考えられます。 また、これはもっと早く言及すべきでしたが、AiScriptは他の言語の真似をするために作られた言語ではないはずですから、他言語を参考にするのはそうしなければ天秤がどちらにも傾かないような場合のみに限るべきではないでしょうか?

個人的には1オブジェクトの責任は小さくあってほしい主義です。(プロジェクト自体の方針は把握してませんが……)

ただの関数をメソッド呼び出しのように書けるようにするものなのでプロパティに追加して所属させるのとは別物です(是非は別として)。

着火した身でいうのもアレですが、本筋にあまり関わらなそうなのと長文しか出せない気がするのと論点を絞りたいのとでこの場では返答を割愛します。(また機会があれば・・・)

FineArchs avatar May 26 '24 08:05 FineArchs