代码之家  ›  专栏  ›  技术社区  ›  Pierre Olivier Martel

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

  •  31
  • Pierre Olivier Martel  · 技术社区  · 16 年前

    我和一位同事讨论了在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 对齐,使我的大脑更容易分析它!

    15 回复  |  直到 8 年前
        1
  •  40
  •   Community Mohan Dere    9 年前

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

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

    我声称这有两个好处:

    1. 均匀压痕: 每个层次的逻辑嵌套都缩进了两个空格(好吧,也许这只是一个品味问题)
    2. 水平压实度: 较长的变量名不会将缩进代码推过80(或其他)列标记。

    它确实需要一行额外的代码,我通常不喜欢这样,但在这种情况下,用垂直的极简主义来换取水平的极简主义似乎是值得的。

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

    编辑: 我应该提一下 matsadler's answer 也与此类似。我真的认为 一些 缩进是有帮助的。我希望这足以证明把这个问题单独回答是合理的。

        2
  •  21
  •   Community Mohan Dere    9 年前

    作为一个Ruby程序员,我发现第一个更清晰。它清楚地表明,整个表达式是一个分配,分配的东西是基于某些逻辑确定的,并且它减少了重复。对于那些不习惯一切都是表达的语言的人来说,这看起来很奇怪,但是为那些不懂语言的人编写代码并没有那么重要,除非他们是你的目标用户。否则人们应该对它有一个过眼云烟的熟悉。

    我也同意 bp's suggestion 您可以通过缩进整个if表达式使其更清晰地阅读,从而使它在视觉上都在赋值的右边。它完全是美学的,但我认为这使得它更容易被浏览,甚至对不熟悉语言的人也应该更清楚。

    作为旁白: 这种类型的 if 根本不是Ruby独有的。它存在于所有的lisp(普通lisp、scheme、clojure等)、scala、所有的mls(f、ocaml、sml)、haskell、erlang甚至Ruby的直接前身smalltalk中。它在基于C(C++,爪哇,Cype,Objul-C)的语言中并不常见,这是大多数人使用的语言。

        3
  •  16
  •   badp    16 年前

    我不喜欢你在第一个街区使用空格。是的,我是一个蟒蛇,但我相信当我第一次说的时候我说得很对 可以 在其他代码中间看起来很混乱,可能在其他代码周围 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 实际上是这样一个函数,可以不必要地对其进行评估。

        4
  •  12
  •   Derick Bailey    16 年前

    封装…

    @products = get_products
    
    def get_products
      if params[:category]
        Category.find(params[:category]).products
      else
        Product.all
      end
    end
    
        5
  •  6
  •   Matías Flores    16 年前

    只是另一种方法:

    category = Category.find(params[:category]) if params[:category]
    @products = category ? category.products : Product.all
    
        6
  •  5
  •   Tombart    11 年前

    另一种方法是使用一个块来包装它。

    @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
    

    这是您不能用重写的代码做的,并且它是正确对齐的。

        7
  •  2
  •   matsadler    16 年前
    @products =
    if params[:category]
      Category.find(params[:category]).products
    else
      Product.all
    end
    

    是另一种选择,它都避免重复 @products 并保持 if 对齐 else .

        8
  •  2
  •   BaroqueBobcat    16 年前

    假设您的模型如下所示:

    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]
    
        9
  •  1
  •   Nate Noonen    16 年前

    先用三元,后不用。

    第一本几乎不可能读懂。

        10
  •  1
  •   blissapp    16 年前

    我也不是红宝石人,但第二个命令范围内的警铃会立即响起,在if块结束后,这个变量还会可用吗?

        11
  •  1
  •   klew    16 年前

    我想说,对于不熟悉Ruby中的结构的人来说,第二个版本更易于阅读。所以+它!另一方面,第一次施工更干燥。

    当我再看一点的时候,我发现第一个解决方案更有吸引力。我是一个Ruby程序员,但我以前没用过。当然,我会开始的!

        12
  •  1
  •   Julien 'JS'    8 年前

    我认为最好的代码是:

    @products = Category.find(params[:category])&.products.presence || Product.all
    

    查找后的“&”确保方法“products”不会评估类别是否为零。

        13
  •  0
  •   joeslice    16 年前

    在我看来,第二个对典型的程序员来说更易读。我不是红宝石人,所以我没有意识到if/else返回值……所以,以我为例(是的,这就是我的观点:d),第二个看起来是个不错的选择。

        14
  •  0
  •   Andres    16 年前

    如果有人在浏览代码,我会说第二个代码块(您的)绝对是我发现最容易快速理解的代码块。

    你朋友的代码很好,但是正如BP指出的那样,缩进在这个意义上是有区别的。

        15
  •  0
  •   Community Mohan Dere    9 年前

    我不喜欢你在第一个块中使用括号。是的,我是一个Lisp用户,但我相信当我第一次说 可以 在其他代码中间看起来很混乱,可能在其他代码周围 if 阻碍。

    怎么样。。。

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

    (开玩笑,使问题恶化 @badp's answer )

    推荐文章