【问题标题】:Ruby Code explainedRuby 代码解释
【发布时间】:2010-10-11 05:03:49
【问题描述】:

谁能解释一下这段 Ruby 代码:

def add_spec_path_to(args) # :nodoc:
  args << {} unless Hash === args.last
  args.last[:spec_path] ||= caller(0)[2]
end

我已经看到&lt;&lt; 运算符用于连接字符串或在其他语言中用作按位运算符,但有人可以在这种情况下解释它。是否以某种方式将空白 lamda 附加到 args 上还是我完全错了?

我也可以看到它是这样使用的:

before_parts(*args) << block

Hash 是关键字吗?

我也不确定||= 操作员在说什么。

对于caller(0)[2] 是什么,我同样一无所知。

【问题讨论】:

    标签: ruby


    【解决方案1】:

    我假设argsArray

    Hash 是一个类的名称 - 第一行将空哈希 {} 推送到 args 除非 args 的最后一个元素已经是 Hash=== 类运算符用于测试对象是否属于某个类)。

    ||= 运算符类似于 += 运算符:它或多或少等同于:

    args.last[:spec_path] = args.last[:spec_path] || caller(0)[2]
    

    因此,当且仅当它当前未设置时,它才会设置 args.last[:spec_path]

    caller 方法返回有关调用方法的信息。

    【讨论】:

      【解决方案2】:

      ||= 是一个常见的 Ruby 习惯用法:它仅在尚未设置值时才分配值。效果和代码一样

      if some_variable == nil
         some_variable = some_value
      end
      

      some_variable= some_value unless some_variable
      

      ===,当没有被覆盖时,比较两个对象的身份。在Hash === args.last 的情况下,Hash(它是 Class 类型的对象)正在检查它是否与 args 数组中最后一项的类匹配。代码利用了 Class#=== 的实现强制检查比较对象的类这一明显事实。

      反之则不行,例如:

      a = [{}]
      Hash === a.last #=> true
      a.last === Hash #=> false
      

      方法的尾随参数可以作为哈希的内容提供,而无需提供 {}

      所以你可以这样做:

      def hello(arg1, arg2, arg3)
        puts [arg1.class, arg2.class, arg3.class].join(',')
      end
      
      hello 1,2,3 #=> Fixnum,Fixnum,Fixnum
      hello :a, "b", :c => 1, :d => 99 #=> Symbol,String,Hash
      

      它通常用于为函数提供可变长度的可选参数列表。

      顺便说一句,您确定您准确地转录了原始代码吗?为了获得参数数组,您通常会在声明的参数中添加一个 *,否则 args 必须作为数组输入,而这会破坏对象。

      def add_spec_path_to(*args)              # now args is an array
          args << {} unless Hash === args.last # if trailing arguments cannot be
                                               # interpreted as a Hash, add an empty 
                                               # Hash here so that following code will 
                                               # not fail
          args.last[:spec_path] ||= caller(0)[2] # Set the spec_path option if it's not
                                               # already set
      end
      

      编辑:进一步扩展 *args 事情,试试这个:

      def x(*args)
        puts args.join(',')
        puts args.map{|a| a.class }.join(',')
      end
      
      x 1,2,:a=>5,:b=>6 
      1,2,a5b6
      Fixnum,Fixnum,Hash
      

      ... 使用 *args 会导致 args 作为数组呈现给方法。如果我不使用*,像这样,例如:

      def y(args)
        puts args.join(',')
        puts args.map{|a| a.class }.join(',')
      end
      

      ... 那么在我调用该方法之前 args 必须是一个数组,否则我将得到一个“ArgumentError:错误数量的参数”,但只有一件事传递。所以它必须看起来像这样:

      y [1,2,{:c=>3,:d=>4}]
      

      ...使用 {} 显式创建的哈希。而且很丑。

      以上所有测试均使用 MRI 1.8.6,顺便说一句。

      【讨论】:

      • 不,传入一个数组是有意义的,因为不会返回 args,否则,附加的 Hash 会丢失。
      • 我不确定我是否理解:你会这样调用方法: some_method([arg1, arg2, arg3], options_hash) ?太可怕了:请参阅上面的编辑答案...
      【解决方案3】:

      用更短的方式:

      def add_spec_path_to(args) # :nodoc:
      
      ...
      
      # Append an empty hash to args UNLESS the last arg is a hash.. in which case do nothing
      args << {} unless Hash === args.last # so we need a hash. If it is not there, make an empty one and put it there.
      
      ...
      
      #if args.last[:spec_path] equals nil or false, set it to caller(0)[2]... 
      
      #so inside that hash from the first part, if :spec_path is not there, create it by using caller(0)[2].
      
      args.last[:spec_path] ||= caller(0)[2] 
      
      ...
      
      end
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-03-26
        • 2015-04-02
        • 1970-01-01
        • 1970-01-01
        • 2015-03-23
        • 2012-05-11
        • 2012-07-20
        • 2011-09-25
        相关资源
        最近更新 更多