【问题标题】:Type error comparing signal int with int [Elm]比较信号 int 和 int [Elm] 的类型错误
【发布时间】:2015-02-06 20:46:04
【问题描述】:

我想让一个按钮在选中时突出显示(浅蓝色背景)。为此,我编写了以下代码:

players : Input Int
players = input 1

playerSelection : Element
playerSelection = flow right [ 
      color (buttonPressed 1) (button players.handle 1 "Solo")
    , color (buttonPressed 2) (button players.handle 2 "2P")
    , color (buttonPressed 3) (button players.handle 3 "3P")
    , color (buttonPressed 4) (button players.handle 4 "4P")
    ]

buttonPressed : Int -> Color
buttonPressed p = if players.signal == p then lightBlue else white

当我在我的其余代码中运行它时,我收到了这个错误消息:

   if | players.signal == p -> lightBlue
      | True -> white

  Expected Type: Int
    Actual Type: Signal.Signal Int

问题在于 player.signal 是一个 Signal Int。如果我将其替换为正常数字,我的程序将正常工作。但是,我需要一个按钮在选择时变为浅蓝色,在选择另一个按钮时再次变为白色。有没有办法将信号转换为普通整数以进行比较?我已经尝试创建一个外部比较方法并将信号提升到该方法,但是这种方法的结果将是一个我无法使用的 Signal Bool。为了说明我做了什么:

buttonPressed : Int -> Color
buttonPressed p = if equals p <~ players.signal then lightBlue else white

equals : Int -> Int -> Bool
equals a b = a == b

更新

我试图将信号“向上”移动到调用函数,但问题仍然存在。我不妨把所有的代码都展示出来(不多,我一开始就应该这样做)。

heartbeat = every (second/50)

data Player = One | Two | Three | Four

-- STARTING SCREEN
players : Input Int
players = input 1

beginState : Int -> Element
beginState ps = collage 600 600 [
      move (0,270) (toForm (playerSelection ps))
    ]

playerSelection : Int -> Element
playerSelection ps = flow right [
      color (buttonPressed 1 ps) (button players.handle 1 "Solo")
    , color (buttonPressed 2 ps) (button players.handle 2 "2P")
    , color (buttonPressed 3 ps) (button players.handle 3 "3P")
    , color (buttonPressed 4 ps) (button players.handle 4 "4P")
    ]

buttonPressed : Int -> Int -> Color
buttonPressed p ps = if p == ps then lightBlue else white

playState : Element
playState = draw

draw : Element
draw = collage 1024 768 [
      filled black (rect 1024 768)
    ]

endState : String -> Element
endState p = plainText (show p ++ " has won!")

startGame : Signal Element
startGame = foldp (\_ _ -> draw) (beginState <~ players.signal) Keyboard.space

main : Signal Element
main = startGame

我将首先解释我的最终目标是什么。游戏在开始屏幕 (beginState) 中开始。在此屏幕中,玩家可以自定义:
- 昵称(返回字符串的文本字段)
- 控件(wasd、箭头键、用户为方向定义的任何键)
- 玩家数量

输入空格键后,游戏开始,这些参数不能再更改。这可以在 startGame 中看到。实际游戏的运作方式与本主题无关,但重要的是要知道我希望将开始屏幕中的自定义选项转移到游戏机制中。因此,每个玩家的某些操作的输入键必须与控件文本字段中选择的字符一致。选择的玩家数量将决定在游戏中创建哪些对象。此游戏的 playState 目前已替换为持有者draw

关于问题:
我慢慢了解到我不能简单地通过调用players.signal 来询问玩家数量,因为我会得到一个信号整数而不是整数。这意味着我将无法调用controls.signal 来检索要放入Keyboard.directions 的字符(因为我将得到一个信号字符而不是字符)。问题基本上是,我如何像使用其他编程语言中的变量一样存储和传递来自信号的信息?

我在这段代码上工作得越多,我就越意识到我不明白信号是如何工作的。在您的示例(Apanatshka)中,我看到您将信号提升到 playerSelection,它需要一个 Int 作为参数而不是 Signal Int。当我尝试应用此功能时(请参阅此更新部分中的代码),我收到以下错误:

Type error on line 76, column 35 to 63:
       beginState <~ players.signal

  Expected Type: Signal.Signal Graphics.Element.Element
    Actual Type: Graphics.Element.Element

我大致了解可能出了什么问题。一个函数正在发送两个信号,它只需要一个信号。老实说,我更愿意看到信号传播到更相关的位置,比如在 beginState 中:

beginState : Element
beginState = collage 600 600 [
      move (0,270) (toForm (playerSelection <~ players.signal))
    ]

但随后 toForm 得到一个 Signal Element 参数而不是 Element,这又会产生问题。从技术上讲,我可以将信号一直放在 main 中,但我对此感觉很不好,因为一旦游戏开始并且信号作为 Int 参数传递,就不再需要 players.signal

我希望我很清楚我想要完成什么。并快速总结我的问题:
- players.signal 在不将所有信号都支持到 main 的情况下适合我的代码在哪里?
- 我将如何存储和传递来自信号的信息?

