C++到F#顺利翻译

use*_*943 7 c++ f# immutability code-translation

嘿伙计们,我有以下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);      
}
Run Code Online (Sandbox Code Playgroud)

我可以直接将它翻译成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;
Run Code Online (Sandbox Code Playgroud)

当然,这可能会让你们大多数人感到畏缩,因为这不是F#的意思.这段代码为F#提供了一些"unkosher"概念,例如do-while循环和可变数据.

但我有兴趣看到的是一个带有不可变数据的"适当的"F#转换,以及某种等同的do-while.

Tom*_*cek 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
Run Code Online (Sandbox Code Playgroud)

我认为这是非常优雅的功能解决方案.它可能比命令式解决方案慢一点,但关键是功能样式使得一旦学习它就更容易编写和更改实现(您可以始终使用命令式样式作为优化).

为了避免所有可变状态,您需要先为障碍物生成位置,然后初始化阵列.例如,您可以递归地向集合添加新坐标,直到它具有所需的长度.然后你可以使用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)
Run Code Online (Sandbox Code Playgroud)

这不是真的更短,而且会慢一点,所以我坚持第一个解决方案.但是,这是一个很好的练习,显示递归和设置...


Lau*_*ent 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)
Run Code Online (Sandbox Code Playgroud)

如果板子一开始是空的,您可以删除 Seq.filter。就像托马斯的解决方案一样,它生成无限的位置序列。然后,它会删除错误和重复的位置。最后,它用 nObstacles 第一个元素更新棋盘。