首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >haskell :如何使用gtk和函数映射绘制一些像素(副作用)

haskell :如何使用gtk和函数映射绘制一些像素(副作用)
EN

Stack Overflow用户
提问于 2016-04-02 15:05:30
回答 1查看 157关注 0票数 1

我正在努力学习gtk2hs,一个API允许haskell程序使用窗口、菜单、工具栏和图表。

我想画Mandelbrot集,所以我开始写东西,但是我被困在了最后,也就是当我不得不用副作用来画Mandelbrot集的每一点时。

数据是:我有一个300 of *200 of的画布(一个绘图区域)和一个函数mandelbrot :: Float -> Float -> Bool,如果点在mandelbrot集中,它的输出是真的,否则为false。

要实现的工作是:对于每个像素(宽度:0到300,高度:0到200),将坐标转换为-2.2*-2.2.2.2,调用mandelbrot函数,如果结果为真,则调用绘制点的包开罗的函数。(函数是C.rectangle a b 1 1。)

我的尝试:

代码语言:javascript
复制
example :: Double -> Double -> C.Render ()
example width height = do
  setSourceRGB 0 0 0
  setLineWidth 1

  let 
    affiche a b = do 
      if (mandelbrot a b) then 
        C.rectangle a b 1 1
        return()
      return ()

    colonnes = [0..299]
    lignes = [0..199]
  in 
    map (\t -> t/300*4-2) colonnes
    map (\t -> t/200*4-2) lignes
    map affiche (zip (colonnes,lignes))
    stroke            -- it displays the changes on the screen

它触发错误:-在第二个return

代码语言:javascript
复制
Error: Parse error: return

谢谢

编辑:非常感谢你的回答。我大大地改进了我的程序,但我仍然有错误。

以下是最新版本:

代码语言:javascript
复制
affiche :: Double -> Double -> Render()
affiche a b = when (mandelbrot a b) $ C.rectangle a b 1 1

colonnes = [ t/300.0*4.0-2.0 | t<-[0.0..299.0] ]
lignes = [ t/200.0*4.0-2.0 | t<-[0.0..199.0] ]


example :: Double -> Double -> C.Render ()
example width height = do
    setSourceRGB 0 0 0
    setLineWidth 1

    mapM_ (\ (a, b) -> affiche a b) (zip (colonnes,lignes))
    stroke

错误是:

(在“mapM_”行的所有2处):

代码语言:javascript
复制
*   Couldn't match type ‘[(a0, b0)]’ with ‘(Double, Double)’
  Expected type: [b0] -> (Double, Double)
    Actual type: [b0] -> [(a0, b0)]
  In the second argument of ‘mapM_’, namely
    ‘(zip (colonnes, lignes))’
  In a stmt of a 'do' block:
    mapM_ (\ (a, b) -> affiche a b) (zip (colonnes, lignes))


*  Couldn't match expected type ‘[a0]’
              with actual type ‘([Double], [Double])’
  In the first argument of ‘zip’, namely ‘(colonnes, lignes)’
  In the second argument of ‘mapM_’, namely
    ‘(zip (colonnes, lignes))’
  In a stmt of a 'do' block:
    mapM_ (\ (a, b) -> affiche a b) (zip (colonnes, lignes))

我还有另外一个问题:请确认如果一个函数返回类型为"Render()“,那么它中的所有语句都应该返回这个类型的值。谢谢

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-04-02 15:43:24

Haskell中的if构造不像其他语言那样工作:它实际上相当于C类语言中的? :三元条件运算符。这意味着,如果您使用if ... then,您必须始终有一个else分支。

原因是: Haskell并不是强制性的;你没有写出应该做什么,而是应该写什么结果。在命令式语言中,您什么也做不了,但是在Haskell中,您总是需要指定一些结果。

现在,一元do块当然基本上是一种命令式的嵌入式语言。在这里,您只需指定一个本地结果应该是无操作操作,以实现此时不做任何操作:

代码语言:javascript
复制
    affiche a b = do 
      if mandelbrot a b then 
        C.rectangle a b 1 1
        return ()   -- note: `return` isn't needed here
       else
        return ()
      return ()  -- nor here

使用when组合器编写此代码的一个较短的方法是,它基本上是在“否则”分支中有一个return ()的if-然后-else:

代码语言:javascript
复制
    affiche a b = when (mandelbrot a b) $ C.rectangle a b 1 1

您的代码还有另一个问题:

代码语言:javascript
复制
  in 
    map (\t -> t/300*4-2) colonnes      -- number list
    map (\t -> t/200*4-2) lignes        -- number list
    map affiche (zip (colonnes,lignes)) -- list of `Render ()` actions
    stroke                              -- `Render ()` action

在这里,你只需写出几个完全不同类型的表达式。你不能那样做!Haskell对这些表达应该做什么?

很明显你想要执行一系列的动作。因此,您再次需要do

代码语言:javascript
复制
  in do
    ...

但是,您从map (\t -> t/300*4-2) colonnes获得的这些列表根本不是操作。你不能执行它们,只需评估它们。在这种情况下,显然您希望colonnes是将该函数映射到列表[0..299]上的结果。好吧,那你为什么不马上指定呢?

代码语言:javascript
复制
    colonnes = map (\t -> t/300*4-2) [0..299]
    lignes = map (\t -> t/200*4-2) [0..199]

或者,为什么不作为一个清单理解

代码语言:javascript
复制
    colonnes = [ t/300*4-2 | t<-[0..299] ]
    lignes = [ t/200*4-2) t<-[0..199] ]

最后,您需要将affiche映射到列表上。这实际上是一张“一元行动图”。这个函数是mapM_,而不是map

代码语言:javascript
复制
    colonnes = [ t/300*4-2 | t<-[0..299] ]
    lignes = [ t/200*4-2) t<-[0..199] ]
  in
    mapM_ affiche $ zip colonnes lignes
    stroke

快到了,但不太好。如果affiche的签名需要两个数字的元组(从zip获得这样的元组),那么这个方法就可以了。然而,affiche a b = do意味着函数是咖喱 (与Haskell中的惯例一样,特别是对于zip!)。不过,你可以很容易地撤销它。

代码语言:javascript
复制
    mapM_ (uncurry affiche) $ zip colonnes lignes

但是在这个特定的情况下,我实际上建议

代码语言:javascript
复制
    affiche :: (Float,Float) -> C.Render ()
    affiche (a,b) = when (mandelbrot a b) $ C.rectangle a b 1 1

因为ab确实属于一起,所以形成一个单一的坐标规范。

还有一个问题:这里使用了Float数字。好吧..。在Haskell,这样做是没有意义的。无论如何,rectangle需要Double作为参数,所以要么切换affichemandelbrot以接受Double,要么在传递到rectangle之前转换ab

代码语言:javascript
复制
    affiche :: (Float,Float) -> C.Render ()
    affiche (a,b) = when (mandelbrot a b)
                  $ C.rectangle (realToFrac a) (realToFrac b) 1 1

哦,还有一件事:我认为zip在这里做的不对。但是..。你自己想想..。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36374883

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档