【问题标题】:Find certain object in Ruby在 Ruby 中查找特定对象
【发布时间】:2026-01-24 09:05:03
【问题描述】:

一直在使用 Rails,但现在我完全没有框架编码,我错过了很多我自己无法编写的方法。所以我需要一点提示。

所以我有这个代码

class Foo
   initialize(k)
    (1..k).each do |b|
       Boo.new(b)
    end
end

class Boo
  initialize(a)
    @a = a
  end
end

Foo.new(10)
#and now I want to access one of the Boo's with a certain a (in Rails Boo.find_by_a(2) e.g.)

如何编写一个使用特定值“a”的方法,以通过 @a = a 获取特定的 Boo?

更新:

好的,我在 Boo 中添加了这个方法

  def self.all
    ObjectSpace.each_object(self).to_a
  end

现在我得到了正确的对象:

boo = Boo.all.select{ |b| b.a == 2 }.first

但如果我想让它成为一种方法,我会把它放在哪里?

【问题讨论】:

  • 你没有在任何地方捕获这些。现在您正在创建 10 个嘘声,但它们没有被存储,并且会很快被 GC 拾取。一般规则先学习 Ruby,然后再学习 Rails。这将对您非常有益,并且有大量的资源。
  • 我已经编写了一个all 方法,它可以将所有对象返回到一个数组中。只是现在不知道如何挑选合适的。
  • 您的代码没有显示任何内容,并且格式错误。请向我们展示您的实际代码,或者至少先修复这里的代码。
  • 我刚刚阅读了您的更新,您似乎误解了 PORO 与 ActiveRecord 对象。 Boo.find_by_xxx 不会做你想做的,因为这依赖于 AR 和 SQL。你在这里最好的实现方法是 Foo 的实例方法。
  • 面向对象的设计是关于责任的。需要有人负责持有和维护对您的嘘声的访问。也许是 Foo,也许是 Boo 类,也许是其他类,甚至是全局变量。这取决于你的问题。滥用 ObjectSpace 是最糟糕的选择。

标签: ruby class methods


【解决方案1】:

不要依赖ObjectSpace 这个功能,当 GC 收集时你会丢失这些对象,因为 Ruby 认为它们是不需要的。

这是你打算做什么的一般概念

class Foo 
  attr_reader :boos
  def initialize(k)
    @boos = (1..k).map {|b| Boo.new(b) } 
  end
  def find_boo_with(x)
    @boos.find {|b| b.a == x }
  end
end

class Boo
  attr_reader :a
  def initialize(a)
    @a = a
  end
end

那么你就可以这样做了

f = Foo.new(10)
f.boos
#=> [#<Boo:0x256f428 @a=1>,
     #<Boo:0x256f3b0 @a=2>,
     #<Boo:0x256f368 @a=3>,
     #<Boo:0x256f338 @a=4>,
     #<Boo:0x256f2f0 @a=5>,
     #<Boo:0x256f2d8 @a=6>,
     #<Boo:0x256f2a8 @a=7>,
     #<Boo:0x256f290 @a=8>,
     #<Boo:0x256f278 @a=9>,
     #<Boo:0x256f260 @a=10>]
f.find_boo_with(9)
#=> #<Boo:0x256f278 @a=9>

我希望这会有所帮助,但您确实应该研究 RubyMonk 或 TryRuby 之类的东西,以更好地了解 PORO(普通旧 Ruby 对象)的工作原理。

如果你真的想要你的 find 方法,你也可以这样做根据Boo 是什么以及您要创建的数量来判断内存问题。

class Boo
   class << self
      def all_boos
        @all_boos ||= []
      end
      def find(x)
        @all_boos.find {|boo| boo.a == x }
      end
      alias_method :all, :all_boos
   end
   attr_reader :a
   def initialize(a)
     @a = a
     self.class.all_boos << self
   end
end

那你就可以了

10.times {|i| Boo.new(i) }
Boo.all
#=> [#<Boo:0x256f428 @a=1>,
     #<Boo:0x256f3b0 @a=2>,
     #<Boo:0x256f368 @a=3>,
     #<Boo:0x256f338 @a=4>,
     #<Boo:0x256f2f0 @a=5>,
     #<Boo:0x256f2d8 @a=6>,
     #<Boo:0x256f2a8 @a=7>,
     #<Boo:0x256f290 @a=8>,
     #<Boo:0x256f278 @a=9>,
     #<Boo:0x256f260 @a=10>] 
Boo.find(9)
#=>  #<Boo:0x256f278 @a=9>

【讨论】:

  • 我最后使用了这个答案,效果很好。谢谢!
【解决方案2】:

如果我确实了解您的需求,那么我认为您可以使用 ObjectSpace 来实现这一点。这是说明使用的示例代码:

class Foo
  def initialize(k)
    (1..k).each do |b|
       Boo.new(b)
    end
  end
end

class Boo
  attr_reader :a
  def initialize(a)
    @a = a
  end

 def self.find(value)
   ObjectSpace.each_object(Boo).select { |b| b.a == value }.first
 end
end
Foo.new(10)
Boo.find(2).a

让我知道这是否是您正在寻找的解决方案。

【讨论】:

  • 哈哈看看我的更新我得到了较小的部分,但你的回答为我把它放在一起。非常感谢 !!小问题。如果我已经拥有更新中显示的 .all 方法怎么办。
  • 不错!我同样很高兴您自己找到了答案并且我的样本有所帮助。 =D
  • 不要依赖ObjectSpace,当 GC 收集时你会丢失这些对象,因为 Ruby 认为它们是不需要的。
  • 只有当对象真的没有被使用时,对工程师mnky?
  • 试试这个,例如100.times { Boo.new }; ObjectSpace.each_object(Boo).count #=&gt; possibly 100 if GC hasn't started; GC.start; ObjectSpace.each_object(Boo).count #=&gt; 0。这是因为Boo 对象被视为一次性并标记为 GC,然后当 GC 运行时,它会处理这些对象。 (顺便说一句,一般情况下不强制 GC 运行,这只是一个示例)
【解决方案3】:

不确定我是否 100% 理解您的问题,但在 Boo 中使用 attr_reader 可以让您像访问函数一样访问 a

class Foo
  def initialize(k)
    (1..k).each do |b|
       Boo.new(b)
    end
  end
end

class Boo
  attr_reader :a

  def initialize(a)
    @a = a
  end
end

Foo.new(10)

至于你问题的另一部分,这里有一篇很好的文章解释how dynamic finders work

【讨论】:

  • 这仍然没有任何作用,因为 Foo 的实例创建了 10 个 Boos 但不保留它们。 each 只会丢弃Foo::initialize 中的这些对象