You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ruby技术问询:实现基于字符串的惰性Token生成器

实现惰性Tokenizer的两种实用方案

要实现惰性分词,核心就是避免提前把所有匹配结果加载到内存,而是每次调用next_token时才去查找下一个匹配项。这里有两种适合Ruby环境的实现思路,兼顾惰性特性和性能需求,我们逐一拆解:

方案一:基于位置追踪的轻量高效实现

这种方式直接利用String#match结合偏移量追踪当前匹配位置,没有额外的协程开销,性能表现最优,非常适合大量实例化的场景。

class Tokenizer
  def initialize(str, regex)
    @str = str
    @regex = regex
    @current_pos = 0
  end

  def next_token
    # 从当前位置开始执行匹配
    match = @str.match(@regex, @current_pos)
    return nil unless match

    # 更新下一次匹配的起始位置
    @current_pos = match.end(0)
    match[0]
  end
end

用法验证:

t = Tokenizer.new('abcdefgh', /.../)
puts t.next_token # => "abc"
puts t.next_token # => "def"
puts t.next_token # => nil

这个方案的优势在于极简高效:没有额外的上下文切换开销,每次调用只是执行一次匹配并更新位置,完全符合惰性加载的要求,同时性能表现拉满。

方案二:利用Fiber封装String#scan的生成器特性

你提到String#scan带块调用时是生成器模式(不会一次性生成所有结果),我们可以用Fiber把这个行为封装成next_token接口,代码更简洁,可读性更好。

class Tokenizer
  def initialize(str, regex)
    @fiber = Fiber.new do
      str.scan(regex) do |match|
        Fiber.yield match
      end
      nil # 所有匹配完成后返回nil
    end
  end

  def next_token
    @fiber.resume
  end
end

用法验证:

t = Tokenizer.new('abcdefgh', /.../)
puts t.next_token # => "abc"
puts t.next_token # => "def"
puts t.next_token # => nil

这个方案的好处是代码更优雅,直接复用Ruby内置的scan逻辑,不需要自己处理位置追踪。但要注意:Fiber虽然开销不大,但如果你的应用会创建十万级以上的Tokenizer实例,可能会有轻微的性能损耗,此时方案一更合适。

场景选择建议

  • 若只是常规业务场景,两种方案性能差异几乎可以忽略,方案二的代码更简洁易读。
  • 若涉及高频创建实例或超大量调用,方案一的位置追踪方式因为没有Fiber的上下文切换开销,会是更优选择。

内容的提问来源于stack exchange,提问作者user1934428

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 07:31:12