【问题标题】:Is there an abstraction for the declare-update-return pattern?声明更新返回模式是否有抽象?
【发布时间】:2013-09-09 21:16:15
【问题描述】:

在 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

这从things 的平面列表开始,它们具有相关但不同的uris。它将这个平面列表转换为层次结构,将共享相同segments 的things 与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

【问题讨论】:

  • 不清楚你想要什么。 x = [] 后跟x.some_in_place_update!(data) 使some_in_place_update! 成为Array 的方法,它的名字暗示它正在就地修改数组x(给定some_data 的元素作为输入)即使它是空的。你有没有真的用新方法修补Array?了解some_in_place_update! 正在做的事情会很有帮助。
  • 你能举出一两个具有这种模式的实际方法的例子吗?
  • @Borodin“就地更新”可能是描述我真正意思的不好方式。我试图传达这样一个想法,即对象 x 正在被该方法调用改变。
  • @DanBurton:啊!你想要each_with_object。我会写一个答案。

标签: ruby design-patterns idioms mutation


【解决方案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

【讨论】:

    【解决方案2】:

    啊!你想要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
    

    【讨论】:

      猜你喜欢
      • 2017-05-13
      • 1970-01-01
      • 2023-03-13
      • 1970-01-01
      • 2020-03-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-08
      • 2010-10-25
      相关资源
      最近更新 更多