实施Bresenham算法

spi*_*boi 3 haskell

嗨,我正在尝试编写一些基本代码来实现Bresenham算法,但我不得不尝试使用round/.我的代码是:

bresenhamAlgorithm :: Coord -> Coord -> Int -> Int
bresenhamAlgorithm (x1, y1) (x2, y2) x'= round $ (fromIntegral ((y2 - 
y1) * (x' - x1)) / fromIntegral (x2 - x1)) + y1
Run Code Online (Sandbox Code Playgroud)

我不断收到No instance for (Fractional Int) arising from a use of ‘/’No instance for (RealFrac Int) arising from a use of ‘round’错误存在的.我不明白,因为我相信fromIntegral会将分子和分母转换为允许使用该/操作的分数?

Cub*_*bic 5

我不明白,因为我认为fromIntegral会将分子和分母转换为允许使用/ operation的小数?

是的,你的理解基本上是正确的,但你做了一个小的疏忽.


这里的代码格式略有不同:

bresenhamAlgorithm :: Coord -> Coord -> Int -> Int
bresenhamAlgorithm (x1, y1) (x2, y2) x'= round $
  (
    fromIntegral ((y2 - y1) * (x' - x1)) 
  / fromIntegral (x2 - x1)
  ) + y1
Run Code Online (Sandbox Code Playgroud)

这是(因为+绑定强于$)相当于:

bresenhamAlgorithm :: Coord -> Coord -> Int -> Int
bresenhamAlgorithm (x1, y1) (x2, y2) x'= round (
  (
    fromIntegral ((y2 - y1) * (x' - x1)) 
  / fromIntegral (x2 - x1)
  ) + y1)
Run Code Online (Sandbox Code Playgroud)

这里的问题实际上既不是也不round/,最后是你+ y1的问题.因为y1已经知道Int,类型检查器试图统一

fromIntegral ((y2 - y1) * (x' - x1)) / fromIntegral (x2 - x1)
Run Code Online (Sandbox Code Playgroud)

Int,当然失败,因为/不允许Int.

解决方案是添加另一个 fromIntegral:

bresenhamAlgorithm :: Coord -> Coord -> Int -> Int
bresenhamAlgorithm (x1, y1) (x2, y2) x'= round $
  (
    fromIntegral ((y2 - y1) * (x' - x1)) 
  / fromIntegral (x2 - x1)
  ) + fromIntegral y1 -- we need y1 as a fractional
Run Code Online (Sandbox Code Playgroud)

或者你也可以把所有东西+ y1包括在内 - 包括round括号中的- 也可以.


现在之前未捕获的原因是因为fromIntegral实际上可以将整数类型转换为任何数字类型...包括它们自己.所以在这种情况下,因为表达是如何写的typechecker推断,你的意思是fromIntegral要转换Int为... Int一次.这不是你的意思,但编译器不知道.

如果我们添加一个小辅助函数:

toDouble :: Int -> Double
toDouble = fromIntegral
Run Code Online (Sandbox Code Playgroud)

并在你的定义中使用它而不是 fromIntegral

bresenhamAlgorithm :: Coord -> Coord -> Int -> Int
bresenhamAlgorithm (x1, y1) (x2, y2) x'= round $
  (
    toDouble ((y2 - y1) * (x' - x1)) 
  / toDouble (x2 - x1)
  ) + y1
Run Code Online (Sandbox Code Playgroud)

为了强制转换,我们实际上得到了一个更有用的错误消息:

• Couldn't match expected type ‘Double’ with actual type ‘Int’
• In the second argument of ‘(+)’, namely ‘y1’
  In the second argument of ‘($)’, namely
    ‘(toDouble ((y2 - y1) * (x' - x1)) / toDouble (x2 - x1)) + y1’
  In the expression:
    round
      $ (toDouble ((y2 - y1) * (x' - x1)) / toDouble (x2 - x1)) + y1

    ) + y1
        ^^
Run Code Online (Sandbox Code Playgroud)

  • @spicyboi这不是一个错误,它只是一个警告,告诉你,因为你没有真正告诉编译器什么类型`fromIntegral`应该返回它只是为你选择了一个(`Double`).如果你使用这个答案中的`toDouble`函数(它只是限制`fromIntegral`),消息就会消失(把它想象成"是的,这就是我的意思",你可以告诉编译器). (2认同)