像命令式程序一样跟踪 Haskell 中的程序变量

发布于 09-11 02:54 字数 1067 浏览 6 评论 0原文

每次用户与我的程序交互时,我很难找出如何进行更改。很难解释,所以这里有一个例子(Haskell + wxhaskell):

simulate :: Int -> Frame () -> IO ()
simulate qNr window = do
 fdata <- readFile "qarchive"
 case (split (listTake (split fdata '\n') qNr 0) '#') of
  (qst:a:b:c:coralt:answer:x) -> do
   -- GUI Controls Initialization (buttons,text,etc...) 
   nextButton <- button window [text := "Next Question ->", on command := set infoField [text := "Next Question"], position := pt 185 155]
   return ()

main :: IO ()
main = start gui

gui :: IO ()
gui = do
 window <- frame [text := "Question program", clientSize := sz 640 480]
 headerText <- staticText window [text := "Title Text", fontSize := 20, position := pt 5 5]
 simulate 0 window
 return ()

我希望在按下“下一个问题”按钮时更改一些小部件。我想将这些小部件更改为我从文件中读取的某些值。如何跟踪当前问题的编号?我实际上无法将 QuestionNumber 作为变量递增,因为 Haskell 不允许这样的事情。我想还有另一种方法可以做到这一点。

示例:

Initialize GUI
Read data from file
If button is pressed, increment question number by 1 and change widgets.

您如何以功能性方式解决此类问题?

I'm having a hard time finding out how to make something change every time the user interacts with my program. It's hard to explain so here's an example (Haskell + wxhaskell):

simulate :: Int -> Frame () -> IO ()
simulate qNr window = do
 fdata <- readFile "qarchive"
 case (split (listTake (split fdata '\n') qNr 0) '#') of
  (qst:a:b:c:coralt:answer:x) -> do
   -- GUI Controls Initialization (buttons,text,etc...) 
   nextButton <- button window [text := "Next Question ->", on command := set infoField [text := "Next Question"], position := pt 185 155]
   return ()

main :: IO ()
main = start gui

gui :: IO ()
gui = do
 window <- frame [text := "Question program", clientSize := sz 640 480]
 headerText <- staticText window [text := "Title Text", fontSize := 20, position := pt 5 5]
 simulate 0 window
 return ()

I want some widgets to change when the "Next Question" button is pressed. I want to change these widgets to some values I read from a file. How can I keep track of what the current question number is? I cannot actually increment questionNumber as a variable, since Haskell doesn't permit such things. I guess there's another way to do it.

Example:

Initialize GUI
Read data from file
If button is pressed, increment question number by 1 and change widgets.

How do you tackle this kind of problem in a functional manner?

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

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

发布评论

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

