我与一位同事讨论了在if..else块中分配变量的最佳方法。他的原始代码是:
@products = if params[:category]
Category.find(params[:category]).products
else
Product.all
end
我用这种方式重写了它:
if params[:category]
@products = Category.find(params[:category]).products
else
@products = Product.all
end
这也可以使用一个使用ternery操作符(?:)的单行重写,但让我们假装产品分配超过100个字符并且不能放在一行中。
两者中的哪一个更清楚?第一个解决方案占用的空间少一些,但我认为声明一个变量并将其分配给三行后可能更容易出错。我也希望看到我的if
和else
对齐,让我的大脑更容易解析它!
答案 0 :(得分:40)
作为badp's answer中语法的替代方案,我想提议:
@products =
if params[:category]
Category.find(params[:category]).products
else
Product.all
end
我声称这有两个好处:
它需要一些额外的代码,我通常不喜欢这种代码,但在这种情况下,将垂直极简主义换成水平极简主义似乎是值得的。
免责声明:这是我自己的特殊方法,我不知道它在Ruby社区的其他地方使用的程度。
编辑:我应该提到matsadler's answer也与此类似。我确实认为一些缩进是有帮助的。我希望这足以证明这是一个单独的答案。
答案 1 :(得分:21)
作为Ruby程序员,我发现第一个更清晰。它清楚地表明整个表达式是一个赋值,分配的东西是基于某些逻辑确定的,它减少了重复。对于那些不习惯一切都是表达的语言的人来说,这看起来很奇怪,但为那些不懂语言的人编写代码并不是IMO的重要目标,除非他们特别是你的目标用户。否则,人们应该对此有所了解。
我同意bp's suggestion你可以通过缩进整个if-expression使它更清楚地阅读,以便它在视觉上直接分配。它完全是美学的,但我认为这使得它更易于浏览,即使对不熟悉该语言的人也应该更清晰。
撇开一边:这种if
并不是Ruby独有的。它存在于所有Lisps(Common Lisp,Scheme,Clojure等),Scala,所有ML(F#,OCaml,SML),Haskell,Erlang甚至Ruby的直接前身Smalltalk中。它在基于C(C ++,Java,C#,Objective-C)的语言中并不常见,这是大多数人使用的。
答案 2 :(得分:16)
我不喜欢你在第一个区块中使用空格。是的,我是一个Pythonista,但我相信当我说第一个可能在其他代码的中间看起来令人困惑时,我可能会提出一个公平的观点,也许是围绕其他if
块。
怎么样......
@products = if params[:category] Category.find(params[:category]).products
else Product.all
end
@products = if params[:category]
Category.find(params[:category]).products
else
Product.all
end
你也可以试试......
@products = Product.all #unless a category is specified:
@products = Category.find(params[:category]).products if params[:category]
...但如果Product.all
实际上是一个函数,那么这是一个坏主意,然后可能会对其进行不必要的评估。
答案 3 :(得分:12)
...封装
@products = get_products
def get_products
if params[:category]
Category.find(params[:category]).products
else
Product.all
end
end
答案 4 :(得分:6)
另一种方法:
category = Category.find(params[:category]) if params[:category]
@products = category ? category.products : Product.all
答案 5 :(得分:5)
另一种方法是使用一个块来包装它。
@products = begin
if params[:category]
Category.find(params[:category]).products
else
Product.all
end
end
解决了分配问题。但是,对于这样的"复杂的"码。如果我们只想将变量初始化一次,这种方法将非常有用:
@products ||= begin
if params[:category]
Category.find(params[:category]).products
else
Product.all
end
end
这是您可以对重写的代码执行的操作,并且它已正确对齐。
答案 6 :(得分:2)
假设您的模型如下所示:
class Category < ActiveRecord::Base
has_many :products
end
class Product < ActiveRecord::Base
belongs_to :category
end
你可以做一些更疯狂的事情,比如:
#assuming params[:category] is an id
@products = Product.all( params[:category] ? {:conditions => { :category_id => params[:category]}} : {})
或者,您可以使用性感的,延迟加载的named_scope功能:
class Product < ActiveRecord::Base
...
#again assuming category_id exists
named_scope :all_by_category, lambda do |cat_id|
if cat_id
{:conditions => {:category_id => cat_id}}
end
end
#if params[:category] is a name, and there is a has and belongs to many
named_scope :all_by_category, lambda do |cat_name|
if cat_name
{:joins => :categories, :conditions => ["categories.name = ?",cat_name]}
end
end
...
end
像
一样使用@products = Product.all_by_category params[:category]
答案 7 :(得分:2)
@products =
if params[:category]
Category.find(params[:category]).products
else
Product.all
end
是另一种选择,它既可以避免重复@products
,又可以使if
与else
保持一致。
答案 8 :(得分:1)
首先使用三元,第二条如果不使用。
第一个几乎不可能阅读。
答案 9 :(得分:1)
我也不是一个Ruby人,但是警钟会立即响应第二个命令的范围,if块结束后该变量是否可用?
答案 10 :(得分:1)
我想说第二个版本对于那些不熟悉ruby中的结构的人来说更具可读性。所以+为它!另一方面,第一次建设更加干旱。
当我再看一下时,我发现第一个解决方案更具吸引力。我是一名红宝石程序员,但我之前没有使用它。我肯定会开始!
答案 11 :(得分:1)
我认为最好的代码是:
@products = Category.find(params[:category])&.products.presence || Product.all
&#34;&amp;&#34;找到后确保方法&#34;产品&#34;不会评估类别是否为零。
答案 12 :(得分:0)
对我来说,第二个对于典型的程序员来说更具可读性。我不是一个红宝石家伙,所以我没有意识到if / else返回一个值....所以,以我为例(是的,这是我的观点:D),第二个似乎是一个不错的选择。
答案 13 :(得分:0)
如果正在浏览代码,我会说第二个代码块(你的代码),绝对是我最容易理解的代码块。
你的好友的代码很好,但是正如bp所指出的那样,缩进在这个意义上造成了一个不同的世界。
答案 14 :(得分:0)
我不喜欢你在第一个街区使用括号。是的,我是一名LISP用户,但我相信当我说第一个可能在其他代码的中间看起来令人困惑时,我可能会提出一个公平的观点,可能围绕其他if
块。< / p>
怎么样......
@products = (if (params[:category])
((Category.find params[:category]).
products)
else
(Product all)
end
)
(^面朝舌头,加剧@badp's answer的问题)
答案 15 :(得分:0)
这也可以使用三元运算符(
? :
)用单线重写,但是我们假装产品分配超过100个字符并且不能排成一行。
我们不假装-因为在许多情况下,它的可读性是优化变量名和条件复杂度的长度,从而使赋值包括在内。条件适合一行。
但不要使用三元运算符(它是unnecessary in Ruby且难以阅读),而应使用if … then … else
。将then
(或;
)全部放在一行时是必需的。像这样:
cat = params[:category]
@products = if cat then Category.find(cat).products else Product.all end
对于可读性和自我记录代码,当我的程序读起来像英语句子时,我喜欢它。到目前为止,Ruby是我发现的最好的语言。