代码之家  ›  专栏  ›  技术社区  ›  Amin Shah Gilani

为什么在数组中声明常量并将其分配给其他常量,作为Ruby中的类常量进行访问?

  •  2
  • Amin Shah Gilani  · 技术社区  · 6 年前

    给出了下面的Ruby类

    class Example
      PARENTS = [
        FATHER = :father,
        MOTHER = :mother
      ]
    end
    

    这些工作按预期进行

    > Example::PARENTS
    #=> [:father, :mother]
    > Example::PARENTS[0]
    #=> :father
    > Example::PARENTS[1]
    #=> :mother
    

    然而,这为什么有效?

    > Example::FATHER
    #=> :father
    > Example::MOTHER
    #=> :mother
    

    实际上,为什么示例类的作用域中有三个常量?

    > Example.constants
    #=> [:MOTHER, :PARENTS, :FATHER]
    

    关键是,如果我用一个额外的方法扩展类:

    class Example
      def self.whos_your_daddy
        FATHER
      end
    end
    

    它像通常一样访问常量。

    > Example.whos_your_daddy
    #=> :father
    

    这种行为是怎么可能的?通过声明常量 里面 对于一个数组,我希望它们的作用域在数组中。请在答案中引用相关文件。

    编辑: 我想我要澄清一下,回答这个问题最简单的方法是解释两件事:

    首先,执行以下代码时会发生什么情况:

    PARENTS = [
      FATHER = :father,
      MOTHER = :mother
    ]
    

    第二,声明一个常量是否与声明它的类的作用域相关联?为什么?

    6 回复  |  直到 6 年前
        1
  •  5
  •   Stefan    6 年前

    首先,执行以下代码时会发生什么情况:

    PARENTS = [
      FATHER = :father,
      MOTHER = :mother
    ]
    
    • PARENTS = ... 尝试设置常量 PARENTS . 但要做到这一点,它必须评估任务的右侧:
      • [...] 尝试创建数组。但要做到这一点,它必须评估其论点:
        • FATHER = :father 设置常量 FATHER :father . 这次任务的结果是 父亲 这是第一个论点。
        • MOTHER = :mother 设置常量 MOTHER :mother . 这次任务的结果是 母亲 这是第二个论点。

    所以在 按时间顺序的 秩序:

    1. 常数 父亲 设置为 父亲
    2. 常数 母亲 设置为 母亲
    3. 包含元素的数组 父亲 母亲 创建
    4. 常数 父母 设置为该数组

    您的代码相当于:

    FATHER = :father
    MOTHER = :mother
    PARENTS = [FATHER, MOTHER]  # or [:father, :mother]
    

    通过声明数组内的常量,我希望它们在数组内具有作用域。请在答案中引用相关文件。

    你可以使用 Module.nesting 要确定当前嵌套,即在中定义常量的位置:(中的更多示例 docs )

    class Example
      p outer_nesting: Module.nesting
      PARENTS = [
        p(inner_nesting: Module.nesting)
      ]
    end
    

    输出:

    {:outer_nesting=>[Example]}
    {:inner_nesting=>[Example]}
    

    如您所见,数组文字不会影响当前嵌套。两个位置的常量将在下定义 Example .

    如果您真的想“在”数组中声明常量(即在数组的singleton类中),可以这样做:

    class Example
      PARENTS = []
      class << PARENTS
        FATHER = :father
        MOTHER = :mother
        PARENTS.push(FATHER, MOTHER)
      end
    end
    
    p Example.constants                          #=> [:PARENTS]
    p Example::PARENTS.singleton_class.constants #=> [:FATHER, :MOTHER]
    

    以上只是为了演示的目的,实际上不需要这样做。

        2
  •  2
  •   Amin Shah Gilani    6 年前

    我可以理解它是如何令人困惑的,但除了不鼓励将值重新分配给常量之外,在范围方面,实例变量和常量极其相似。

    第一类声明中的视觉技巧是在数组中声明常量。

    首先:理解当您声明一个常量时,返回的值就是您的定义。例如。

    FATHER = :father
    #=> :father
    

    现在,让我们看看常量声明:

    PARENTS = [
      FATHER = :father,
      MOTHER = :mother
    ]
    

    声明的 PARENT ,您可以使用:

    PARENTS = [
      :father,
      :mother
    ]
    

    但你更进一步,在定义中声明了一个常量。现在请理解 实例变量和常量的范围相似 ,它与声明它的实例绑定,因此 在任何地方声明常量都会将其绑定到实例。

    通过执行 FATHER = :father ,您已经声明了另一个常量,在本例中,常量的作用域始终是声明它的类。 Example . 同样的道理也适用于 MOTHER = :mother .

    如果您更习惯于实例变量_

    class Example
      @parents = [
        @father = :father,
        @mother = :mother
      ]
    end
    

    这些工作按预期进行

    > Example.instance_variable_get :@parents
    #=> [:father, :mother]
    > Example.instance_variable_get(:@parents)[0]
    #=> :father
    > Example.instance_variable_get(:@parents)[1]
    #=> :mother
    

    但这也是可行的。

    > Example.instance_variable_get :@father
    #=> :father
    > Example.instance_variable_get :@mother
    #=> :mother
    

    实际上,这三个都在示例类的范围内。

    > Example.instance_variables
    #=> [:@mother, :@parents, :@father]
    

    关键是,如果我用一个额外的方法扩展类:

    class Example
      def self.whos_your_daddy
        @father
      end
    end
    

    它像正常一样访问实例变量。

    > Example.whos_your_daddy
    #=> :father
    
        3
  •  2
  •   sawa    6 年前

    我在Ruby___编程书中找到了这个,第94页:

    类或模块中定义的常量可以在类或模块中的任何位置不加修饰地访问。在类或模块之外,可以使用scope运算符::访问它们,该运算符的前缀是返回适当类或模块对象的表达式。在任何类或模块之外定义的常量可以不加修饰地访问,也可以使用不带前缀的作用域运算符访问。方法中不能定义常量。常量可以通过在常量名称之前使用类或模块名称和作用域运算符从外部添加到现有的类和模块。

    结论,一个类中不能有两个同名常量,一个在数组内部,一个在外部。所以您不需要范围来访问它,因为范围是整个类。

    您不需要常量数组中的常量,因为数组是常量,其内部值___也是常量。

        4
  •  2
  •   Eric Duminil    6 年前

    简短的回答可能是:

    您的代码相当于:

    class Example
      PARENTS = [
        Example.const_set("FATHER", :father),
        Example.const_set("MOTHER", :mother)
      ] 
    end
    

    通过一些测试:

    puts Example.is_a? Module
    # true
    puts Example.is_a? Class
    # true
    p Example::PARENTS
    # [:father, :mother]
    p Example.constants
    # [:FATHER, :MOTHER, :PARENTS]
    puts Example::PARENTS.is_a? Module
    # false
    Example::PARENTS.const_set("UNCLE", :uncle)
    # undefined method `const_set' for [:father, :mother]:Array (NoMethodError)
    

    *:顶级常量(=类或模块外部定义的常量)似乎存储在 Object .

        5
  •  1
  •   mechnicov    6 年前

    你的答案在你的问题中。

    class Example
      PARENTS = [
        FATHER = :father,
        MOTHER = :mother
      ]
    end
    

    这里有3个常量( PARENTS , FATHER MOTHER )它们在一个范围内。数组不构成新的作用域。

    Example.constants 方法只是向您展示它们。

    即使将方法添加到类中,它也不会发生任何变化。

    class Example
      PARENTS = [
        FATHER = :father,
        MOTHER = :mother
      ]
    
      def self.whos_your_daddy
        FATHER
      end
    end
    
    Example.constants #=> [:MOTHER, :PARENTS, :FATHER]
    
        6
  •  0
  •   Kimmo Lehto    6 年前

    数组定义不是块。它不在数组的作用域中运行。

    这与以下情况没什么不同:

    PARENTS = []
    PARENTS << mom = :mother
    

    如您所见,范围不会改变:

    > puts self
    main
    => nil
    > array = [ puts(self.inspect) ]
    main
    => [nil]
    

    赋值返回赋值:

    > foo = "bar"
    "bar"
    > puts foo
    bar
    > puts(foo = "baz")
    baz
    > puts foo
    baz
    

    你不能“储存”类似的东西 MOTHER = :mother 在数组中,由于它不是一个值,所以它返回一个值,即 :mother .