関数型プログラミングとテキストアドベンチャー


14

これは主にFPに関する理論的な質問ですが、私のポイントを説明するためにテキストアドベンチャー(古い学校のZorkなど)を取り上げます。FPを使用してステートフルシミュレーションをどのようにモデル化するかについて、ご意見をお聞かせください。

テキストアドベンチャーには、本当にOOPが必要です。たとえば、すべての「部屋」はRoomクラスのインスタンスであり、基本的なItemクラスとItem<Pickable>持ち運びできるものなどのインターフェイスを持つことができます。

FPでのワールドモデリングの動作は異なります。特に、ゲームの進行(オブジェクトの移動、敵の敗北、スコアリングの増加、プレイヤーの位置の変更)に応じて変化する必要があるワールドに不変性を適用する場合。私Worldはそれをすべて備えた単一の大きなオブジェクトを想像します。探索できる部屋は何か、それらはどのようにリンクされているか、プレイヤーは何を運んでいるか、どのレバーがトリガーされたか。

純粋なアプローチは、基本的にこの大きなオブジェクトを任意の関数に渡し、それらによって返される(おそらく変更される)ことだと思います。例えば、私は新しい部屋に変更されてそれをmoveToRoom取得Worldして返す関数を持っています。World.player.locationWorld.rooms[new_room].visited = True

これがより「正しい」方法であっても、そのために純粋さを強制しているようです。プログラミング言語によっては、この潜在的に非常に大きなWorldオブジェクトをやり取りするのに費用がかかる場合があります。また、すべての関数がWorldオブジェクトにアクセスする必要がある場合があります。たとえば、部屋は浸水する可能性があるため、他の部屋でトリガーされたレバーに応じてアクセス可能またはそうでない場合がありますが、プレイヤーがライフジャケットを持っていれば、とにかく入ることができます。モンスターはプレイヤーが別の部屋でいとこを殺害したかどうかによって攻撃的であるかもしれません。この手段roomCanBeEnteredの機能は、アクセスする必要があるWorld.player.invetoryWorld.roomsdescribeMonsterアクセスする必要があるWorld.monstersので、(基本的に、あなたがしなければなりません負荷全体を渡します)。これは、特にFPのプログラミングスタイルが優れている場合でも、グローバル変数を呼び出すように思えます。

この問題をどのように解決しますか?


4
「プログラミング言語によっては、この非常に大きな可能性のあるWorldオブジェクトをやり取りするのに費用がかかる場合があります。」おそらく参照渡しされます。「また、すべての関数がWorldオブジェクトにアクセスする必要があるかもしれません。」すべての機能がゲームの状態全体にアクセスする必要があると信じることは難しいと思います。
ドーバル

2
クリス・マーテンの研究は興味深いと思います。宣言型言語でインタラクティブなフィクションを作成する方法を示すことを目的としています。github.com/chrisamaphone/interactive-lp
ダニエル

2
このようなゲームを機能的な方法でプログラミングするための著者のアプローチについて説明しているこのブログをご覧ください。この投稿は特に重要です。
-gallais

3
この質問が、@ EricLippertがOcamlでZマシン(の一部)の実装に関する一連の記事を書くという後の決定に影響を与えたかどうか疑問に思う必要があります...?
ジュール

1
@Jules興味のある人のための、そのシリーズの開始へのリンク:ericlippert.com/2016/02/01/west-of-house
KChaloux

回答:


4

関数型言語はオブジェクトの代わりにデータ構造と分離された関数を使用することに注意してください。たとえば、代わりに部屋のセットとインベントリアイテムのリストをワールドとして作成します。

また、理想的には、関数全体に渡すのではなく、関数に与えるデータの量を、実際に必要な量に制限します(たとえば、世界から1つの関連する部屋を抽出します。もちろん、完全に相互依存する世界は難しい場合があります)分ける)。結果は、ワールドデータ構造に再組み込まれ、新しい状態が作成されます。状態を使用せずに状態をモデル化することはできません。あなたが言うように、いくつかのものは本質的に突然変異を必要とします。

最も実用的な関数型言語は、突然変異を直接実現する方法(HaskellのSTモナドまたはClojureのトランジェントなど)またはそれを効率的にシミュレートする方法を提供します(多くの場合、データ構造の変更されていない部分を再利用することで(Clojureのデフォルトのデータ構造が良い例です)) )。どちらの方法でも純度は維持されます。

変化させる必要がある状態の量は限られているように見えるので、パフォーマンスの問題についてはあまり心配せず、(すでに最適化されている可能性のある)素朴な機能的アプローチを採用します。

私が見た別のオプションは、個々の機能から世界の一部を変更するための指示のみを返し、それらに従って世界を更新することです。これを説明する一連のブログ投稿はhttp://prog21.dadgum.com/23.htmlで利用できます。

これらの答えは両方とも、完全に相互依存するものを定義によってセグメント化できないため世界全体を機能に渡さないよりも、変更を整理する方法を扱っていますが、あなたの場合にできるだけ最善を尽くしてみてください機能的だけでなく、優れた実践。


0

私自身、間違いなく、問題の言語が何らかの形でデータベースにアクセスする能力を調べます。同時に世界の状態を変更するイベントのほとんどは、単にディスクに記録され、現在の部屋内の現在のプレーヤーには影響しません(爆発などの特別な状況、またはMMOで、ドアを開くスイッチは除きます)リモート、他のプレイヤーの叫びなど)。

そのため、現在のクライアントは、Roomオブジェクトとオブジェクトに直接影響するものを認識するだけで十分です。noticableEventsOutsideRoom単純にRoomデータベースへの最近の変更の影響を受けるサブクラスになり、ゲームオブジェクトが非常に小さくなります。


私は、このアプローチは、経路探索または(例えば近くのモブの農業など)地元のイベントをトリガするためにあまり行っていない理解して、私が過去に虐待データベースに知られてきた...私はおそらくちょうどにコールを送りたいupdate mobs set agro=1 where distance<5となりますそれで終わりました。たぶんそれはベストプラクティスではありませんが、それは私の目的に合っています。データベースを経由して経路探索用として、1は常に使用することができダイクストラの最短経路アルゴリズム ...
Ayelis

0

実際の解決策は、すべてを大きなWorldオブジェクトに収集し、それを渡すことではありません。代わりに、扱う関数のタイプを正確に指定することをお勧めします。以下に例を示します。

BAD:
   f :: (World, Int) -> World

Good:
   f :: (Int,Int,Int,Int) -> World

悪い例は、既存のオブジェクトを変更しようとしていますが、良い例は、ゲームの状態空間から世界を作成しようとしています。基本的に、世界を作成するには、正しい世界を選択するために必要なすべてのデータを知る必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.