2013-04-17 2 views
0

그래서 Ruby를 처음 사용하고 숙어를 배우는 데는 상당히 익숙합니다. 나는 여전히 정적 인 타이핑 사고 방식을 가지고 있기 때문에, 한 가지 문제는 과도하게 타이핑한다는 것입니다. 어쨌든, 내 상황은 이것이야.Ruby 인수에서 여러 유형을 처리하기위한 "적절한"관용구

저는 Gene이라는 객체가 있습니다 : name과 : id. 유전자 배열을 유지 관리하는 Genotype이라는 또 다른 개체가 있습니다.

주어진 유전자형에 주어진 유전자가 포함되어 있는지 확인하고 싶습니다. Genotype.has_gene을 전달할 수 있습니까? 유전자 이름, 유전자 id 또는 실제 유전자. 전자의 경우, 루틴은 이름 OR ID 중 일치하는 것을 통과합니다. 전체 유전자가 전달되면 루틴은 두 값의 일치를 주장합니다.

내 논리는 전달 된 값이 Integer인지 확인하는 것이며,이 경우 전달 된 값이 ID라고 가정합니다. 그렇지 않으면 String인지 확인하고 이름이라고 가정합니다. 그렇지 않으면 유전자인지 확인하십시오. 다른 사람은 불평하고 보석합니다.

코드는 다음과 같습니다

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 

뭔가 잘못 여기 느낌하지만 난 그것을 아래로 고정 할 수 없습니다. 루비의 유명한 간결함이 부족한 것처럼 보입니다 :-)

생각들?

+0

찾고있는 것이'유전자 '인 경우 왜'문자열 '과'정수 '를 사용 하시겠습니까? 유형에 대한 테스트는 정말 나쁜 코드 냄새입니다. –

+0

이런 종류의 일을하는 경우'Fixnum','String','Gene','Symbol','NilClass' 등의 메소드에 to_gene 메소드를 추가하면 옵션이 될 수 있습니다. 그러면 간단히'@ gene_specs.include? (gene.to_gene)'할 수 있습니다. 그것은 대답이 아닌 커다란 * *와 * * 일 수도 있습니다. –

+0

글쎄, 진 (Gene)은 실제로 Integer와 String의 패키지입니다. 따라서 유효한 입력은 Integer, String 또는 Integer-and-String, 즉 Gene입니다. 그러나 관계없이, 나쁜 냄새에 대한 나의 인식은 게시물로 이어지는 것이 었습니다 :-) –

답변

1

다음은 어떻게 단순화할까요? 원하는 경우 오리 타이핑을 사용할 수도 있지만 코드를 더 복잡하게 만들 것이라고 생각합니다. 기본적으로

def genes 
    @gene_specs.collect &:gene 
end 

def has_gene?(x) 
    case x 
    when Integer 
    genes.any? { |g| g.id == x } 
    when String 
    genes.any? { |g| g.name == x } 
    when Gene 
    genes.include?(x) # assumes that Gene#== is defined well 
    else 
    raise ArgumentError, "Can't intepret passed data as gene information" 
    end 
end 

, 루비 신분으로 개체를 비교합니다 (즉, 메모리에서의 위치는)하지만, 유전자 클래스에 대한이 같은 다른 뭔가를 수행 할 수 있습니다 :

class Gene 
    def ==(other) 
    return false unless other.class == Gene 
    id == other.id 
    end 
end 

그것은 떨어져 지불을 Ruby's Enumerable module에있는 방법을 연구하는 데 시간을 보내십시오.

+1

내가 공부할 필요가있는 당신의 직감 Enumerable은 죽었다. 치트 시트를 가져 주셔서 감사합니다. –

1

루비가 메서드 매개 변수의 형식을 적용하지 않지만 좋은 이유가없는 한 하나의 매개 변수에 여러 형식을 허용하는 것은 여전히 ​​바람직하지 않습니다. 당신은 별도의 세 가지 방법을 제공하는 경우는 명확하게 다음과 같습니다

def has_gene?(gene) 
    ... 
end 

def has_gene_with_id?(id) 
    ... 
end 

def has_gene_with_name?(name) 
    ... 
end 
+1

