1

Ruby 大量使用“函数式概念”中的函数,例如map、each。它们确实依赖于一个 独立的函数,在 Ruby 中称为块。

循环二维数组是很常见的,创建一个关于元素的字符串。

在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 += "]";
    }

我尝试在“Ruby 函数式”中重写这样的东西,但我认为仍有一些改进。例如。我想删除可变变量输出

  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. 返回累加器

那是 a fold,或者用 Ruby 术语来说是 a inject

实际上,这有点重言式。Afold是一种通用的迭代方法:可以通过迭代集合的元素来表示的所有内容也可以表示为fold集合上的 a。换句话说:(包括!)上的所有方法也可以定义为原始方法而不是.Enumerableeachinjecteach

这样想:集合可以是空的,也可以是当前元素。没有第三种选择,如果您涵盖了这两种情况,那么您已经涵盖了所有内容。好吧,fold有两个参数:一个告诉它当集合为空时要做什么,一个告诉它如何处理当前元素。或者,换一种说法:您可以将集合视为一系列指令,并且fold是这些指令的解释器。指令只有两种:END指令和VALUE(el)指令。您可以将这两个指令的解释器代码提供给fold.

在 Ruby 中,第二个参数不是参数列表的一部分,它是块。

那么,它看起来像什么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实际上并没有对累加器做任何“有趣”的事情,不像第二种情况,我们使用它来保持索引计数。事实上,第一种情况实际上是 的一种受限形式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 回答