0

だから私はRubyにかなり慣れていないので、まだイディオムを学んでいます。私はまだ静的型付けの強い考え方を持っているので、おそらく 1 つの問題は、私が過剰に型付けしているということです。とにかく、私の状況はこれです。

:name と :id を持つ Gene と呼ばれるオブジェクトがあります。Gene の配列を保持する Genotype と呼ばれる別のオブジェクトがあります。

特定の遺伝子型に特定の遺伝子が含まれているかどうかを確認したいと思います。Genotype.has_gene を渡せるようにしたいのですが? 遺伝子名、遺伝子 ID、または実際の遺伝子。前者の場合、ルーチンは name または id のいずれか渡された方に一致します。完全な Gene が渡された場合、ルーチンは両方の値の一致を要求します。

私のロジックは、渡された値が整数であるかどうかを確認することです。その場合、ID であると想定します。それ以外の場合は、文字列であるかどうかを確認し、名前であると想定します。それ以外の場合は、遺伝子かどうかを確認します。それ以外の場合は、不平を言って保釈します。

コードは次のようになります。

def has_gene?( gene )
      if gene.is_a? Integer
        id = gene
        name = ""
      elsif gene.is_a? String
        id = nil
        name = gene
      elsif gene.is_a? Gene
        id = gene.id
        name = gene.name
      else
        raise "Can't intepret passed data as gene information"   
      end
      name_valid = false
      id_valid = false
      @gene_specs.each do |current_gene_spec|
        current_gene = current_gene_spec.gene 
        name_valid = name.empty? || name == current_gene.name
        id_valid = id.nil? || id == current_gene.id
        break if name_valid && id_valid
       end
       return name_valid && id_valid
    end

ここで何かがおかしいと感じますが、特定できません。Rubyの有名な簡潔さが欠けているようです:-)

考え?

4

5 に答える 5

3

Ruby には、 Kernel#Arrayなど、値を基本オブジェクトに変換するメソッドがあります。これを使用して、値を合理的な配列に変換できます。

Array(nil) # => []
Array(10) # => [10]
Array("hello") # => ["hello"]
Array([1, 2, 3]) # => [1, 2, 3]

これにより、型のチェックに多くのスペースを費やさずに、受け取る入力の型に柔軟な非常にRubyっぽいメソッドを書くことができます。この不自然な例を考えてみましょう:

def say_hello(people)
  people = [people] if people.is_a?(Person)

  people.each { |p| puts "Hello, #{p.name}" }
end

def say_hello(people)
  Array(people).each { |p| puts "Hello, #{p.name}" }
end

Geneあなたの場合、にメソッドを追加するのではなくKernel、クラスメソッドを追加して強制を行うことをお勧めします。

class Gene
  def self.coerce(geneish)
    case geneish
    when Gene
      geneish
    when Integer
      new(id: geneish)
    when String
      new(name: geneish)
    else
      raise ArgumentError, "Can't coerce #{geneish.inspect} into a Gene"
    end
  end
end

def has_gene?(gene)
  gene = Gene(gene)

  name_valid = false
  id_valid = false
  @gene_specs.each do |current_gene_spec|
    current_gene = current_gene_spec.gene 
    name_valid = gene.name.empty? || gene.name == current_gene.name
    id_valid = gene.id.nil? || gene.id == current_gene.id
    break if name_valid && id_valid
  end
  return name_valid && id_valid
end

メソッドの残りの部分はhas_gene?そのまま残しましたが、ここでの他の回答のいくつかには、いくつかのEnumerableメソッドを使用してクリーンアップするための良い提案があります。

于 2013-12-10T20:32:37.683 に答える
1

ruby はメソッド パラメーターの型を強制しませんが、正当な理由がない限り、1 つのパラメーターに複数の型を許可することは依然として悪い習慣です。3 つの個別のメソッドを指定すると、より明確になります。

def has_gene?( gene)
  ...
end

def has_gene_with_id?( id)
  ...
end

def has_gene_with_name?( name)
  ...
end
于 2013-04-17T15:17:37.983 に答える
0

Ruby は動的に型付けされているだけでなく、ダック型付けのパラダイムに準拠しています。

アヒルのように歩き、アヒルのように泳ぎ、アヒルのように鳴く鳥を見るとき、私はその鳥をアヒルと呼びます。

どういう意味ですか?Xこれは、Ruby では、(特定の状況でない限り) オブジェクトが正確なクラスであるかどうかを気にする必要がないことを意味します。オブジェクトのように振る舞うかどうか気にする必要がありXます。

どのように確認しますか?特定のオブジェクトが意図したとおりに動作するかどうかを確認する最も有名な方法は、#respond_to?. このメソッドは、オブジェクトでメソッドを呼び出すことができるかどうかをチェックします。

そこから、オブジェクトがクラスの名前 (カスタム クラスであっても) の形式でメソッドに応答するかどうかを確認し、それを呼び出して任意の型を必要なクラスに変換することができます#to_xxたとえば、メソッド内で文字列のみが使用されると予想される場合は、次のようにします。

def a_method( string )
    unless (string.respond_to? :to_str) // trigger error
    string = string.to_str
    // use string
end

このように、次のような特別なタイプを定義している場合Duck:

class Duck

    def to_str
        // internally convert Duck to String
    end

    ...

end

私はそれをあなたの関数に渡すことができます:

obj = Duck.new
a_method( obj )

これは、私と の設計者の両方がa_methodお互いに知らなくても、期待どおりに機能します。

于 2013-04-17T15:19:58.680 に答える