【问题标题】:Extract data from a signal从信号中提取数据
【发布时间】:2015-07-13 16:13:05
【问题描述】:

我有这样的信号:signal1 = Signal.constant {a=4, b=3, l = []}
如何从信号中提取数据?
我试过Signal.map (\x -> x) signal1Signal.map 返回另一个信号。

【问题讨论】:

    标签: elm elm-signal


    【解决方案1】:

    如果您在 Elm 中编程时遇到过以下问题:

    • “如何从信号中提取价值?”
    • “如何将此变量更改为另一个值?”
    • “我应该把 foldp 放在哪里,我应该传递什么参数?”

    您应该阅读The Elm Architecture 教程并尝试在此过程中实现其代码。说真的,任何新手 Elm 程序员都应该从头到尾认真阅读本教程,因为它会消除很多困惑。

    不过,我将给出一个简化的摘要 - 请记住,我省略了许多细节和复杂性。如果您阅读过本教程,您可能会理解典型的 Elm 程序是如何构建的。大多数 Elm 程序有 3 个主要部分:模型、更新和视图。

    模型包含所有程序数据的初始值及其类型定义。 Update 接受一个事件和一个模型,并根据该事件返回一个修改后的模型。 View 获取一个模型(在程序当前所处的任何阶段)并将其绘制到屏幕上,换句话说,返回一个Element

    在一个简单的程序中,您可以立即渲染信号发出的任何内容,而无需存储任何中间状态,只需将Signal.map 应用于渲染函数和信号即可。下面,函数show 扮演原始视图的角色。您不使用任何模型或更新,因为只要鼠标信号发出新事件,您就会立即渲染。

    import Graphics.Element exposing (Element, show)
    import Mouse
    import Signal exposing (Signal, map)
    
    main : Signal Element
    main = map show Mouse.isDown
    

    但是如果你想maintain state between the events,你必须直接使用foldp,或者通过一些更高层次的抽象。状态是指您的模型,由您的更新功能的连续应用程序修改。在任何时间点,您的模型都处于某种状态。你的main 通常看起来像这样:

    main = map view (foldp update model signal)
    

    main 函数始终具有Signal Element 类型——也有例外,但在幕后它们无论如何都会转换为Signal Element。无论您对数据、代码和函数做什么,在某些时候您都必须将它们全部组合成 Signal Element 类型的东西,这将是您的 main 函数的主体。

    一个模型通常是一个record,有很多字段,其中任何一个都可以是记录。更新通常具有Event -> Model -> Model 类型,其中Model 是您的模型类型,Event 是您最终发出的信号。视图通常具有Model -> Element 类型。 (类型名称不必是EventModel,我在本例中使用它们作为占位符)。

    您不能从信号中提取值,这在 Elm 中是故意不可能的。相反,您使用Signal.map 将一个函数提升Signal context。所有对信号发出的值的操作都是在Signal 上下文中由提升到其中的函数完成的。假设您有一个signal 类型为Signal Event。那么foldp update model signal 将具有Signal Model 类型,因为foldp 具有以下类型:

    foldp : (a -> state -> state) -> state -> Signal a -> Signal state
    

    如果您的视图函数具有Model -> Element 类型,那么map view (foldp update model signal) 将具有Signal Element 类型。

    main 被要求具有Signal Element 类型,因此map view (foldp update model signal) 可以是main 的主体。

    import Graphics.Element exposing (Element, show)
    import Mouse
    import Signal exposing (Signal, foldp, map)
    
    type alias Model = Int
    model : Model
    model = 0
    
    update : () -> Model -> Model
    update event model = model + 1
    
    view : Model -> Element
    view model = show model
    
    main : Signal Element
    main = map view (foldp update model Mouse.clicks)
    

    上面是一个非常简单的累积鼠标点击的程序。我们有一个虚拟事件,我们的模型只是一个整数。 Signal.map 函数是如何工作的?它的类型:

    map : (a -> b) -> Signal a -> Signal b
    

    它采用一个普通函数,将一个值转换为另一个值,并采用第一个值的信号,产生第二个值的信号。假设你有很多不同的信号,它们会发出对应于鼠标点击、按键、定时事件、HTML 输入字段等各种东西的值。你可以用任何你喜欢的方式操作这些信号,但在某些时候你merge 将它们变成一个最终信号,类型为Signal Something(其中Something 对应于一个复杂的数据类型,包含程序所需的所有输入数据)。

    因为你必须有一个main 函数,在某些时候你必须将你的最终信号转换为Signal Element,所以在某些时候你必须映射(提升)一个Something -> Element 类型的函数超过@ 987654372@ 获取Signal Element。为什么叫提升?因为partial applicationSignal.map 这两种类型定义是等价的:

    map : (a -> b) -> Signal a -> Signal b
    map : (a -> b) -> (Signal a -> Signal b)
    

    提升一个类型为a -> b的普通日常函数到Signalcontext,这样它就可以处理值的信号而不是只是价值观。这是一个更复杂的例子,它同时计算秒数和鼠标点击次数:

    import Graphics.Element exposing (Element, show)
    import Mouse
    import Signal exposing (Signal, foldp, map, merge)
    import Time exposing (Time, fps, inSeconds)
    
    type alias Model = { clicks : Int, time : Int }
    model : Model
    model = { clicks=0, time=0 }
    
    type Event = Seconds Int | Mouse ()
    update : Event -> Model -> Model
    update event model = case event of
      Seconds time -> { model | time <- model.time + time }
      Mouse () -> { model | clicks <- model.clicks + 1 }
    
    view : Model -> Element
    view model = show model
    
    timeSignal = Seconds << round << inSeconds <~ fps 1
    mouseSignal = map Mouse Mouse.clicks
    
    signal : Signal Event
    signal = merge timeSignal mouseSignal
    
    main : Signal Element
    main = map view (foldp update model signal)
    

    现在,我希望您对 Elm 程序的结构以及如何处理信号发出的事件以及从一个事件到另一个事件修改数据有一个基本的了解。现在您可以通过添加更多信号来扩展上述程序,使您的模型更复杂,并使您的视图绘制更精美的输出。

    【讨论】:

      【解决方案2】:

      这几乎是不可能的,因为你不应该这样做。

      为什么?好吧,在 Elm 应用程序中查看一个可能的 main 签名可能会有所帮助:

      main : Signal Element
      

      这里,我们声明我们程序的类型是一个Signal of Element;这意味着我们的程序是一个随时间变化的元素。 Elm 运行时会为我们整理出“随时间变化”位,只要我们让它知道我们关心哪些信号(通过引用它们),以及如何将它们连接在一起(使用 map、foldp 等) .

      如果您尝试访问内部值以将其显示为应用程序的一部分 - 正确的方法是使用该主签名,并让 Elm 进行信号解包。

      如果您只想查看运行时的值(例如在控制台日志中),请查看:

      http://package.elm-lang.org/packages/elm-lang/core/2.1.0/Debug

      【讨论】:

      • 但是我正在使用 StartApp 模块,我不能在模型中使用信号,是吗?
      • 你的模型中不应该有任何Signals。你能提供一些代码来显示你正在尝试做什么吗?
      猜你喜欢
      • 2015-10-02
      • 2015-05-21
      • 2015-08-30
      • 1970-01-01
      • 2019-09-06
      • 2023-04-01
      • 1970-01-01
      • 2017-02-17
      • 1970-01-01
      相关资源
      最近更新 更多