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# 的本意。这段代码有一些 F# 的“unkosher”概念,例如 do-while 循环和可变数据。

但我有兴趣看到的是具有不可变数据的“正确”F# 翻译,以及某种等效的 do-while。

4

2 回答 2

5

作为第一步,您可以看一下如何在循环中简化while循环for。一种选择是用于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 回答