Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
元编程定义接受关键字参数的Ruby方法?_Ruby_Metaprogramming - Fatal编程技术网

元编程定义接受关键字参数的Ruby方法?

元编程定义接受关键字参数的Ruby方法?,ruby,metaprogramming,Ruby,Metaprogramming,Struct允许我创建一个新类,该类接受参数并具有良好的语义。但是,参数不是必需的,它们的顺序需要参考定义: Point = Struct.new(:x, :y) Point.new(111, 222) #=> <point instance with x = 111, y = 222> Point.new(111) #=> <point instance with x = 111, y = nil> 但是大括号中应该放些什么来定义klass上的init

Struct
允许我创建一个新类,该类接受参数并具有良好的语义。但是,参数不是必需的,它们的顺序需要参考定义:

Point = Struct.new(:x, :y)

Point.new(111, 222)
#=> <point instance with x = 111, y = 222>

Point.new(111)
#=> <point instance with x = 111, y = nil>
但是大括号中应该放些什么来定义
klass
上的
initialize
方法,以便:

  • 它需要没有默认值的关键字参数
  • 关键字在
    属性中以符号数组的形式给出
    ;及
  • initialize
    方法将它们分配给同名的实例变量

    • 我可能误解了这个问题,但你是在寻找这样的问题吗

      module StricterStruct
        def self.new(*attributes)
          klass = Class.new
          klass.class_eval do 
            attributes.map!{|n| n.to_s.downcase.gsub(/[^\s\w\d]/,'').split.join("_")}
            define_method("initialize") do |args|
              raise ArgumentError unless args.keys.map(&:to_s).sort == attributes.sort
              args.each { |var,val| instance_variable_set("@#{var}",val) }
            end
            attr_accessor *attributes
          end
          klass
        end
      end
      
      然后

      Point=stricerstruct.new(:x,:y)
      #=>点
      p=新点(x:12,y:77)
      #=> #
      p2=新点(x:17)
      #=>参数错误
      p2=新点(y:12)
      #=>参数错误
      p2=新点(y:17,x:22)
      #=>  #
      
      如果你想要更多的东西,请解释,因为我认为这符合你的标准,至少我对它的理解是这样的。因为它定义了方法,可以采用“关键字”(
      Hash
      )参数并分配适当的实例变量

      如果希望参数的指定顺序与定义顺序相同,只需删除排序即可

      此外,可能还有更干净的实现。

      由于Ruby 2.0+中的新功能,我最终使用了一种(令人惊讶的Pythonic)
      **kwargs
      策略:

      module StricterStruct
        def self.new(*attribute_names_as_symbols)
          c = Class.new
          l = attribute_names_as_symbols
      
          c.instance_eval {
            define_method(:initialize) do |**kwargs|
              unless kwargs.keys.sort == l.sort
                extra   = kwargs.keys - l
                missing = l - kwargs.keys
      
                raise ArgumentError.new <<-MESSAGE
                  keys do not match expected list:
                    -- missing keys: #{missing}
                    -- extra keys:   #{extra}
                MESSAGE
              end
      
              kwargs.map do |k, v|
                instance_variable_set "@#{k}", v
              end
            end
      
            l.each do |sym|
              attr_reader sym
            end
          }
      
          c
        end
      end
      
      模块严格结构
      def self.new(*属性名称作为符号)
      c=新类
      l=属性\u名称\u作为\u符号
      c、 实例评估{
      定义_方法(:初始化)do |**kwargs|
      除非kwargs.keys.sort==l.sort
      额外=kwargs.keys-l
      缺失=l-kwargs.keys
      
      raise ArgumentError.new听起来你在寻找Ruby的内置:

      需要“ostruct”
      foo=OpenStruct.new(条:1,'baz'=>2)
      foo#=>#
      foo.bar#=>1
      foo[:bar]#=>1
      foo.baz#=>2
      foo.baz=3
      foo#=>#
      

      我认为OpenStruct是散列上的糖果涂层,我们可以在没有任何实际约束的情况下访问和分配实例,这与使用普通访问器创建真正的类不同。我们可以假装它是散列,或者是带有方法的类。它是甜点,不是地板蜡,不是,它是两件事合一!

      我也在四处寻找t他的,并最终偶然发现了这颗宝石,它正是这样做的:


      这并不完全正确——正如我的示例所示,
      Point.new(x:12)
      应该给您一个ArgumentError,因为您没有指定
      y
      @JohnFeminella遗漏了该部分。现在它看起来更难看了:(但函数按要求运行。我不明白为什么要引入类变量,因为
      定义方法可以看到
      属性
      。@JohnFeminella将其清理干净。我更喜欢您的错误消息,但这将接受字符串或符号,并将其转换为downcase snake\u case。在我看来,使用hout
      attributes.map!
      。正如我在示例中提到的,所需的属性之一是,如果不指定所有内容,则会引发
      ArgumentError
      或类似的异常。因为
      OpenStruct
      可以随时修改,所以在这里这不是一个好的选择。此外,OpenStruct比Struct慢得多,因此如果你做了很多,这很昂贵。我一直认为OpenStruct是为那些不确定他们想要什么的人设计的。一旦创建了实例,你的案例需要更严格的检查,这肯定是OpenStruct失败的地方,我认为这是OpenStruct的一次否决票。也许你的代码将是Ruby的一个很好的补充,一个nd OpenStruct可能会因为速度太慢而被弃用。我喜欢这条消息,我刚刚更改了我的结构,使其具有非常相似的结构,尽管我为实例变量提供了getter和setter方法。很抱歉,我没有像你回答自己的问题那样快:)。另外请注意,我更新了答案,以接受字符串或符号,并将其格式化为snake_大小写。您的意思是
      def self.new(*attribute_names_as_symbols)
      ?此外,我相信您可以使用
      attr\u reader sym
      创建每个读取访问器。有趣的问题。@CarySwoveland是的,这是我的复制粘贴中的一个拼写错误。谢谢您捕捉到这一点。您也说得对,attr\u reader更简单;我应该了解这一点!
      module StricterStruct
        def self.new(*attributes)
          klass = Class.new
          klass.class_eval do 
            attributes.map!{|n| n.to_s.downcase.gsub(/[^\s\w\d]/,'').split.join("_")}
            define_method("initialize") do |args|
              raise ArgumentError unless args.keys.map(&:to_s).sort == attributes.sort
              args.each { |var,val| instance_variable_set("@#{var}",val) }
            end
            attr_accessor *attributes
          end
          klass
        end
      end
      
      Point = StricterStruct.new(:x,:y)
      #=> Point
      p = Point.new(x: 12, y: 77)
      #=> #<Point:0x2a89400 @x=12, @y=77>
      p2 = Point.new(x: 17)
      #=> ArgumentError
      p2 = Point.new(y: 12)
      #=> ArgumentError
      p2 = Point.new(y:17, x: 22)
      #=>  #<Point:0x28cf308 @y=17, @x=22>
      
      module StricterStruct
        def self.new(*attribute_names_as_symbols)
          c = Class.new
          l = attribute_names_as_symbols
      
          c.instance_eval {
            define_method(:initialize) do |**kwargs|
              unless kwargs.keys.sort == l.sort
                extra   = kwargs.keys - l
                missing = l - kwargs.keys
      
                raise ArgumentError.new <<-MESSAGE
                  keys do not match expected list:
                    -- missing keys: #{missing}
                    -- extra keys:   #{extra}
                MESSAGE
              end
      
              kwargs.map do |k, v|
                instance_variable_set "@#{k}", v
              end
            end
      
            l.each do |sym|
              attr_reader sym
            end
          }
      
          c
        end
      end
      
      require 'ostruct'
      
      foo = OpenStruct.new(bar: 1, 'baz' => 2)
      foo # => #<OpenStruct bar=1, baz=2>
      
      foo.bar # => 1
      foo[:bar] # => 1
      foo.baz # => 2
      foo.baz = 3
      foo # => #<OpenStruct bar=1, baz=3>
      
      class FooBar
        kwattr :foo, bar: 21
      end
      
      foobar = FooBar.new(foo: 42) # => #<FooBar @foo=42, @bar=21>
      foobar.foo # => 42
      foobar.bar # => 21
      
      class FooBar
        attr_reader :foo, :bar
      
        def initialize(foo:, bar: 21)
          @foo = foo
          @bar = bar
        end
      end