パターンが表示されるたびに:
- アキュムレータを初期化します(あなたの場合
output
)
- いくつかのコレクションの反復ごとに、アキュムレータを変更します(あなたの場合はそれに追加します)
- アキュムレータを返す
それはfold
、または Ruby 用語ではinject
です。
実際、それはちょっとしたトートロジーです。Afold
は反復の普遍的な方法です。コレクションの要素を反復することによって表現できるものはすべてfold
、コレクションの として表現することもできます。言い換えると、 (! を含む)のすべてのメソッドは、 の代わりに をプリミティブ メソッドとして定義することもできます。Enumerable
each
inject
each
このように考えてみてください: コレクションは空でも、現在の要素でもかまいません。これら 2 つのケースをカバーする場合、3 番目のオプションはありません。fold
コレクションが空の場合に何をするかを指示する引数と、現在の要素をどう処理するかを指示する引数の 2 つの引数を取ります。または、さらに別の言い方をすれば、コレクションを一連の命令として見ることができfold
、それらの命令のインタプリタです。END
命令には、命令と命令の 2 種類しかありませんVALUE(el)
。また、これらの両方の命令のインタープリター コードをfold
.
Ruby では、2 番目の引数は引数リストの一部ではなく、ブロックです。
それで、それはどのように見えますfold
か?
def to_s
@data.each_with_index.inject("[\n") do |acc, (row, i)|
acc + "Row #{i+1} : #{row.join(',')}\n"
end + ']'
end
コードが機能しない不純物に感染する可能性があるかどうかに興味がある場合each_with_index
は、アキュムレータにインデックスを含めることで簡単に取り除くことができます。
def to_s
@data.inject(["[\n", 1]) do |(s, i), row|
[s + "Row #{i} : #{row.join(',')}\n", i+1]
end.first + ']'
end
また、最初のケースでは、each_with_index
を使用して、アキュムレータで実際に「興味深い」ことは何もしていないことに注意してください。これは、インデックスのカウントを保持するために使用している 2 番目のケースとは異なります。実際、最初のケースは実際には の制限された形式でありfold
、その機能をすべて使用しているわけではありません。それは本当にただのものmap
です:
def to_s
"[\n" + @data.map.with_index(1) do |row, i|
"Row #{i} : #{row.join(',')}\n"
end.join + ']'
end
私の個人的な意見では、実際には文字列連結の代わりに (変更可能な) 文字列追加を使用してもまったく問題ありません。
def to_s
"[\n" << @data.map.with_index(1) do |row, i|
"Row #{i} : #{row.join(',')}\n"
end.join << ']'
end
これにより、いくつかの不要な文字列オブジェクトを作成する必要がなくなりますが、さらに重要なことは、より慣用的であるということです。本当の問題は変更可能な状態を共有することですが、ここでは変更可能な文字列を共有していません。呼び出し元がto_s
返されると、文字列にアクセスできますが、それ自体は返されたため、アクセスできなくなります。to_s
本当に凝ったものにしたい場合は、文字列補間を使用することもできます:
def to_s
%Q<[\n#{@data.map.with_index(1) do |row, i|
"Row #{i} : #{row.join(',')}\n"
end.join}]>
end
残念ながら、これは IRb の構文強調表示だけでなく、私の脳の強調表示も壊します ;-)