【问题讨论】:

    标签: types signals elm


    【解决方案1】:

    回答“更新”

    我认为Elm websiteLearn 页面有一些很好的资源来解决您提出的一般问题:

    1. Using Signals
    2. Modular Architecture

    我会尝试做一个简短的回顾并添加我的意见,因为这些只是官方文档的链接。

    输入错误提示

    首先,来自 Elm 编译器的类型错误可能会令人困惑。预期与实际通常以不直观的顺序给出。但是您得到的类型错误来自于给foldp 一个作为信号的基值。 foldp : (a -&gt; b -&gt; b) -&gt; b -&gt; Signal a -&gt; Signal b 的类型表明如果b = Signal Element,那么foldp 的结果就是Signal (Signal b)。这通常是当您“过早”开始在代码中使用信号时遇到的问题情况。

    Using Signals

    我了解您认为信号是可变变量,并且只想在需要其当前值的任何位置提及它们。但正如您所指出的,这基本上就是信号与可变变量不同的地方。因此,简单架构的总体思路是将所有输入组合到一个数据结构中:

    data Input =
        Players Int
      | Control String
    
    input : Signal Input
    input = merge (Players <~ players.signal) (Control <~ control.signal)
    

    您构建另一个数据结构来保存程序所需的所有状态并定义initialState
    然后定义一个通用更新函数,在给定新输入的情况下,您可以更新状态。
    您还可以创建一个 view 函数来显示您的状态。

    然后你创建main:

    main = view <~ foldp update initialState input
    

    这基本上是第一个链接中描述的常见模式。我认为这回答了您的问题我将如何存储和传递来自信号的信息?foldp
    它回答了 players.signal 在哪里适合我的代码而不将我的所有信号都支持到 main? 因为这里的答案是:您必须将所有信号支持到 main。但是这个答案不能令人满意,并且可能无法扩展,所以这就是我包含第二个链接的原因。

    Modular Architecture

    单个foldp 保存所有状态的模式似乎有点过于严格。1 但它确实有效。直到你开始扩展它并希望对不同组件进行一些关注点分离.

    所以架构页面的基本思想是通过在模块中定义它们的状态、输入、更新、视图来创建不交互的组件。然后你的主程序导入不同的模块,创建数据结构来组合不同的状态/输入/更新/视图,并能够再次使用单个foldp,最后在main中使用。


    1 我实际上认为它太严格并且可以放松,但这是我不想进入的切线。这是一个不平凡的想法,如果要实施,还需要做更多的工作,所以它不是推荐新手的完全合适的材料。但这是我的宠物项目(不是我有时间),所以你去:[shameless plug]

    “更新”前的原答案

    总体思路

    你的想法是正确的。这个想法是将players.signal 完全从buttonPressed 函数中移除。对Signals 的一般建议是尽量不要使用它们,然后在最后一刻---在main--- 使用它们。如果main 变得过于复杂,那么即使这意味着在其他函数中使用信号,也要进行重构。但试着从非信号函数开始。

    解决方案

    仅在 main 中使用信号,因此要在 buttonPressed 中使用 players.signal,您需要将其作为参数传递(注意参数是 Int,而不是 Signal Int):

    buttonPressed : Int -> Int -> Color
    buttonPressed ps p = if ps == p then lightBlue else white
    

    您需要将这个IntplayerSelection 传递给buttonPressed,所以也将它作为参数添加到那里:

    playerSelection : Int -> Element
    playerSelection ps = flow right [ 
          color (buttonPressed ps 1) (button players.handle 1 "Solo")
        , color (buttonPressed ps 2) (button players.handle 2 "2P")
        , color (buttonPressed ps 3) (button players.handle 3 "3P")
        , color (buttonPressed ps 4) (button players.handle 4 "4P")
        ]
    

    现在你可以这样写 main:

    main = playerSelection <~ players.signal
    

    Executable example

    import Graphics.Input (Input, input, button)
    
    players : Input Int
    players = input 1
    
    playerSelection : Int -> Element
    playerSelection ps = flow right [ 
          color (buttonPressed ps 1) (button players.handle 1 "Solo")
        , color (buttonPressed ps 2) (button players.handle 2 "2P")
        , color (buttonPressed ps 3) (button players.handle 3 "3P")
        , color (buttonPressed ps 4) (button players.handle 4 "4P")
        ]
    
    buttonPressed : Int -> Int -> Color
    buttonPressed ps p = if ps == p then lightBlue else white
    
    main = playerSelection <~ players.signal
    

    【讨论】:

    • 感谢您的结构化回答。不幸的是,我无法产生相同的结果。我已经编辑了我的原始帖子,以展示我对如何存储和传递来自信号的信息的附加问题所做的工作。非常感谢您的帮助!
    • BabyBurger,我刚刚编辑了我的答案。我希望这能回答您更新的问题。
    猜你喜欢
    • 1970-01-01
    • 2017-12-18
    • 1970-01-01
    • 2022-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-26
    • 2020-02-07
    相关资源
    最近更新 更多