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

Ruby 如何使用if..else块的结果分配变量?,ruby,coding-style,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 = Pr

我和一位同事就在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
对齐,这会让我的大脑更容易解析它

在我看来,第二个版本对于典型的程序员来说更具可读性。我不是一个喜欢ruby的人,所以我没有意识到if/else会返回一个值。。。。因此,以我为例(是的,这是我的观点:D),第二个似乎是一个不错的选择。

如果使用三元,则第一个,如果不使用,则第二个


第一个命令几乎无法读取。

我也不是Ruby人,但第二个命令的范围会立即响起警钟,该变量会在if块结束后可用吗?

作为Ruby程序员,我发现第一个更清楚。它清楚地表明,整个表达式是一个赋值,根据某种逻辑确定赋值的对象,并减少了重复。对于那些不习惯所有语言都是表达式的人来说,这看起来很奇怪,但在我看来,为不懂这种语言的人编写代码并不是那么重要的目标,除非他们特别是你的目标用户。否则,人们应该对它略知一二

我也同意您可以通过缩进整个if表达式使其在视觉上位于作业右侧,从而使其读得更清楚。它完全是审美的,但我认为这使它更容易浏览,甚至对于不熟悉该语言的人来说也应该更清晰


作为旁白:这种
,如果
不是Ruby独有的。它存在于所有的Lisp(公共Lisp、Scheme、Clojure等)、Scala、所有的MLs(F#、OCaml、SML)、Haskell、Erlang甚至Ruby的直接前身Smalltalk中。这在基于C(C++、Java、C#、Objective-C)的语言中并不常见,这是大多数人使用的语言。

我想说,对于不熟悉ruby结构的人来说,第二个版本更容易阅读。那就这么办吧!另一方面,第一次施工更加干燥

当我看得再长一点时,我发现第一个解决方案更具吸引力。我是一名ruby程序员,但我之前没有使用过它。当然,我会开始的

封装

@products = get_products

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

我不喜欢你在第一个块中使用空格。是的,我是一个PythOnista,但是我相信当我说第一个可能在其他代码的中间看起来很混乱时,我可以做出一个公平的观点,也许在其他<代码>如果 那么

@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]

…但如果
产品,这是一个坏主意。所有的
实际上都是这样一个函数,可以对其进行不必要的计算。

假设您的模型如下所示:

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]}} : {})
或者,您可以使用性感的、懒散加载的功能:

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
是另一种选择,它既避免重复
@产品
,又使
if
else
保持一致,这只是另一种方法:

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

如果你正在浏览代码,我会说第二个代码块(你的),肯定是我发现最容易快速理解的代码块


您好友的代码很好,但正如bp指出的那样,缩进在这个意义上有很大的不同。

作为中语法的替代方案,我建议:

@products = 
  if params[:category]
    Category.find(params[:category]).products
  else
    Product.all
  end
我认为这有两个好处:

  • 统一缩进:每一级逻辑嵌套都缩进两个空格(好吧,也许这只是一个品味问题)
  • 水平紧凑性:较长的变量名不会将缩进的代码推过80(或任何)列标记
  • 它确实需要额外的一行代码,这通常是我不喜欢的,但在这种情况下,用垂直极简主义来交换水平极简主义似乎是值得的

    免责声明:这是我自己独特的方法,我不知道它在Ruby社区的其他地方使用到了什么程度


    编辑:我应该提到的是,它也与此类似。我确实认为有一些缩进是有帮助的。我希望这足以证明将此作为一个单独的答案是合理的。

    另一种方法是使用块将其封装起来

    @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
    

    这是重写后的代码所不能做到的,而且它是正确对齐的。

    我不喜欢在第一个块中使用括号。是的,我是一个LISP用户,但是我相信当我说第一个可能在其他代码的中间看起来很混乱时,我可以做出一个公平的观点,也许在其他<代码> > Bug。

    那么

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

    (^开玩笑,加剧问题)

    我认为最好的代码是:

    @products = Category.find(params[:category])&.products.presence || Product.all
    
    反查找后的“&”确保“产品”方法不会评估类别是否为零

    这也可以使用三元运算符(
    ?:
    )用一行代码重写,但让我们假设产品分配长度超过100个字符,不能放在一行中

    让我们假装不这样做——因为在许多情况下,优化变量名和长度条件复杂度以使包含条件的赋值适合于一行,这对可读性是有好处的

    但是,不要使用三元运算符(这是一个很难读取的运算符),而是使用
    if…then…else
    然后
    @products ||= begin
      if params[:category]
        Category.find(params[:category]).products
      else
        Product.all
      end
    end
    
    @products = (if (params[:category])
            ((Category.find params[:category]).
                products)
        else
            (Product all)
        end
    )
    
    @products = Category.find(params[:category])&.products.presence || Product.all
    
    cat = params[:category]
    @products = if cat then Category.find(cat).products else Product.all end