1

Rubyは、マップなどの「機能概念」の関数をそれぞれ多用しています。それらは、Rubyではいわゆるブロックと呼ばれる自己完結型の関数に実際に依存してい ます。

2D配列をループして、要素に関する文字列を作成することは非常に一般的です。

Javaでは、次のようになります。

   public String toString(){
        String output = "[";
        for (int i =0; i<array.length; i++) {
            output+= "Row "+(i+1)+" : ";
            for (int j=0; j<array[0].length;j++ ) {
                output += array[i][j]+", ";
            }
            output += "\n";
        }

        return output += "]";
    }

そんなことを「ルビーファンクショナルスタイル」で書き直してみましたが、まだまだ改善点があると思います。例えば。可変変数出力を削除したい

  def to_s
        output = "[\n"
        @data.each_with_index do |row,i|
            output << "Row #{i+1} : "
            row.each { |num| output << "#{num}," }
            output << "\n"
        end

        output+"]"
    end
4

3 に答える 3

3

パターンが表示されるたびに:

  1. アキュムレータを初期化します(あなたの場合output
  2. いくつかのコレクションの反復ごとに、アキュムレータを変更します(あなたの場合はそれに追加します)
  3. アキュムレータを返す

それはfold、または Ruby 用語ではinjectです。

実際、それはちょっとしたトートロジーです。Afoldは反復の普遍的な方法です。コレクションの要素を反復することによって表現できるものはすべてfold、コレクションの として表現することもできます。言い換えると、 (! を含む)のすべてのメソッドは、 の代わりに をプリミティブ メソッドとして定義することもできます。Enumerableeachinjecteach

このように考えてみてください: コレクションは空でも、現在の要素でもかまいません。これら 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 の構文強調表示だけでなく、私の脳の強調表示も壊します ;-)

于 2013-02-20T21:52:51.383 に答える
1

可変変数を使用しないメソッドは次のとおりです。

def to_s
  (
    [ "[" ] +
    @data.map.with_index { |row,i| "Row #{i+1} : #{row * ','}" } +
    [ "]" ]
  ).join("\n")
end
于 2013-02-20T21:49:30.120 に答える
0

同じことですが、ブロックが少なくて短いです。

  def to_s
        output = "[\n"
        @data.each_with_index do |row,i|
            output << "Row #{i+1} : #{row.join(',')}\n"
        end

        output+"]"
    end
于 2013-02-20T20:47:18.223 に答える