如何使用if..else块的结果分配变量?

时间:2010-05-27 21:20:40

标签: ruby coding-style

我与一位同事讨论了在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个字符并且不能放在一行中。

两者中的哪一个更清楚?第一个解决方案占用的空间少一些,但我认为声明一个变量并将其分配给三行后可能更容易出错。我也希望看到我的ifelse对齐,让我的大脑更容易解析它!

16 个答案:

答案 0 :(得分:40)

作为badp's answer中语法的替代方案,我想提议:

@products = 
  if params[:category]
    Category.find(params[:category]).products
  else
    Product.all
  end

我声称这有两个好处:

  1. 统一缩进:每个级别的逻辑嵌套都缩进两个空格(好吧,也许这只是品味问题)
  2. 水平紧凑:较长的变量名称不会将缩进代码推过80(或其他)列标记
  3. 它需要一些额外的代码,我通常不喜欢这种代码,但在这种情况下,将垂直极简主义换成水平极简主义似乎是值得的。

    免责声明:这是我自己的特殊方法,我不知道它在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,又可以使ifelse保持一致。

答案 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是我发现的最好的语言。