在Elerea,为什么stateful return不是最后一次状态转换后的状态?

In Elerea, why doesn't stateful return the state after the last state transformation?

让我们看一下 hackage documentation for stateful

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

输出是:

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

它所做的是将一个字符一个接一个地推入信号网络,首先是 'o',然后是 'l',然后是 'l',依此类推。 给有状态((:),又名 cons)的状态转换函数接受一个元素和一个元素列表,return 是带有单个元素的列表。 (在 Haskell 中,一个 StringChar 的列表)

这是怎么回事

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

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

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

?

可能是因为它先给出初始值,然后是(:)第一次调用的结果第二次,等等。所以如果我们压入六个字符(包括'~') 我们在 5 个字符被前置后得到 ""。字符串 "~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

所以我的问题是:

编辑: 在 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%。

Probably because it gives the initial value first, then the result of the first invocation of (:) second, etc. So if we push in six characters (including '~') we get out "" after five characters have been prepended. The string "~hello" is actually in there, but on sampling the signal we get the old state (right before it is discarded).

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

The initial state is the first output, and every following output is calculated from the previous one and the value of the global parameter

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

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

The current input affects the current output, i.e. the initial state given in the first argument is considered to appear before the first output, and can never be observed.

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

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

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