2

この質問は、私が以前に尋ねた別の質問に関連しています。

JSON ファイルからデータを読み取り、作成したデータ型に解析しようとしています。

{
  "rooms":
  [
    {
      "id": "room1",
      "description": "This is Room 1.  There is an exit to the north.\nYou should drop the white hat here.",
      "items": ["black hat"],
      "points": 10,
      "exits": [
        {
          "direction": "north",
          "room": "room2"
        }
      ],
      "treasure": ["white hat"]
    },
    {
      "id": "room2",
      "description": "This is Room 2.  There is an exit to the south.\nYou should drop the black hat here.",
      "items": [],
      "points": 10,
      "exits": [
        {
          "direction": "south",
          "room": "room1"
        }
      ],
      "treasure": ["black hat"]
    }
  ]  
}

部屋のユーザー定義タイプは次のとおりです。

type room = {
  room_id          :  int ;
  room_description :  string ;
  room_items       :  item list ;
  room_points      :  int ;
  room_exits       :  exit list ;
  room_treasure    :  item list ;
}
and exit = direction * room

ただし、部屋には「出口」フィールドがあり、それ自体が「部屋」タイプです。次に、 room1 のレコードを作成しようとすると最初に room2 を定義する必要がありますが、 room2を定義するにはroom1を知る必要があります。これは循環型のようです。

誰でもこれで私を助けることができますか?

4

2 に答える 2

2

OCaml の不変で熱心なサブセットに固執する場合、任意の循環構造を構築する実際の方法はありません。問題はまさにあなたが述べているとおりです。

を使用して循環構造の特定の例を構築することは可能let recですが、(たとえば) JSON の解析中に任意の構造を構築するためにこれを拡張できるとは思いません。

不変データの要件を削除することで、問題を解決できます。他のルームへのリンクを OCaml 参照 (可変フィールド) にすると、JavaScript の命令部分で行うのとほとんど同じように循環構造を構築できます。

room_exitsこれを機能させる 1 つの方法は、リストではなく配列を使用することです。OCaml 配列は変更可能です。

3 つのノードにまたがる完全なグラフを作成するコードを次に示します (隣接ノードのみを含む単純なノード タイプの場合)。

# type node = { nabes: node array };;
type node = { nabes : node array; }
# type graph = node list;;
type graph = node list
# let z = { nabes = [||] };;
val z : node = {nabes = [||]}
# let temp = Array.init 3 (fun _ -> { nabes = Array.make 2 z});;
val temp : node array =
  [|{nabes = [|{nabes = [||]}; {nabes = [||]}|]};
    {nabes = [|{nabes = [||]}; {nabes = [||]}|]};
    {nabes = [|{nabes = [||]}; {nabes = [||]}|]}|]
# temp.(0).nabes.(0) <- temp.(1);;
- : unit = ()
# temp.(0).nabes.(1) <- temp.(2);;
- : unit = ()
# temp.(1).nabes.(0) <- temp.(0);;
- : unit = ()
# temp.(1).nabes.(1) <- temp.(2);;
- : unit = ()
# temp.(2).nabes.(0) <- temp.(0);;         
- : unit = ()
# temp.(2).nabes.(1) <- temp.(1);;
- : unit = ()
# let k3 : graph = Array.to_list temp;;
val k3 : graph =
  [{nabes =
     [|{nabes = [|<cycle>; {nabes = [|<cycle>; <cycle>|]}|]};
       {nabes = [|<cycle>; {nabes = [|<cycle>; <cycle>|]}|]}|]};
   {nabes =
     [|{nabes = [|<cycle>; {nabes = [|<cycle>; <cycle>|]}|]};
       {nabes = [|{nabes = [|<cycle>; <cycle>|]}; <cycle>|]}|]};
   {nabes =
     [|{nabes = [|{nabes = [|<cycle>; <cycle>|]}; <cycle>|]};
       {nabes = [|{nabes = [|<cycle>; <cycle>|]}; <cycle>|]}|]}]

中間構造を介してリンクすることで問題を解決することもできます。たとえば、部屋名を部屋にマップするディクショナリを作成できます。そうすれば、他のルームへのリンクは名前だけを使用できます (OCaml 値への直接リンクではなく)。私は過去にこの方法を使用しましたが、かなりうまく機能します。(実際、これは JSON が暗黙的に機能する方法です。)

于 2015-12-19T23:40:06.077 に答える
1

そのため、前の回答では、 functionroom_exitsGameではなくインターフェイスに入れましたRoom。この背後にある直感は、部屋の出口、つまり他の部屋は部屋の一部ではないということです。「部屋は壁、宝物、その他の部屋である」などの構造を定義すると、部屋以上のものを定義することになり、基本的には迷路全体を定義することになります。したがって、部屋は単なる部屋、つまりコンテンツです。部屋のつながり方はMaze. (Game以前の回答でこれを使用しましたが、おそらくMazeより良い名前です)。

要約すると、特定のケースでは、部屋のデータ表現から他の部屋への参照を削除し、迷路情報をmaze(またはgame) データ構造内の連想コンテナーとして保存するだけで済みます。

 type exits = (dir * room) list

 type maze = {
   ...
   entry : room;
   rooms : exits Room.Map.t
 }

または、より正確にはDir.Map、連想リストの代わりに連想コンテナーとして使用できます。

type exits = room Dir.Map.t

後者の表現では、方向ごとに部屋が 1 つしかないことが保証されます。

注:上記の定義は、インターフェースをRoom実装し、ライブラリを使用していることを前提としています。(コースページからRWOへのリンクがあったことを覚えているので、あなただと思います)。同等のインターフェースを実装するには、関数とインターフェースを実装する必要があります。型ジェネレーターを使用すると簡単です。基本的には次のようになります。ComparableCorecompareSexpable

module Room = struct 
  type t = {
    name : string;
    treasures : treasure list;
    ...
  } with compare, sexp

  include Comparable.Make(struct 
    type nonrec t = t with compare, sexp
  end)
end

は、ファンクターがインターフェイスを実現するために必要な関数と、関数のペアを with compare, sexp自動的に生成します。comparesexp_of_tt_of_sexpComparable.MakeComparable

注:もちろん、この時点で多すぎる場合は、String.Map.tデータ構造を使用して、部屋の名前でルックアップを実行できます。これは悪い考えではありません。

于 2015-12-21T15:14:27.217 に答える