【问题标题】:In Elerea, why doesn't stateful return the state after the last state transformation?在 Elerea 中,为什么 stateful 不返回最后一次状态转换后的状态?
【发布时间】:2016-05-01 10:22:18
【问题描述】:

让我们看一下 hackage documentation for stateful

do
  smp <- start (stateful "" (:))
  res <- forM "olleh~" smp
  print res

输出是:

["","o","lo","llo","ello","hello"]

它的作用是将一个字符一个接一个地推入信号网络,首先是'o',然后是'l',再是'l',以此类推。 赋予有状态的状态转换函数((:),又名cons)接受一个元素和一个元素列表,并返回带有单个元素的列表。 (在 Haskell 中,StringChars 的列表)

这是怎么回事

(:) 'o' "" -- gives "o", then
(:) 'l' "o" -- gives "lo", etc

这一切都很简单,但是您是否注意到输入字符串的末尾有一个“~”?那为什么不给呢

["","o","lo","llo","ello","hello", "~hello"]
                                    ^^^^^^

?

可能是因为它首先给出初始值,然后是第一次调用(:)second的结果,等等。所以如果我们输入六个字符(包括'~'),我们就会退出五个字符后的 "" 已预先添加。字符串"~hello" 实际上在那里,但是在对信号进行采样时,我们得到了 old 状态(就在它被丢弃之前)。

我希望以下两个(人为的)示例产生相同的输出,但事实并非如此:

-- 1
do
  smp <- start $ do
    n <- input
    return (n*2)
  res <- forM [1,2,3] smp
  print res -- prints [2,4,6]

-- 2
do
  smp <- start $ stateful 0 (\n _  -> n*2) -- ignore state
  res <- forM [1,2,3] smp
  print res -- prints [0,2,4]

-- edit: 3
-- to make the trio complete, you can use transfer to do the same
-- thing as 1 above
-- there is of course no reason to actually do it this way
do
  smp <- start $ transfer undefined (\n _ _ -> n*2) (pure undefined)
  res <- forM [1,2,3] smp
  print res

所以我的问题是:

  • 为什么要这样做?我们获得的初始值是否足够重要,以至于我们必须接受这种延迟?
  • 替代函数是否可以在下一个值可用时立即返回? (换句话说,这个替代函数是否可以在发送“h”后立即返回“hello”,因此可以删除“~”?)结果信号将永远不会产生初始值。

编辑: 在 Alexander 向我指出 transfer 之后,我想到了这个:

do
  smp <- start (stateful' "" (:))
  res <- forM "olleh" smp -- no '~'!
  print res -- prints ["o","lo","llo","ello","hello"]

stateful' :: a -> (p -> a -> a) -> SignalGen p (Signal a)
stateful' s f = transfer s (\p _ a -> f p a) (pure undefined)

stateful' 在我的机器上似乎比 stateful 慢了大约 25%。

【问题讨论】:

    标签: haskell frp elerea


    【解决方案1】:

    可能是因为它首先给出初始值,然后是第一次调用 (:) 第二个的结果,等等。所以如果我们输入六个字符(包括 '~'),我们会在五个字符之后输出 ""前置。字符串“~hello”实际上在那里,但是在对信号进行采样时,我们得到了旧状态(就在它被丢弃之前)。

    这正是stateful 的工作原理。引用the documentation:

    初始状态是第一个输出,后面的每一个输出都是根据前一个和全局参数的值计算出来的

    这回答了你的第一个问题。

    至于第二个,是的,替代函数可以立即返回新状态。其实还有such a function already,叫transfer

    当前输入影响当前输出,即第一个参数中给出的初始状态被认为出现在第一个输出之前,并且永远无法观察到。

    (它还带有状态,您在示例中应该忽略它。)

    有了这个函数,“hello”的例子可以改写成这样:

    do
      smp <- start (transfer
                     ""
                     (\character _ state -> character:state)
                     (pure undefined))
      res <- forM "olleh" smp
      print res
    

    【讨论】:

    • 谢谢!正如你所说,我已经有了一些问题的答案。我完全错过了传递函数,可能是因为给出的例子不像其他例子那么简单。 Stateful 的例子似乎更适合作为转移的例子。仔细观察,我猜转移可以做的不仅仅是我正在寻找的有状态变体。
    • 我刚刚运行了你的代码并得到了Prelude.undefined。它使用(pure undefined) 就像我编辑的问题一样。
    • 啊,对;它不适用于undefined,因为在库的内部某处,它希望看到Signal 值并看到undefined;而pure undefined 之所以有效,是因为它是一个undefined 打包到Signal 中。库需要容器,但从不查看打包的值。我会更新我的帖子。谢谢,很抱歉造成混乱!
    猜你喜欢
    • 1970-01-01
    • 2014-06-19
    • 2018-09-06
    • 1970-01-01
    • 2016-03-16
    • 1970-01-01
    • 1970-01-01
    • 2018-09-16
    • 1970-01-01
    相关资源
    最近更新 更多