검사 논리가 번거롭기 때문에 통합하려고했습니다. 배열의 항목을 명시 적으로 반복하고 각 항목을 확인하십시오. @DavidGrayson이 지적했듯이, Enumerable을 더 잘 알면 그 수표를 한 줄짜리로 줄일 수 있습니다. 아직도, 나는 위에서 보여준 올인원 (all-in-one) 방법의 경제를 좋아한다. –

+0

여러 유형을 허용하는 것이 좋지 않다고 생각합니다.필요가있는 메소드에만 사용하십시오. Ruby on Rails의'link_to' 메소드는 다양한 유형의 인수를 취하는 다양한 방법의 좋은 예입니다. 또한 @ lassej의 패턴을 독립적으로 유연한 두 개의 인수를 취하는 방법으로 확장하는 것은 꽤 못생긴 것입니다. –

0

루비뿐만 아니라 동적으로 입력 된입니다,하지만 내용의 오리 입력 패러다임을 준수 :

나는 새를 볼 때 그 오리처럼 걷고, 오리처럼 수영하고, 오리처럼 돌팔이 키운다. 나는 그 새를 오리라고 부른다.

이것은 무엇을 의미합니까? Ruby에서 Object가 정확히 X 클래스 인 경우 특별한 상황이 아니라면 실제로 신경 쓰지 않아야합니다. X 개체처럼 작동하는지주의해야합니다.

어떻게 확인하나요? 특정 객체가 원하는대로 작동하는지 확인하는 가장 유명한 방법은 #respond_to?을 사용하는 것입니다. 이 메소드는 객체에서 메소드를 호출 할 수 있는지 확인합니다.

여기에서 객체가 #to_x 형태의 메소드에 응답하는지 확인 할 수 있습니다. 여기에서 x은 클래스 (심지어 사용자 정의 클래스)의 이름이고 호출하여 모든 유형을 이동하려는 클래스로 변환합니다 필요한 것.

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 모두 예상처럼은하지 않고, 일 것이다 심지어 서로를 안다.

+0

문자열의 경우 모든 객체에'to_s '가 있기 때문에 더 나은 선택은'to_str'을 사용하는 것입니다. –

+0

여기에'to_sr'와'to_i'가 아닌'to_str'과'to_int'를 사용해야합니다. 이전의 "IS-A"(당신이 여기에서 원하는 것임), 후자는 "완전히 어리석은 표현"을 가지고 있지 않다는 것을 의미합니다. 예를 들어,'nil'은 확실히 문자열이 아니지만, 빈 문자열처럼 완전히 어리석은 문자열 표현을 가지고 있지 않습니다. –

+3

그러나'respond_to? '를 사용하는 것은 오리 입력이 아니라, 누군가가 잘못된 객체를 넘길 까봐 두려워서 * 치킨 타이핑 *입니다. @DavidGrayson, –

0

당신은 무엇을 찾아야할지 지정하는 클래스의 사용자를 허용, 블록을 받아들이는 방법을 쓸 수있다 : 루비 예 Kernel#Array 기본 객체로 값을 강요하는 방법이있다

Gene = Struct.new(:name, :id) 
class Genotype 
    def initialize 
    @arr=[] 
    end 
    def add(gene) 
    @arr << gene 
    end 
    def any?(&block) 
    @arr.any?(&block) 
    end 
end 

gt = Genotype.new 
gt.add Gene.new('a',0) 
gt.add Gene.new('b',1) 
p gt.any?{|g| g.name == "john"} #false 
p gt.any?{|g| g.values == ["b",1]} #true 
2

합니다. 우리는 합리적인 배열에 값을 강요하기 위해 이것을 사용할 수 있습니다 : 이것은 우리가 공간 확인 유형을 많이 지출하지 않고 받아 입력의 종류에 유연 매우 Rubyish 방법을 쓸 수 있습니다

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

. 오히려 KernelGene 방법을 추가하는 대신, 내가 강제 할 수있는 클래스 메소드를 추가 제안, 귀하의 경우를 들어

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 

:이 인위적인 예를 고려

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 메서드를 사용하여 정리하는 좋은 방법을 제공합니다.

관련 문제