0

在 ruby​​ 中编写带有突变的迭代代码时,我经常发现自己遵循以下模式:

def build_x some_data
  x = [] # or x = {}
  some_data.each do |data|
    x.some_in_place_update! (... data ...)
  end
  x
end

x通常与 不具有相同的形状some_data,因此简单的map行不通。)

有没有更惯用或更好的方法来编写遵循这种模式的代码?


[编辑] 一个真实的例子:

def to_hierarchy stuff
  h = {}
  stuff.each do |thing|
    path = thing.uri.split("/").drop(4)
    sub_h = h
    path.each do |segment|
      sub_h[segment] ||= {}
      sub_h = sub_h[segment]
    end
    sub_h.merge!(
      data: thing.data,
    )
  end
  h
end

这从 s 的平面列表开始thing,这些 s 具有相关但不同uri的 s。它将这个平面列表转换为层次结构,将thing共享相同segments 的相关 s 分组uri。这遵循我描述的模式:初始化h,循环一些数据并h沿途变异,然后h在最后吐出。

[edit2] 另一个相关示例

def count_data obj
  i = if obj[:data] then 1 else 0
  obj.each do |k, v|
    i += count_statements v unless :data == k
  end
  i
end
4

2 回答 2

1

您的to_hierarchy示例可以通过以下方式完成each_with_object

def to_hierarchy stuff
  stuff.each_with_object({}) do |thing, h|
    #...
  end
end

each_with_object将额外的对象传递给块在迭代完成时返回该对象。

如果您更偏爱传统主义者,则可以使用inject

def to_hierarchy stuff
  stuff.inject({}) do |h, thing|
    #...
    h
  end
end

请注意块参数顺序更改,并且块必须返回h以便inject可以将其反馈到下一个块调用。

您的一般示例可以写成:

def build_x some_data
  some_data.each_with_object([]) do |data, x|
    x.some_in_place_update! (... data ...)
  end
end

或者:

def build_x some_data
  some_data.inject({}) do |x, data|
    x.some_in_place_update! (... data ...)
    x
  end
end
于 2013-09-09T22:03:59.607 回答
1

啊! 你想要each_with_object。像这样

def to_hierarchy stuff
  stuff.each_with_object({}) do |thing, h|
    path = thing.uri.split("/").drop(4)
    sub_h = h
    path.each do |segment|
      sub_h[segment] ||= {}
      sub_h = sub_h[segment]
    end
    sub_h.merge!(
        data: thing.data,
    )
  end
end
于 2013-09-09T22:06:44.347 回答