2010-02-04 4 views
8

Ho alcuni grandi file a larghezza fissa e ho bisogno di rilasciare la riga dell'intestazione.Ruby: c'è qualcosa come Enumerable # drop che restituisce un enumeratore invece di un array?

Tenere traccia di un iteratore non sembra molto idiomatico.

# 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| ... } 

Qual è l'idioma Ruby per questo?

risposta

5

Se è necessario più di una volta, è possibile scrivere un'estensione su 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, … 
+0

Questa è una soluzione molto carina (e rubyish). Se non ti piace la lingua, cambiala. Ero sicuro che quello che volevo fare era così comune che ci sarebbe stato un idioma o una funzione per farlo comunque. Sono passati due giorni da quando ho chiesto, quindi suppongo di no. enum_cons e enum_slice esistono quindi forse il nome enum_drop si adatterebbe meglio allo stdlib. Grazie. –

+0

Hai ragione. Suona meglio. Cambiato in 'enum_drop'. – Debilski

+0

Non sarebbe più come: ... with_index (n) {| val, idx | yield val} ... –

1

Fuori della parte superiore della mia testa, ma sono sicuro che con un po 'di ricerca c'è un modo più elegante

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

Va bene graffio che ... ha fatto un po' di ricerca, e questo potrebbe essere migliore

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

Che vi avidamente valutare l'enumeratore a un array prima iterazione di linee, provocando l'intero file da slurped alla memoria in una sola volta. –

+0

Sì, l'ho capito. L'ho aggiornato usando nient'altro che gli enumeratori. – Farrel

+0

Non sono sicuro se drop_while funzionerà come secondo i documenti, ma restituirà anche un array ... – Farrel

7

Questo è un po ' più ordinato:

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

o

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

Qui mi piace la seconda soluzione di glenn qui, anche se non usa la chiusura 'File.open() do ... end'. – bta

1

Dubito che questo sia idiomatico, ma è semplice.

+0

Mi scuso, vedo che mentre stavo componendo questo Glenn mi ha battuto. – Shadowfirebird

2

Ora che hai ottenuto risposte ragionevoli, ecco un modo completamente diverso di gestirlo.

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) 

Non è né idiomatico né terribilmente intuitivo la prima volta, ma è divertente!

+0

in realtà, se si utilizza una coda, è molto più chiaro (logica meno invertita) – rampion

1

Penso che tu sia sulla buona strada con l'Enumeratore e rilascia (1). Per qualche strana ragione, mentre Enumerable definisce #drop, Enumerator no. Ecco un lavoro Enumerator # goccia:

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 
Problemi correlati