openlang

协程 proposer 要 amber 却拿到 green,history 里只剩最后一块 tile

在给一个调色板小玩具做 tile 选择器(移动端应用,接触屏 UI 之前先用 lua main.lua 加一串脚本化的决定做无界面测试)。有两处不对劲,我分不清是一个 bug 还是两个。

  1. 带 want = "amber"(调色板里第三个)的拒绝,下一个给我的是 x=2 color=green。普通的拒绝没问题。
  2. 最后的 history 输出里每一条的 color 和 x 都一样,都是最后一次 proposal 的(x=3 color=green,一遍遍重复)。不过 grid 最终是对的。

resume 的打印显示决定都正确传进去了,所以我不觉得是 resume 差一位(见 coroutine.resume 文档)。是 history 读 grid 读晚了吗?

协程如下。

function Proposer.new(palette, start_x)
  local proposal = { x = start_x, color = nil }
  return coroutine.create(function()
    local x = start_x
    while true do
      local c = palette:next()
      proposal.x, proposal.color = x, c
      local decision = coroutine.yield(proposal)
      if decision.accept then
        x = x + 1
      elseif decision.want then
        palette:seek(decision.want)
      end
    end
  end)
end

再贴 Session:step 里记录 history 的那一段。

table.insert(self.history, { tile = self.pending, decision = decision })
if decision.accept then
  self.grid[self.pending.x] = self.pending.color.name
end

amber 是在哪丢的?

从英语翻译查看原文

后续讨论 8

顺着彼此的依据读下去
  1. 固定链接

    协程每次 yield 的都是同一个 proposal 表,所以 history 存的只是对同一个表的引用。每次循环 yield 一个新表。

    从英语翻译查看原文
  2. 固定链接

    协程每次 yield 的都是同一个 proposal 表

    对,就是这个,#2(history 转储)也是这么来的。每次 yield 一个新表就能修好。amber 那个多半是另一个 bug,在 seek 里。

    从英语翻译查看原文
  3. 固定链接

    history 只是存了对同一个表的引用

    对,和转储的现象完全吻合。(同一个表,所以每条都显示最后的值。)

    想问下修复该放在哪。你会在协程里复制,也就是每次 yield 一个新的 { x = x, color = c },还是在 Session:step 记录 history 的时候复制?如果以后有人 yield 了复用的表,第二种感觉更稳。还是说这样有点过头了?

    从英语翻译查看原文
  4. 固定链接

    在协程里修。bug 就出在那,生产者应该对自己交出去的东西负责。在 Session:step 里拷贝也行,但那样会把别名问题藏起来,下一个持有引用的消费者还是会被同样咬一口。

    想双保险的话,记录时做一次浅拷贝几乎没成本。是有点过头,但是便宜的过头。

    从英语翻译查看原文
  5. 固定链接

    能贴一下 palette:seek 和 palette:next 吗?如果 seek 把索引设成 amber,而 next 是先自增再返回,拿到的就是 amber 后面那个颜色,也就是 green。

    从英语翻译查看原文
  6. 固定链接

    marmalade 你的 seek 猜测说得通,不过有个细节,want 是字符串 "amber",不是 3,所以 seek 得先把名字解析成索引。除了 next 里的先自增,这是第二个可能藏着差一位的地方。

    简单的检查办法是让协程在 seek 之后 yield,并在 next 运行之前打印索引。如果它已经是 3,那问题就出在 next。

    关于别名,对修复来说关键的部分在 §3.4.9。表构造器每次求值都会构造一个新表。所以在循环里写 { x = x, color = c },每次 yield 就是一个新的。

    从英语翻译查看原文
  7. 固定链接

    说得对,我漏掉了字符串到索引这一步。实际上我会先查这里,因为名字查找如果从 0 开始数,而 next 从 1 开始数,光这一点就会得到 green。在 seek 之后立刻打印索引就能定论,对吧?这样 next 要么洗清嫌疑,要么就是它的问题。

    从英语翻译查看原文

同一个问题的其他角度

  1. 一个把“Saved”读了两遍的状态区域

    一次保存之后,屏幕阅读器把“Saved”读了两遍。它背后的状态只有一次转入 saved 的转换,reducer 的测试也证明了这一点。 变了两次的是 DOM。状态组件只在有消息时才渲染这个区域,大致是 message && <p aria-live=…

    回复 3最近活动 typescriptjavascript
  2. 一个血条变红,把其他的也带红了

    这次在 Godot UI 里被共享资源坑了。我的小原型里每个单位都有一个血条场景,是一个 ProgressBar,填充部分用 StyleBoxFlat 做了覆盖。血量低于 25 的时候我想让那一个血条变红,于是写了这个。 第一个单位挨了一下,屏幕上所…

    回复 4最近活动 gdscript
  3. 标签改了,可屏幕上显示出来了吗?

    你好!我在做一个小型销售报表页面(Vite 开发服务器,TypeScript),有个标签的问题没搞懂。 点击“Load report”后,标签显示 Loading,页面卡住大约一秒,然后直接跳到 Done。Calculating 从来没出现过。加上…

    回复 7最近活动 javascripttypescript