【问题标题】:Ruby: Is there something like Enumerable#drop that returns an enumerator instead of an array?Ruby:有没有像 Enumerable#drop 这样返回枚举器而不是数组的东西?
【发布时间】:2011-01-13 02:07:59
【问题描述】:

我有一些固定宽度的大文件,我需要删除标题行。

跟踪迭代器似乎不太习惯。

# This is what I do now.
File.open(filename).each_line.with_index do |line, idx|
  if idx > 0
     ...
  end
end

# This is what I want to do but I don't need drop(1) to slurp
# the file into an array.
File.open(filename).drop(1).each_line do { |line| ... }

Ruby 的成语是什么?

【问题讨论】:

    标签: ruby enumerable


    【解决方案1】:

    我认为您在使用 Enumerator 和 drop(1) 方面是正确的。出于某种奇怪的原因,虽然 Enumerable 定义了 #drop,但 Enumerator 却没有。这是一个有效的枚举器#drop:

      class Enumerator
        def drop(n_arg)
          n = n_arg.to_i # nil becomes zero
          raise ArgumentError, "n must be positive" unless n > 0
          Enumerator.new do |yielder|
            self.each do |val|
              if n > 0
                n -= 1
              else
                yielder << val
              end
            end
          end
        end
      end
    

    【讨论】:

      【解决方案2】:

      如果您不止一次需要它,您可以写一个扩展至Enumerator

      class Enumerator
        def enum_drop(n)
          with_index do |val, idx|
            next if n == idx
            yield val
          end
        end
      end
      
      File.open(testfile).each_line.enum_drop(1) do |line|
        print line
      end
      
      # prints lines #1, #3, #4, …
      

      【讨论】:

      • 这是一个非常好的(和 ruby​​ish)解决方案。如果您不喜欢该语言,请更改它。我确信我想做的事情是如此普遍,但它会有一个现有的习语或功能。自从我问起已经两天了,所以我想没有。 enum_cons 和 enum_slice 存在,因此名称 enum_drop 可能更适合标准库。谢谢。
      【解决方案3】:

      既然你已经得到了合理的答案,这里有一种完全不同的处理方式。

      class ProcStack
        def initialize(&default)
          @block = default
        end
        def push(&action)
          prev = @block
          @block = lambda do |*args|
            @block = prev
            action[*args]
          end
          self
        end
        def to_proc
          lambda { |*args| @block[*args] }
        end
      end
      #...
      process_lines = ProcStack.new do |line, index|
        puts "processing line #{index} => #{line}"
      end.push do |line, index|
        puts "skipping line #{index} => #{line}"
      end
      File.foreach(filename).each_with_index(&process_lines)
      

      第一次体验既不习惯,也不非常直观,但很有趣!

      【讨论】:

      • 其实如果用队列的话,会清晰很多(逆逻辑少)
      【解决方案4】:

      我怀疑这是惯用的,但它很简单。

      f = File.open(filename)
      f.readline
      f.each_line do |x|
         #...
      end
      

      【讨论】:

      • 我很抱歉,我看到格伦在创作这首曲子时打败了我。
      【解决方案5】:

      稍微整洁:

      File.open(fname).each_line.with_index do |line, lineno|
        next if lineno == 0
        # ...
      end
      

      io = File.open(fname)
      # discard the first line
      io.gets
      # process the rest of the file
      io.each_line {|line| ...}
      io.close
      

      【讨论】:

      • 我喜欢 glenn 的第二种解决方案,即使它没有使用看起来更整洁的 File.open() do ... end 闭包。
      【解决方案6】:

      在我的脑海中,但我确信通过更多的研究会有更优雅的方法

      File.open( filename ).each_line.to_a[1..-1].each{ |line|... }
      

      好吧,从头开始……做了一些研究,这可能会更好

      File.open( filename ).each_line.with_index.drop_while{ |line,index|  index == 0 }.each{ |line, index| ... }
      

      【讨论】:

      • 这将在遍历行之前急切地将枚举器评估为数组,从而导致整个文件立即被存储到内存中。
      • 是的,我意识到了这一点。我只使用枚举器对其进行了更新。
      • 不确定 drop_while 是否会根据文档工作,它还返回一个数组...
      • 是的。看起来 drop_while 返回一个数组。 10.times.class=> 枚举器 ... 10.times.drop_while{ |line,index|索引 == 0 }.class=> 数组
      猜你喜欢
      • 2023-03-06
      • 2011-05-03
      • 2013-07-30
      • 2014-09-29
      • 1970-01-01
      • 1970-01-01
      • 2023-03-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多