2011-03-22 125 views
7

嘿夥計們,我從C++有以下一段代碼。C++到F#平滑翻譯

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#意圖使用的方式。這段代碼對F#有一些「unkosher」概念,比如do-while循環和可變數據。

但是我會感興趣的是看到的是一個「正確的」F#翻譯與不可變的數據,以及某種類似的do-while等價。

回答

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的第一個元素更新板子。

5

作爲第一步,您可以看看如何簡化for循環內的while循環。一種選擇是使用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) 

這是不是真的更短,這將是一個有點慢,所以我會堅持到第一個解決方案。然而,這是一個很好的鍛鍊顯示遞歸和套...

+0

我認爲你的第一個解決方案很有趣。 「讓我覺得」開箱即用「這裏可能不需要100%的不可變性,因爲無論如何董事會有可能變化,我只是希望避免(x,y)的可變性, – user627943 2011-03-23 03:45:49