评论(4

泼猴你往哪里跑2024-09-18 02:54:21

函数的参数是变量。当用户输入新值时,将这些值传递给函数。

例如,一个根据用户输入更新值的简单程序:

main = loop 0

loop n = do
    print n
    v <- read `fmap` getLine
    loop (n + v)

请注意,根据用户提供的内容,对“loop”的递归调用每次都会传递不同的值。

这是函数式编程的基本思维方式——命令式程序中循环中的局部可变变量变成了函数式程序中递归函数的参数。

The arguments to functions are your variables. As the user enters new values, pass those values to functions.

For example, a simple program that updates a value based on user input:

main = loop 0

loop n = do
    print n
    v <- read `fmap` getLine
    loop (n + v)

Note the recursive calls to 'loop' have a different value passed each time, based on what the user provided.

This is a fundamental way of thinking in functional programming -- what would be a local, mutable variable in a loop in an imperative program becomes a parameter to a recursive function in a functional program.

小姐丶请自重2024-09-18 02:54:21

不幸的是,由于 wxHaskell 是一个基于事件的框架,Don 和 ZachS 的答案并不适用。

您在这里要做的是分配一个可变变量,就像在命令式语言中一样。 WxHaskell 为此提供了函数variable。这是一个(不完整的)示例:

gui = do
        ...
        counter <- variable [value := 1 :: Int]  -- allocate mutable variable
        button  <- button window [ text       := "Count!"
                                 , on command := next counter button]
    where
    next counter button = do
        n <- get counter value         -- get its value
        set button  [text  := show n]
        set counter [value := n+1]     -- set its value

请注意,wxHaskell 附带了大量示例源代码。特别是,wx/ImageViewer.hs 具有可变变量。

然而,除了像这样的特殊情况外,避免像瘟疫这样的可变变量是有益的。 (事实上​​,它们在 wxHaskell 中也造成了混乱,只是它们在这里很难避免。)替代方案包括重新考虑代码、累积参数、使用 s ->; 形式的类型。 (a,s) 和状态单子。

Unfortunately, since wxHaskell is an event-based framework, Don's and ZachS answers don't apply.

What you have to do here is to allocate a mutable variable, just like you would in an imperative language. WxHaskell offers the function variable for that. Here an (incomplete) example:

gui = do
        ...
        counter <- variable [value := 1 :: Int]  -- allocate mutable variable
        button  <- button window [ text       := "Count!"
                                 , on command := next counter button]
    where
    next counter button = do
        n <- get counter value         -- get its value
        set button  [text  := show n]
        set counter [value := n+1]     -- set its value

Note that wxHaskell comes with lots of example source code. In particular, wx/ImageViewer.hs features a mutable variable.

However, except for special situations like this, it is beneficial to avoid mutable variables like the plague. (In fact, they make a mess in wxHaskell, too, it's just that they are difficult to avoid here.) Alternatives include rethinking your code, accumulating parameters, using types of the form s -> (a,s) and the state monad.

偏爱自由2024-09-18 02:54:21

在函数式语言中实现此目的的一种方法是通过函数将状态线程化。某些功能可能由 monad 处理,例如获取键盘状态,因此您不必自己处理。一个示例(或多或少的伪代码):

someFunction :: SomeDataTypeThatRepresentsState -> a ... -> (SDTTRS, a...) (change to fit reality of course)
someFunction x y .... | x == WeHaveKeyboardInput = someFunction dowhatever.....
                      | someFunction dowhateverelse

这应该让您了解如何开始。

编辑:

我应该更深入地提到 Monad。 Monad 基本上是隐藏所有状态传递的一种方法。我知道它过于简单化了,但它可以让你开始。要了解有关 monad 和处理状态的更多信息,请点击以下链接:http:// www.engr.mun.ca/~theo/Misc/haskell_and_monads.htm

One way to do this in a functional language is to thread your state through your functions. Some of the functionality will probably be handled by monads, such as getting the keyboard state, so you won't have to handle that yourself. An example (more or less pseudocode):

someFunction :: SomeDataTypeThatRepresentsState -> a ... -> (SDTTRS, a...) (change to fit reality of course)
someFunction x y .... | x == WeHaveKeyboardInput = someFunction dowhatever.....
                      | someFunction dowhateverelse

That should give you an idea of how to get started.

EDIT:

I should have mentioned Monads more in depth. Monads are basically a way to hide all of that state passing. It's oversimplified, I know, but it gets you started. To read more about monads and handling state, follow this link: http://www.engr.mun.ca/~theo/Misc/haskell_and_monads.htm

尐籹人2024-09-18 02:54:21

您可能还想查看 Hackage 上的 simple-observer 包。 (声明:我是包维护者。)

它是 Observer 设计模式的 Haskell 实现(只是在基于事件的框架中解决“每次都进行更改......”问题的门票),使用 MVar 来实现可变状态。 此处有一篇博客文章进一步讨论了该问题

当我遇到与你完全相同的问题时,我创建了这个包。

You might also want to check out the simple-observer package on Hackage. (Disclosure: I'm the package maintainer.)

It's a Haskell implementation of the Observer design pattern (just the ticket for solving "make something change every time ..." problems in an event-based framework), using MVars for mutable state. There's a blog post discussing it further here

I created this package when I was faced with exactly the same problem as you.

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