怠惰な列挙子を使用してRubyで大きなファイルを操作する
列挙子は、Rubyをそのような強力で動的な言語にするものの中心にあります。また、怠惰な列挙子は、非常に大きなコレクションを効率的に処理できるようにすることで、これをさらに一歩進めます。
ファイル(結局のところ)は、行または文字の単なる大きなコレクションです。したがって、怠惰な列挙子を使用すると、非常に興味深く強力なことを実行できます。
とにかく列挙子とは何ですか?
each
のようなメソッドを使用するたびに 、列挙子を作成します。これが、[1,2,3].map { ... }.reduce { ... }
のようなメソッドをチェーンできる理由です。 。以下の例で私が何を意味するかを見ることができます。 each
を呼び出す 列挙子を返します。これを使用して、他の反復操作を実行できます。
# I swiped this code from Ruby's documentation https://ruby-doc.org/core-2.2.0/Enumerator.html
enumerator = %w(one two three).each
puts enumerator.class # => Enumerator
enumerator.each_with_object("foo") do |item, obj|
puts "#{obj}: #{item}"
end
# foo: one
# foo: two
# foo: three
通常の列挙子には、大きなコレクションで問題があります。その理由は、呼び出す各メソッドがコレクション全体を反復処理する必要があるためです。次のコードを実行すると、これを自分で確認できます。
# This code will "hang" and you'll have to ctrl-c to exit
(1..Float::INFINITY).reject { |i| i.odd? }.map { |i| i*i }.first(5)
reject
メソッドは、無限のコレクションの反復を終了できないため、永久に実行されます。
しかし、少し追加するだけで、コードは完全に実行されます。単にlazy
と呼ぶと メソッドでは、Rubyは賢いことを行い、計算に必要な回数だけ反復を行います。この場合、これはわずか10行であり、無限大よりも大幅に小さくなっています。
(1..Float::INFINITY).lazy.reject { |i| i.odd? }.map { |i| i*i }.first(5)
#=> [4, 16, 36, 64, 100]
6千部のMobyDick
これらのファイルトリックをテストするには、大きなファイルが必要です。非常に大きいため、「怠惰になるのに失敗した」ことは明らかです。
ProjectGutenbergからMobyDickをダウンロードし、100部のテキストファイルを作成しました。しかし、それは十分な大きさではありませんでした。だから私はそれを約6,000に上げました。つまり、今のところ、私はおそらく世界で6,000部のMobyDickを含むテキストファイルを持っている唯一の人です。それは一種の謙虚さです。しかし、私は逸脱します。
moby dickをダウンロードして数千回複製し、大きなファイルを再生しました。構文はbashではありません。魚の殻です。残っているのは私だけだと思います。
これは、使用していることを知らなかったとしても、おそらく使用したことのあるクールなRubyのトリックです。コレクションを反復処理するRubyのほとんどすべてのメソッドは、ブロックを渡さずに呼び出すと、列挙子オブジェクトを返します。それはどういう意味ですか?
この例を考えてみましょう。ファイルを開いて、各行を使用して各行を印刷できます。しかし、ブロックなしで呼び出すと、列挙子が表示されます。対象となるメソッドは、each_line
です。 、each_char
およびeach_codepoint
。
File.open("moby.txt") do |f|
# Print out each line in the file
f.each_line do |l|
puts l
end
# Also prints out each line in the file. But it does it
# by calling `each` on the Enumerator returned by `each_line`
f.each_line.each do |l|
puts l
end
end
これらの2つの例はほとんど同じように見えますが、2番目の例は AMAZING POWERSのロックを解除するための鍵を握っています 。
ファイル内のすべての行を「含む」列挙子を作成したら、他のruby配列の場合と同じように、それらの行をスライスおよびダイスできます。ここにいくつかの例があります。
file.each_line.each_with_index.map { |line, i| "Line #{ i }: #{ line }" }[3, 10]
file.each_line.select { |line| line.size == 9 }.first(10)
file.each_line.reject { |line| line.match /whale/i }
これは本当にクールですが、これらの例にはすべて1つの大きな問題があります。それらはすべて、ファイルを反復処理する前にファイル全体をメモリにロードします。 Moby Dickのコピーが6,000個含まれているファイルの場合、遅延が顕著になります。
大きなテキストファイルで「クジラ」という単語の最初の10個のインスタンスをスキャンしている場合、10回目の出現を監視し続ける必要はありません。幸い、Rubyの列挙子にこれを行うように指示するのは非常に簡単です。 「lazy」キーワードを使用するだけです。
以下の例では、遅延読み込みを利用して、かなり洗練された処理を実行しています。
File.open("moby.txt") do |f|
# Get the first 3 lines with the word "whale"
f.each_line.lazy.select { |line| line.match(/whale/i) }.first(3)
# Go back to the beginning of the file.
f.rewind
# Prepend the line number to the first three lines
f.each_line.lazy.each_with_index.map do |line, i|
"LINE #{ i }: #{ line }"
end.first(3)
f.rewind
# Get the first three lines containing "whale" along with their line numbers
f.each_line.lazy.each_with_index.map { |line, i| "LINE #{ i }: #{ line }" }.select { |line| line.match(/whale/i) }.first(3)
end
ソケット、パイプ、シリアルポート-これらは、IOクラスを使用してRubyで表されます。つまり、すべてにeach_line
があるということです。 、each_char
およびeach_codepoint
メソッド。したがって、これらすべてにこのトリックを使用できます。かなりきちんとしています!
残念ながら、怠惰な列挙子は、実行しようとしているタスクでファイル全体を読み取る必要がない場合にのみ、処理を高速化します。本の最後のページにのみ出現する単語を検索する場合は、本全体を読んで見つける必要があります。ただし、その場合、このアプローチは非列挙型アプローチよりも遅くなることはありません。
-
Excel ファイルが非常に大きいのはなぜですか? (ソリューションの 7 つの理由)
Excelで作業中 、処理または開くのに時間がかかりすぎる大きなファイルに遭遇することがよくあります。作業中でも簡単な命令を実行するのに時間がかかり、デスクトップがフリーズすることもあります。 Excel ファイルが大きい理由は複数考えられます .私の Excel について質問がある場合の理由と解決策は次のとおりです。 ファイルが大きすぎます。 ここから練習用ワークブックをダウンロードできます。 Excel ファイルが非常に大きくなる 7 つの理由 エクセル ファイルはさまざまな理由で大きくなる可能性があります。 エクセル 幅広い用途と用途があります。その結果、さまざまな側面がさまざまな理
-
Windows 10 で ISO ファイルを操作する方法
ISO ファイルには、通常、または伝統的に光メディアにあるデータの完全なアーカイブが含まれています。現在、CD や DVD がソフトウェア配布に使用されることはあまりありませんが、ISO は依然として大規模なソフトウェア ダウンロードの一般的なコンテナーです。 ISO 形式でソフトウェアをリリースする開発者には、Microsoft の Windows 10 インストール イメージが含まれます。 Windows は、Windows 8 の発売以来、ISO ファイルを適切にサポートしてきました。サードパーティのソフトウェアがなくても、デバイスのファイル システムに ISO ファイルをマウントする