C++到 F# 平滑翻译

发布于 2024-10-26 06:22:49 字数 794 浏览 5 评论 0原文

大家好,我有以下 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# 的“不合规矩”概念,例如 do-while 循环和可变数据。

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

Hey folks, I have the following piece of code from 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);      
}

I can translate it to F# directly with no problem.

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;

Of course this probably makes most of you cringe, since this is not the way F# was meant to be used. This code has some "unkosher" concepts for F#, such as do-while loops and mutable data.

But what I would be interested in seeing is a "proper" F# translation with immutable data, and some sort of do-while equivalent.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

〃温暖了心ぐ 2024-11-02 06:22:49

第一步,您可以了解如何简化 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)

这实际上并不短,而且会慢一些,所以我会坚持使用第一个解决方案。然而,这是一个很好的练习,展示了递归和集合......

As a first step, you can take a look how to simplify the while loop inside the for loop. One option is to use Seq.initInfinite to generate a sequence that will give you any number of random X, Y coordinates. Then you can use Seq.find to find the first one that refers to an empty board field.

I also changed isEmpty to take a tuple (so that you can pass as argument to Seq.find using partial function application) and I changed some names to follow more standard F# style (you generally wouldn't use hungarian naming notation):

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

I think this is quite elegant functional solution. It may be a bit slower than the imperative solution, but the point is that functional style makes it easier to write & change the implementation once you learn it (You can always use imperative style as optimization).

To avoid all mutable state, you'll need to generate locations for obstacles first and then initialize the array. For example, you could recursively add new coordinates to a set until it has the required length. Then you can generate array using 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)

This isn't really shorter and it will be a bit slower, so I'd stick to the first solution. However, it is a nice exercise showing recursion and sets...

和影子一齐双人舞 2024-11-02 06:22:49

这是我的尝试:

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。就像托马斯的解决方案一样,它生成无限的位置序列。然后,它会删除错误和重复的位置。最后,它用 nObstacles 第一个元素更新棋盘。

Here is my try:

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)

You can remove the Seq.filter if the board is empty at the beginning. Like in Tomas solution, it generates an infinite sequence of positions. Then, it removes bad and duplicated positions. Finally, it updates the board with the nObstacles first elements.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文