我正在尝试编写快速简洁的代码。我很感激您的想法,哪个是编写以下代码的最佳方式,以及原因:
选项#1
def get_title
title = check_in_place_one
if title.empty?
title = check_in_place_two
if title.empty?
title = check_in_place_three
end
end
return title
end
选项#2
def get_title
title = check_in_place_one
title = check_in_place_two unless !title.empty?
title = check_in_place_three unless !title.empty?
return title
end
我认为选项#1更好,因为如果title
找到check_in_place_one
,我们会测试title.empty?
一次,然后跳过方法中的其余代码并返回。但是,它看起来太长了。选项#2看起来更好,但是处理title.empty?
一个额外的时间,并且在返回之前不必要的时间。另外,我错过了第三种选择吗?
答案 0 :(得分:2)
从性能上看,代码的两个版本之间没有区别(除了可能来自解析的非常小的差异,这应该是可忽略的)。控制结构是相同的。
从可读性来看,如果你可以摆脱嵌套,那么这样做会更好。你的第二个选择更好。
通常最好摆脱任何不需要进一步处理的情况。这是由return
完成的。
def get_title
title = check_in_place_one
return title unless title.empty?
title = check_in_place_two
return title unless title.empty?
title = check_in_place_three
return title
end
上面代码中的最后一个title =
和return
是多余的,但我将它们放在那里以保持一致性,这提高了可读性。
您可以使用tap
进一步压缩代码,如下所示:
def get_title
check_in_place_one.tap{|s| return s unless s.empty?}
check_in_place_two.tap{|s| return s unless s.empty?}
check_in_place_three
end
tap
是一种非常快速的方法,与instance_eval
不同,它的性能损失通常是可以忽略的。
答案 1 :(得分:2)
以下方法可用于任意数量的顺序测试。而且,它完全是一般的。可以更改返回条件,可以轻松地将参数分配给测试方法等。
tests = %w[check_in_place_one check_in_place_two check_in_place_three]
def do_tests(tests)
title = nil # Define title outside block
tests.each do |t|
title = send(t)
break unless title.empty?
end
title
end
我们试一试:
def check_in_place_one
puts "check 1"
[]
end
def check_in_place_two
puts "check 2"
''
end
def check_in_place_three
puts "check 3"
[3]
end
do_tests(tests) #=> [3]
check 1
check 2
check 3
#=> [3]
现在更改其中一个测试:
def check_in_place_two
puts "check 2"
'cat'
end
do_tests(tests) #=> 'cat'
check 1
check 2
#=> "cat"
如果有更多的测试,将它们放入一个类include
d的模块中可能会很方便。混合方法的行为与您为类定义的方法相同。例如,他们可以访问实例变量。我将用第一种测试方法的定义来证明。我们可能希望将测试方法设为私有。我们可以这样做:
module TestMethods
private
def check_in_place_one
puts "@pet => #{@pet}"
puts "check 1"
[]
end
def check_in_place_two
puts "check 2"
''
end
def check_in_place_three
puts "check 3"
[3]
end
end
class MyClass
@@tests = TestMethods.private_instance_methods(false)
puts "@@tests = #{@@tests}"
def initialize
@pet = 'dog'
end
def do_tests
title = nil # Define title outside block
@@tests.each do |t|
title = send(t)
break unless title.empty?
end
title
end
include TestMethods
end
解析代码时会显示以下内容:
@@tests = [:check_in_place_one, :check_in_place_two, :check_in_place_three]
现在我们执行测试:
MyClass.new.do_tests #=> [3]
@pet => dog
check 1
check 2
check 3
确认测试方法是私有的:
MyClass.new.check_in_place_one
#=> private method 'check_in_place_one' called for...'
使用模块的优点是您可以添加,删除,重新排列和重命名测试方法,而无需对类进行任何更改。
答案 2 :(得分:1)
嗯,这里有一些其他选择。
选项1:首先返回非空检查。
def get_title
return check_in_place_one unless check_in_place_one.empty?
return check_in_place_two unless check_in_place_two.empty?
return check_in_place_three
end
选项2:带有短路评估的辅助方法。
def get_title
check_place("one") || check_place("two") || check_place("three")
end
private
def check_place(place)
result = send("check_in_place_#{place}")
result.empty? ? nil : result
end
选项3:检查所有地点,然后找到第一个非空的地方。
def get_title
[
check_in_place_one,
check_in_place_two,
check_in_place_three,
].find{|x| !x.empty? }
end
答案 3 :(得分:0)
虽然您使用unless !title.empty?
进行了360度转弯,但选项2看起来不错。您可以将其缩短为if title.empty?
,因为unless
等同于if !
,因此执行unless !
会将您带回if
。
如果您只有3个地方可以查看,那么选项2是最好的。它简洁,简洁,易于阅读(一旦修复上述的麻烦,就会更容易)。如果您可以添加到您寻找标题的地方,您可以获得更多动态:
def get_title(num_places = 4)
title, cur_place = nil, 0
title = check_in_place(cur_place += 1) while title.nil? && cur_place < num_places
end
def check_in_place(place_num)
# some code to check in the place # given by place_num
end
棘手的问题是其中有一个while
。发生的事情是while
会检查表达式title.nil? && cur_place < num_places
并返回true,因为title
仍为零且0小于4。
然后我们将调用check_in_place
方法并传入值1,因为cur_place += 1
表达式将其值增加到1然后返回它,将其提供给方法(假设我们想要当然,开始检查到位1)。
这将重复,直到check_in_place
返回非零值,或者我们用完了要检查的地方。
现在,get_title
方法更短,并且会自动支持在num_places
位置进行检查,因为您的check_in_place
方法也可以查看更多地方。
还有一件事,你可能想给https://codereview.stackexchange.com/看看,这个问题似乎很适合它。
答案 4 :(得分:0)
我认为没有任何理由变得过于聪明:
def get_title
check_in_place_one || check_in_place_two || check_in_place_three
end
编辑:如果check_in_place_X方法确实在失败时返回空字符串,那么让它们返回nil
会更好(并且更具惯用性)。它不仅允许像上面的代码那样进行真正的比较,return ""
会导致构造一个新的和不必要的String对象。