在Smalltalk中有方法ifNotNilDo:
它的使用方式如下:
database getUser ifNotNilDo: [:user | Mail sendTo: user ]
在不执行块nil
的对象上,将对象本身作为参数传递。类UndefinedObject
(Smalltalk相当于Ruby的NilClass
)中的实现根本不起作用。这样,如果让用户产生nil
对象,则不会发生任何事情。
我不知道Ruby的类似内容,所以我推出了自己的解决方案。 它是这样的:
class Object
def not_nil
yield(self)
end
end
class NilClass
def not_nil
# do nothing
end
end
可以像这样使用:
users = {:peter => "peter@peter.com", :roger => "roger@roger.com" }
users[:roger].not_nil {|u| Mail.send(u) }
这使我们无法访问哈希两次
Mail.send(users[:roger]) if users[:roger]
...或使用临时变量:
u = users[:roger]
Mail.send(u) if u
人们开始建议基于哈希操作的解决方案,并且还要两次访问哈希。我的问题不是直接与哈希相关。
想象一下,第一个操作不是哈希访问,而且也很昂贵。像:
RemoteUserRepo.find_user(:roger).not_nil {|u| Mail.send(u) }
(结束更新)
我的问题是:
答案 0 :(得分:11)
在Ruby 2.3.0+中,您可以将安全导航操作符(&.
)与Object#tap
结合使用:
users[:roger]&.tap { |u| Mail.send(u) }
答案 1 :(得分:4)
您可以使用tap
来避免两次哈希访问:
users[:roger].tap { |u| Mail.send(u) if u }
我可能会使用这样的东西:
[ users[:roger] ].reject(&:nil?).each { |u| Mail.send u }
答案 2 :(得分:4)
在ActiveSupport中,有try
方法。
https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/object/try.rb
data = { a: [1,2,3] }
data[:a].try(:first)
#=> 1
data[:b].try(:first)
#=> nil
data[:b].first
#=> Exception
在幕后,它是靠近你的解决方案实施的。对于任何对象,只有零,它将“发送消息”(就Smalltalk而言)具有属性。
# object.rb
def try(*a, &b)
if a.empty? && block_given?
yield self
else
public_send(*a, &b) if respond_to?(a.first)
end
end
# nilclass
def try(*args)
nil
end
关于您的问题
重新发明这个成语我错了吗?
Rails的家伙做了类似的事情
Ruby中是否支持这样(或更好)的东西?
不,Ruby不支持它开箱即用
还是有其他更短更优雅的方法吗?
在我看来它有一个问题:程序员应该控制数据。人们应该知道他拥有什么样的数据并处理每种类型和每种情况,或者引发错误。在您的情况下,它适用于所有数据类型,但NilClass。什么可能导致很难调试的错误。
我更喜欢使用老式的
Mail.send(users[:roger]) if users[:roger]
# or
users[:roger] && Mail.send(users[:roger])
# or use caching if needed
答案 3 :(得分:2)
在像Ruby这样的函数式语言中,有一个惯用的解决方案,它利用了赋值语句返回可以测试的值的事实:
unless (u = users[:roger]).nil?
Mail.send(u)
end
因此,您可以根据需要避免额外的哈希查找。 (然而,一些功能纯粹主义者不赞成这种事情,因为它测试副作用声明的返回值。)
答案 4 :(得分:2)
不,重新发明这个成语你没有错。您可能仍然可以更好地为其命名,可能是if_not_nil
。
是的,有一种核心的Ruby方法可以做到这一点,虽然它并没有完全渗透优雅:
[RemoteUserRepo.find_user(:roger)].compact.each {|u| Mail.send(u)}
回想一下compact
返回一个删除了nil
元素的数组副本。
答案 5 :(得分:1)
users.delete_if { |_, email| email.nil? }.each { |_, email| Mail.send email }
或
users.values.compact.each { |email| Mail.send email }