7

皆さん、私はC++から次のコードを持っています。

for (int i=0; i < nObstacles; i++)
{
  int x,y;
  bool bAlreadyExists;
  do {          
    x = rand() % nGridWidth;
    y = rand() % nGridHeight;                   
  } while (HasObstacle(x, y));
  SetObstacle(x, y, true);      
}

問題なく直接F#に変換できます。

let R = new System.Random()
for i=0 to nObstacles do
        let mutable bGoodToGo = false;
        let mutable x =0;
        let mutable y = 0
        while not bGoodToGo do
            x <-R.Next(nWidth)
            y <-R.Next(nHeight)
            bGoodToGo <- IsEmptyAt x y
        board.[x,y]<-Obstacle;

もちろん、これはF#の使用方法ではないため、ほとんどの人がうんざりするでしょう。このコードには、do-whileループや可変データなど、F#の「アンコッシャー」の概念がいくつかあります。

しかし、私が興味を持っているのは、不変のデータを使用した「適切な」F#変換であり、ある種のdo-whileと同等です。

4

2 に答える 2

5

while最初のステップとして、ループ内のループを単純化する方法を確認できforます。1つのオプションは、Seq.initInfinite任意の数のランダムなX、Y座標を与えるシーケンスを生成するために使用することです。次に、を使用Seq.findして、空のボードフィールドを参照する最初のフィールドを見つけることができます。

また、タプルを取るように変更isEmptyし(部分機能アプリケーションを使用するための引数として渡すことができるようにSeq.find)、より標準的なF#スタイルに従うようにいくつかの名前を変更しました(通常、ハンガリー語の命名表記は使用しません)。

let isEmpty (x, y) = board.[x,y] = -1

let rnd = new System.Random()
for i = 0 to obstacleCount do
  let x, y =
    // Generate infinite sequence of random X Y coordinates
    Seq.initInfinite (fun _ -> rnd.Next(width), rnd.Next(height))
    // Find first coordinate that refers to empty field
    |> Seq.find isEmpty
  // We still have mutation here
  board.[x,y] <- Obstacle

これは非常にエレガントな機能ソリューションだと思います。命令型ソリューションよりも少し遅いかもしれませんが、重要なのは、機能的なスタイルを使用すると、実装を習得すると、実装の記述と変更が容易になるということです(命令型スタイルは常に最適化として使用できます)。

すべての可変状態を回避するには、最初に障害物の場所を生成してから、配列を初期化する必要があります。たとえば、必要な長さになるまで、セットに新しい座標を再帰的に追加できます。次に、以下を使用して配列を生成できますArray2D.init

let rec generateObstacles obstacles =
  if Set.count obstacles = obstacleCount then obstacles
  else 
    // Try generating new coordinate and add it to the set
    // (if it is already included, this doesn't do anything)
    obstacles
    |> Set.add (rnd.Next(width), rnd.Next(height))
    |> generateObstacles

let obstacles = generateObstacles Set.empty
Array2D.init width height (fun x y -> 
  if obstacles.Contains(x, y) then Obstacle else Empty)

これは実際には短くはなく、少し遅くなるので、最初の解決策に固執します。しかし、それは再帰とセットを示す素晴らしいエクササイズです...

于 2011-03-22T23:44:54.250 に答える
4

これが私の試みです:

Seq.initInfinite (fun _ -> rnd.Next(width), rnd.Next(height))
|> Seq.filter (fun (x, y) -> IsEmptyAt x y)
|> Seq.distinct
|> Seq.take nObstacles
|> Seq.iter (fun (x, y) -> board.[x,y] <- Obstacle)

ボードが最初に空の場合は、Seq.filterを削除できます。Tomasソリューションと同様に、無限の位置シーケンスを生成します。次に、不良で重複した位置を削除します。最後に、nObstaclesの最初の要素でボードを更新します。

于 2011-03-23T00:41:35.520 に答える