41

ドキュメントによると、 Structの未設定の属性は次のように設定されていnilます。

未設定のパラメータのデフォルトはnilです。

特定の属性のデフォルト値を指定することは可能ですか?

たとえば、次の構造体の場合

Struct.new("Person", :name, :happy)

happy属性をデフォルトでtrueはなくに設定したいと思いますnil。これどうやってするの?私が次のようにすると

Struct.new("Person", :name, :happy = true)

私は得る

-:1: syntax error, unexpected '=', expecting ')'
Struct.new("Person", :name, :happy = true)
                                    ^
-:1: warning: possibly useless use of true in void context
4

6 に答える 6

30

これは、構造体をサブクラスとして作成 initializeし、次の例のようにデフォルト値でオーバーライドすることによっても実現できます。

class Person < Struct.new(:name, :happy)
    def initialize(name, happy=true); super end
end

一方では、この方法は少し定型文につながります。他方では、それはあなたが探していることを素晴らしく簡潔に行います。

副作用の 1 つ (好みやユース ケースに応じて、利点または煩わしさのいずれかになる可能性があります) は、明示的に設定しない限り、既定Structのすべての属性の既定の動作が失われることです。nil実際、上記の例は、次のようnameに宣言しない限り、必須パラメーターになります。name=nil

于 2014-08-09T03:51:55.260 に答える
19

@rintaun の例に従って、Ruby 2+ でキーワード引数を使用してこれを行うこともできます

A = Struct.new(:a, :b, :c) do
  def initialize(a:, b: 2, c: 3); super end
end

A.new
# ArgumentError: missing keyword: a

A.new a: 1
# => #<struct A a=1, b=2, c=3> 

A.new a: 1, c: 6
# => #<struct A a=1, b=2, c=6>

アップデート

動作させるには、コードを次のように記述する必要があります。

A = Struct.new(:a, :b, :c) do
  def initialize(a:, b: 2, c: 3)
    super(a, b, c)
  end
end
于 2016-02-03T22:00:16.317 に答える
4

@Linuxios は、メンバー ルックアップをオーバーライドする回答を提供しました。これには 2 つの問題があります。メンバーを明示的に nil に設定することはできず、メンバー参照ごとに余分なオーバーヘッドが発生します。::newまたはに提供された部分メンバー値で新しい構造体オブジェクトを初期化するときに、デフォルトを提供したいだけのように思えます::[]

これは、追加のファクトリ メソッドを使用して Struct を拡張するモジュールです。このメソッドを使用すると、必要な構造をハッシュで記述できます。キーはメンバー名であり、値は初期化時に指定されない場合にデフォルトで入力されます。

# Extend stdlib Struct with a factory method Struct::with_defaults
# to allow StructClasses to be defined so omitted members of new structs
# are initialized to a default instead of nil
module StructWithDefaults

  # makes a new StructClass specified by spec hash.
  # keys are member names, values are defaults when not supplied to new
  #
  # examples:
  # MyStruct = Struct.with_defaults( a: 1, b: 2, c: 'xyz' )
  # MyStruct.new       #=> #<struct MyStruct a=1, b=2, c="xyz"
  # MyStruct.new(99)   #=> #<struct MyStruct a=99, b=2, c="xyz">
  # MyStruct[-10, 3.5] #=> #<struct MyStruct a=-10, b=3.5, c="xyz">
  def with_defaults(*spec)
    new_args = []
    new_args << spec.shift if spec.size > 1
    spec = spec.first
    raise ArgumentError, "expected Hash, got #{spec.class}" unless spec.is_a? Hash
    new_args.concat spec.keys

    new(*new_args) do

      class << self
        attr_reader :defaults
      end

      def initialize(*args)
        super
        self.class.defaults.drop(args.size).each {|k,v| self[k] = v }
      end

    end.tap {|s| s.instance_variable_set(:@defaults, spec.dup.freeze) }

  end

end

Struct.extend StructWithDefaults
于 2013-02-25T01:28:46.047 に答える
2

#initializeへの呼び出しを使用して、メソッドをオーバーライドするのが最善の方法だと思います#super(*required_args)

これには、ハッシュ形式の引数を使用できるという追加の利点があります。次の完全なコンパイル例を参照してください。

ハッシュ形式の引数、デフォルト値、Ruby 構造体

# This example demonstrates how to create Ruby Structs that use
# newer hash-style parameters, as well as the default values for
# some of the parameters, without loosing the benefits of struct's
# implementation of #eql? #hash, #to_s, #inspect, and other
# useful instance methods.
#
# Run this file as follows
#
# > gem install rspec
# > rspec struct_optional_arguments.rb --format documentation
#
class StructWithOptionals < Struct.new(
    :encrypted_data,
    :cipher_name,
    :iv,
    :salt,
    :version
    )

    VERSION = '1.0.1'

    def initialize(
        encrypted_data:,
        cipher_name:,
        iv: nil,
        salt: 'salty',
        version: VERSION
        )
        super(encrypted_data, cipher_name, iv, salt, version)
    end
end

require 'rspec'
RSpec.describe StructWithOptionals do
    let(:struct) { StructWithOptionals.new(encrypted_data: 'data', cipher_name: 'AES-256-CBC', iv: 'intravenous') }

    it 'should be initialized with default values' do
        expect(struct.version).to be(StructWithOptionals::VERSION)
    end

    context 'all fields must be not null' do
        %i(encrypted_data cipher_name salt iv version).each do |field|
            subject { struct.send(field) }
            it field do
                expect(subject).to_not be_nil
            end
        end
    end
end
于 2016-08-09T00:15:48.247 に答える