java - 如何循环二维数组中的元素以在 "Ruby functional Style"中构造字符串

标签 java ruby functional-programming ruby-1.9.3

Ruby 大量使用“函数式概念”中的函数,例如ma​​p, each。它们实际上依赖于一个自包含函数,在 Ruby 中称为 block

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

在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 functional Style”重写这样的东西,但我认为还是有一些改进的地方。例如。我想删除可变变量 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

最佳答案

每当你看到模式时:

  1. 初始化一个累加器(在你的例子中是output)
  2. 在某些集合的每次迭代中修改累加器(在您的情况下附加到它)
  3. 归还累加器

那是一个折叠,或者用 Ruby 的术语来说是一个inject

实际上,这有点同义反复。 fold 是一种通用的迭代方法:everything 可以通过迭代集合的元素来表示,也可以表示为 fold在集合。换句话说:Enumerable 上的所有 方法(包括each!)也可以根据inject 定义为原始方法而不是 each

这样想:一个集合可以是空的,也可以有一个当前元素。没有第三种选择,如果你涵盖了这两种情况,那么你就涵盖了一切。好吧,fold 接受两个参数:一个告诉它当集合为空时要做什么,另一个告诉它如何处理当前元素。或者,换句话说:您可以将集合视为一系列指令,而 fold 是这些指令的解释器。只有两种指令:END 指令和VALUE(el) 指令。您可以为 fold 提供这两条指令的解释器代码。

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

那么,折叠 看起来像什么?

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返回时,它的调用者确实获得了对string,但 to_s 本身已经返回,因此无法再访问它。

如果你想要真正的花哨,你甚至可以使用字符串插值:

def to_s
  %Q<[\n#{@data.map.with_index(1) do |row, i|
    "Row #{i} : #{row.join(',')}\n"
  end.join}]>
end

不幸的是,这不仅破坏了 IRb 的语法高亮,而且破坏了我的大脑 ;-)

关于java - 如何循环二维数组中的元素以在 "Ruby functional Style"中构造字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14988918/

相关文章:

java - 如果将引用重新分配给同步块(synchronized block)内的锁定对象会怎样?

java - Penn Treebank 词性符号本身在 CoreNLP 代码中的什么位置实际表示?

ruby - 使用 gets.chomp 将字符串列表作为参数传递

functional-programming - 是否有支持可移植延续的快速语言?

functional-programming - 奇怪的 Haskell/GHCi 问题

C++中Java8 lambda类型的比较

java - 使用 Camel Bean validator 组件

ruby-on-rails - "desc"如何排序group_by?

ruby - 迭代 Chef Recipe 中的 EncryptedDataBagItem

c# - 如何将返回 `Either` 的方法绑定(bind)到 Language-Ext 中接受 `Option` 的方法?