Programing

초보자가주의해야 할 Ruby Gotchas는 무엇입니까?

crosscheck 2020. 8. 9. 09:47
반응형

초보자가주의해야 할 Ruby Gotchas는 무엇입니까? [닫은]


저는 최근에 Ruby 프로그래밍 언어를 배웠고, 대체로 좋은 언어입니다. 하지만 예상했던 것만 큼 간단하지 않다는 사실에 놀랐습니다. 더 정확하게는, "최소 놀라움의 법칙"은 나에게별로 존경받지 못하는 것 같았습니다 (물론 이것은 매우 주관적입니다). 예를 들면 :

x = true and false
puts x  # displays true!

그리고 유명한 :

puts "zero is true!" if 0  # zero is true!

Ruby 초보자에게 경고 할 다른 "Gotchas"는 무엇입니까?


Wikipedia 루비 문제

기사에서 :

  • 대문자로 시작하는 이름은 상수로 취급되므로 지역 변수는 소문자로 시작해야합니다.
  • $문자 @는 Perl에서와 같이 가변 데이터 유형을 나타내지 않고 범위 확인 연산자로 작동합니다.
  • 부동 소수점 숫자를 표시하려면 0 자리 숫자 ( 99.0) 또는 명시 적 변환 ( 99.to_f)을 따라야합니다 . 99.숫자는 메서드 구문에 영향을 받기 때문에 점 ( ) 을 추가하는 것만으로는 충분하지 않습니다 .
  • 비 부울 데이터의 부울 평가는 엄격 : 0, ""그리고 []모든 평가된다 true. C에서 표현식 0 ? 1 : 00(즉, false)로 평가됩니다 . 그러나 Ruby에서는 1모든 숫자 가 다음 과 같이 평가되므로 true. nilfalse평가합니다 false. 이 규칙의 결과는 규칙에 따라 Ruby 메서드 (예 : 정규 표현식 검색)가 성공시 숫자, 문자열, 목록 또는 기타 거짓이 아닌 값을 반환하지만 nil실패시 (예 : 불일치) 반환한다는 것 입니다. 이 규칙은 단지 특수 목적 스몰 토크에서 사용 true하고는 false부울 표현식에 사용할 수 있습니다.
  • 1.9 이전 버전에는 문자 데이터 유형이 없습니다 (문자 유형을 제공하는 C와 비교 char). 이것은 문자열을 슬라이싱 할 때 놀라움을 유발할 수 있습니다. "abc"[0]yields 97( 문자열 의 첫 번째 문자의 ASCII 코드를 나타내는 정수); 수득 "a"사용 "abc"[0,1]하거나 (1 길이의 문자열을) "abc"[0].chr.
  • 표기법 statement until expression은 다른 언어의 동등한 명령문 (예 : do { statement } while (not(expression));C / C ++ / ...)과 달리 표현식이 이미이면 실제로 명령문을 실행하지 않습니다 true. 이것은 statement until expression실제로 구문 상 설탕 이기 때문 입니다.

    until expression
      statement
    end
    

    등가의 C에있는 / C ++이다 while (not(expression)) statement;처럼 statement if expression에 상당

    if expression
      statement
    end
    

    그러나 표기법

    begin
      statement
    end until expression
    

    루비에서는 표현식이 이미 참인 경우에도 실제로 한 번 문을 실행합니다.

  • 상수는 객체에 대한 참조이므로 상수가 참조하는 내용을 변경하면 경고가 생성되지만 객체 자체를 수정하면 그렇지 않습니다. 예를 들어, Greeting << " world!" if Greeting == "Hello"오류 또는 경고를 생성하지 않습니다. 이것은 finalJava의 변수 와 유사 하지만 Ruby에는 Java와 달리 객체를 "고정"하는 기능도 있습니다.

다른 언어와 현저하게 다른 일부 기능 :

  • 조건식 and의 일반적인 연산자 or는 일반적인 우선 순위 규칙을 따르지 and않습니다 or. 보다 엄격하게 바인딩하지 않습니다 . 루비는 표현식 연산자가 ||&&예상대로 작동합니다.

  • definside def는 파이썬 프로그래머가 기대하는 바를 수행하지 않습니다.

    def a_method
        x = 7
        def print_x; puts x end
        print_x
    end
    

    이것은 x정의되지 않은 것에 대한 오류를 제공합니다 . 당신은을 사용해야합니다 Proc.

언어 기능

  • 메서드 인수 주위에 괄호를 생략하면 메서드가 여러 매개 변수를 사용하는 경우 예기치 않은 결과가 발생할 수 있습니다. Ruby 개발자는 향후 Ruby 버전에서 다중 매개 변수 메소드에서 괄호를 생략하는 것이 허용되지 않을 수 있다고 말했습니다. 현재 (2007 년 11 월) Ruby 인터프리터는 ()코드의 모호한 의미를 피하기 위해 작성자가를 생략하지 않도록 권장하는 경고를 표시 합니다. 사용하지 않는 ()것은 여전히 ​​일반적인 관행이며, 특히 Ruby를라는 메서드와 함께 사람이 읽을 수있는 도메인 별 프로그래밍 언어로 사용하는 것이 method_missing()좋습니다.

초보자는 평등 방법에 문제가 있습니다 .

  • a == b : a와 b가 같은지 확인합니다. 이것이 가장 유용합니다.
  • a.eql? b : 또한 a와 b가 같은지 확인하지만 때로는 더 엄격합니다 (예를 들어 a와 b가 동일한 유형인지 확인할 수 있음). 주로 해시에서 사용됩니다.
  • a. 같습니까? b : a와 b가 동일한 객체인지 확인 (신원 확인)
  • a === b : case 문에 사용됩니다 ( " a matches b " 로 읽음 ).

이 예는 처음 세 가지 방법을 명확히해야합니다.

a = b = "joe"

a==b       # true
a.eql? b   # true
a.equal? b # true (a.object_id == b.object_id)

a = "joe"
b = "joe"

a==b       # true
a.eql? b   # true
a.equal? b # false (a.object_id != b.object_id)

a = 1
b = 1.0

a==b       # true
a.eql? b   # false (a.class != b.class)
a.equal? b # false

참고 == , EQL? 동일? 항상 대칭이어야합니다. a == b이면 b == a입니다.

또한 ==eql? 둘 다 Object 클래스에서 동일한 별칭으로 구현 됩니까? , 그래서 새 클래스를 만들고 ==eql? 평범한 정체성이 아닌 다른 것을 의미하려면 둘 다 재정의해야합니다. 예를 들면 :

class Person
    attr_reader name
    def == (rhs)
      rhs.name == self.name  # compare person by their name
    end
    def eql? (rhs)
      self == rhs
    end
    # never override the equal? method!
end

=== 다른 방법 동작합니다. 우선 그것은 대칭 적이 지 않습니다 (a === b는 b === a를 의미 하지 않습니다 ). 내가 말했듯이 a === b를 "a가 b와 일치"로 읽을 수 있습니다. 다음은 몇 가지 예입니다.

# === is usually simply an alias for ==
"joe" === "joe"  # true
"joe" === "bob"  # false

# but ranges match any value they include
(1..10) === 5        # true
(1..10) === 19       # false
(1..10) === (1..10)  # false (the range does not include itself)

# arrays just match equal arrays, but they do not match included values!
[1,2,3] === [1,2,3] # true
[1,2,3] === 2       # false

# classes match their instances and instances of derived classes
String === "joe"   # true
String === 1.5     # false (1.5 is not a String)
String === String  # false (the String class is not itself a String)

경우 문을 기반으로 ===의 방법 :

case a
  when "joe": puts "1"
  when 1.0  : puts "2"
  when (1..10), (15..20): puts "3"
  else puts "4"
end

다음과 같습니다.

if "joe" === a
  puts "1"
elsif 1.0 === a
  puts "2"
elsif (1..10) === a || (15..20) === a
  puts "3"
else
  puts "4"
end

인스턴스가 일종의 컨테이너 또는 범위를 나타내는 새 클래스를 정의하는 경우 ( include? 또는 match? 메서드 같은 것이있는 경우 ) 다음 과 같이 === 메서드 를 재정의하는 것이 유용 할 수 있습니다 .

class Subnet
  [...]
  def include? (ip_address_or_subnet)
    [...]
  end
  def === (rhs)
    self.include? rhs
  end
end

case destination_ip
  when white_listed_subnet: puts "the ip belongs to the white-listed subnet"
  when black_listed_subnet: puts "the ip belongs to the black-listed subnet"
  [...]
end

  • 원숭이 패치 . Ruby에는 개방형 클래스가 있으므로 런타임에 동작을 동적으로 변경할 수 있습니다.

  • 개체 수있는 정의되지 않은 방법에 응답 하는 경우 method_missing또는 send무시되었습니다. 이것은 Ruby의 메시지 기반 메소드 호출을 이용합니다. RailsActiveRecord 시스템은이를 사용하여 큰 효과를냅니다.


다음 코드는 저를 놀라게했습니다. 위험한 문제라고 생각합니다. 쉽게 실행할 수 있고 디버깅하기 어렵습니다.

(1..5).each do |number|
  comment = " is even" if number%2==0
  puts number.to_s + comment.to_s
end

이것은 다음을 인쇄합니다.

1
2 is even
3
4 is even
5

하지만 블록 앞에 comment =아무것도 추가 하면 ...

comment = nil
(1..5).each do |number|
  comment = " is even" if number%2==0
  puts number.to_s + comment.to_s
end

그런 다음 얻을 :

1
2 is even
3 is even
4 is even
5 is even

기본적으로 변수가 블록 내부에서만 정의되면 블록의 끝에서 소멸 된 다음 nil반복 때마다 재설정됩니다 . 그것은 일반적으로 당신이 기대하는 것입니다. 그러나 변수 블록 이전에 정의 된 경우 외부 변수는 블록 내부에서 사용되므로 그 값은 반복 사이에 지속됩니다.

한 가지 해결책은 다음과 같이 작성하는 것입니다.

comment = number%2==0 ? " is even" : nil

나는 (나를 포함하여) 많은 사람들이 " a = b if c"대신 " " 를 쓰는 경향이 있다고 생각합니다 a = (c ? b : nil). 왜냐하면 더 읽기 쉬우 기 때문입니다. 그러나 분명히 부작용이 있습니다.


super인수없이 호출 할 때 재정의 된 메서드는 실제로 재정의 메서드와 동일한 인수를 사용하여 호출됩니다.

class A
  def hello(name="Dan")
    puts "hello #{name}"
  end
end

class B < A
  def hello(name)
    super
  end
end

B.new.hello("Bob") #=> "hello Bob"

실제로 super인수없이 호출하려면 라고 말해야 super()합니다.


블록과 메서드는 기본적으로 마지막 줄의 값을 반환합니다. puts디버깅 목적으로 끝에 문을 추가 하면 불쾌한 부작용이 발생할 수 있습니다.


상속은 Ruby에서 메서드 가시성결정하는 데 아무런 역할을하지 않습니다 .


클래스 변수, 클래스 속성 및 클래스 메서드를 이해하는 데 많은 어려움이있었습니다. 이 코드는 초보자에게 도움이 될 수 있습니다.

class A
  @@classvar = "A1"
  @classattr = "A2"
  def self.showvars
    puts "@@classvar => "+@@classvar
    puts "@classattr => "+@classattr
  end
end

A.showvars
  # displays:
  # @@classvar => A1
  # @classattr => A2

class B < A
  @@classvar = "B1"
  @classattr = "B2"
end

B.showvars
  # displays:
  # @@classvar => B1
  # @classattr => B2

A.showvars
  # displays:
  # @@classvar => B1   #Class variables are shared in a class hierarchy!
  # @classattr => A2   #Class attributes are not

내가 배운 한 가지는 연산자 || =를 조심스럽게 사용하는 것이었다. 부울을 다루는 경우 특별히주의하십시오. 다른 모든 것이 실패하고 'a'가 nil 인 경우 'a'에 기본값을 제공하기 위해 일반적으로 a || = b를 catch all로 사용했습니다. 그러나 a가 거짓이고 b가 참이면 a가 참으로 할당됩니다.


  • 블록은 이해하는 데 정말 중요하며 모든 곳에서 사용됩니다.

  • 메소드 매개 변수를 괄호로 묶지 않아도됩니다. 사용 여부는 귀하에게 달려 있습니다. 어떤 사람들은 항상 그것들을 사용해야한다고 말합니다 .

  • 예외 처리를 위해 throw 및 catch가 아닌 raise 및 rescue를 사용하십시오.

  • 사용할 수는 ;있지만 한 줄에 여러 항목을 넣고 싶지 않으면 사용할 필요가 없습니다.


인스턴스 메서드 클래스 메서드 를 포함하는 믹스 인에 문제가있었습니다 . 이 코드는 초보자에게 도움이 될 수 있습니다.

module Displayable
  # instance methods here
  def display
    puts name
    self.class.increment_displays
  end
  def self.included(base)
    # This module method will be called automatically
    # after this module is included in a class.
    # We want to add the class methods to the class.
    base.extend Displayable::ClassMethods
  end
  module ClassMethods
    # class methods here
    def number_of_displays
      @number_of_displays # this is a class attribute
    end
    def increment_displays
      @number_of_displays += 1
    end
    def init_displays
      @number_of_displays = 0
    end
    # this module method will be called automatically
    # after this module is extended by a class.
    # We want to perform some initialization on a
    # class attribute.
    def self.extended(base)
      base.init_displays
    end
  end
end

class Person
  include Displayable
  def name; @name; end
  def initialize(name); @name=name; end
end

puts Person.number_of_displays # => 0
john = Person.new "John"
john.display # => John
puts Person.number_of_displays # => 1
jack = Person.new "Jack"
jack.display # => Jack
puts Person.number_of_displays # => 2

처음에는 간단하게 다음 과 같이 인스턴스 메서드 클래스 메서드 를 모두 포함하는 모듈을 가질 수 있다고 생각 했습니다.

module Displayable
  def display
    puts name
    self.class.increment_displays
  end
  def self.number_of_displays  # WRONG!
    @number_of_displays
  end
  [...]
end

불행히도 number_of_displays 메소드 는 "모듈 클래스 메소드"이기 때문에 절대 포함되거나 확장되지 않습니다. "모듈 인스턴스 메서드"만 클래스에 포함되거나 (인스턴스 메서드로) 클래스로 확장 될 수 있습니다 (클래스 메서드로). 이것이 믹스 인의 인스턴스 메서드를 모듈에, 믹스 인의 클래스 메서드를 다른 모듈에 넣어야하는 이유입니다 (보통 클래스 메서드를 "ClassMethods"서브 모듈에 넣습니다). 포함 된 매직 메서드 덕분 에 단 하나의 간단한 "include Displayable"호출에 인스턴스 메서드와 클래스 메서드를 모두 쉽게 포함 할 수 있습니다 (위의 예 참조).

이 믹스 인은 각 디스플레이를 클래스 별로 계산합니다. 카운터는 클래스 속성이므로 각 클래스에는 고유 한 속성이 있습니다 (파생 클래스의 @number_of_displays 카운터가 초기화되지 않으므로 Person 클래스에서 새 클래스를 파생하면 프로그램이 실패 할 수 있음). @number_of_displays@@ number_of_displays 로 대체 하여 글로벌 카운터로 만들 수 있습니다. 이 경우 각 클래스 계층에는 자체 카운터가 있습니다. 전역적이고 고유 한 카운터를 원한다면 모듈 속성으로 만들어야합니다.

이 모든 것이 Ruby로 시작했을 때 제게 직관적이지 않았습니다.

나는 여전히 이러한 mixin 메소드 중 일부를 비공개 또는 보호로 만드는 방법을 알 수 없습니다 ( displaynumber_of_displays 메소드 공개 메소드로 포함되어야 함).


범위 표기법에주의하십시오.

(적어도,보다 더 많은 관심을 지불 내가 처음에 한을!)

0..10(2 개의 점)과 0...10(3 개의 점) 사이에 차이가 있습니다 .

저는 Ruby를 아주 좋아합니다. 그러나이 점-점 대 점-점-점은 나를 괴롭힌다. 나는 다음과 같은 미묘한 이중 구문 "기능"이라고 생각합니다.

  • 오타하기 쉽고
  • 코드를 훑어 보면서 눈으로 쉽게 놓칠 수 있습니다.

내 프로그램에 치명적인 버그를 일으킬 수 없어야합니다.


I think "and" and "or" are nods to Perl, which is one of Ruby's more obvious "parents" (the most prominent other being Smalltalk). They both have much lower precedence (lower than assignment, in fact, which is where the behaviour noted comes from) than && and || which are the operators you should be using.

Other things to be aware of that aren't immediately obvious:

You don't really call methods/functions, although it kinda looks that way. Instead, as in Smalltalk, you send a message to an object. So method_missing is really more like message_not_understood.

some_object.do_something(args)

is equivalent to

some_object.send(:do_something, args) # note the :

Symbols are very widely used. That's those things that start with : and they're not immediately obvious (well they weren't to me) but the earlier you get to grips with them the better.

Ruby is big on "duck-typing", following the principal that "if it walks like a duck and quacks like a duck..." that allows informal substitution of objects with a common subset of methods without any explicit inheritance or mixin relationship.


If you declare a setter (aka mutator) using attr_writer or attr_accessor (or def foo=), be careful of calling it from inside the class. Since variables are implicitly declared, the interpreter always has to resolve foo = bar as declaring a new variable named foo, rather than calling the method self.foo=(bar).

class Thing
  attr_accessor :foo
  def initialize
    @foo = 1      # this sets @foo to 1
    self.foo = 2  # this sets @foo to 2
    foo = 3       # this does *not* set @foo
  end
end

puts Thing.new.foo #=> 2

This also applies to Rails ActiveRecord objects, which get accessors defined based on fields in the database. Since they're not even @-style instance variables, the proper way to set those values individually is with self.value = 123 or self['value'] = 123.


Understanding the difference between Time and Date class. Both are different and have created issues while using them in rails. The Time class sometimes conflicts with other Time class libraries present in standard ruby/rails library. It personally took me a lot of time to understand what was exactly going on in my rails app. Later, I figured when I did

Time.new

It was referring to some library in a location that I was not even aware of.

Sorry if I am not clear with what I want to say exactly. If others have faced similar problems, please re-explain.


One that's caught me out in the past is that the newline character (\n) escape sequence—amongst others—isn't supported by strings within single quotes. The backslash itself gets escaped. You have to use double quotes for the escaping to work as expected.


x = (true and false) # x is false

0 and '' are true, as you pointed out.

You can have a method and a module/class by the same name (which makes sense, because the method actually gets added to Object and thus has its own namespace).

There is no multiple inheritance, but frequently "mixin modules" are used to add common methods to multiple classes.


Methods can be redefined and can become a mind-scratcher until you discover the cause. (Admittedly, this error is probably a bit "harder" to detect when a Ruby on Rails controller's action is re-defined by mistake!)

#demo.rb
class Demo

  def hello1
    p "Hello from first definition"
  end

  # ...lots of code here...
  # and you forget that you have already defined hello1

  def hello1
    p "Hello from second definition"
  end

end
Demo.new.hello1

Run:

$ ruby demo.rb
=> "Hello from second definition"

But call it with warnings enabled and you can see the reason:

$ ruby -w demo.rb
demo.rb:10: warning: method redefined; discarding old hello1
=> "Hello from second definition"

I think it is always good to use .length on things... since size is supported by nearly everything and Ruby has dynamic types you can get really weird results calling .size when you have the wrong type... I would much rather get a NoMethodError: undefined method `length', so I generally never call size on objects in Ruby.

bit me more than once.

Also remember objects have ids, so I try not to use variables call id or object_id just to avoid confusion. If I need an id on a Users object it is best to call it something like user_id.

Just my two cents


I'm new to ruby, and on my first round I hit an issue regarding changing floats/strings to an integer. I started with the floats and coded everything as f.to_int. But when I continued on and used the same method for strings I was thrown a curve when it came to run the program.

Aparently a string doesn't have a to_int method, but floats and ints do.

irb(main):003:0* str_val = '5.0'
=> "5.0"
irb(main):006:0> str_val.to_int
NoMethodError: undefined method `to_int' for "5.0":String
        from (irb):6
irb(main):005:0* str_val.to_i
=> 5


irb(main):007:0> float_val = 5.0
=> 5.0
irb(main):008:0> float_val.to_int
=> 5
irb(main):009:0> float_val.to_i
=> 5
irb(main):010:0>

Arbitrary parenthesis threw me at first too. I saw some code with and some without. It took me awhile to realize that either styles are accepted.


Related to monkut's response, Ruby's to_foo methods hint at how strict a conversion they'll do.

Short ones like to_i, to_s tell it to be lazy, and convert them to the target type even if they're not able to be represented accurately in that format. For example:

"10".to_i == 10
:foo.to_s == "foo"

The longer explicit functions like to_int, to_s mean that the object can be natively represented as that type of data. For example, the Rational class represents all rational numbers, so it can be directly represented as a Fixnum (or Bignum) integer by calling to_int.

Rational(20,4).to_int == 5

If you can't call the longer method, it means the object can't be natively represented in that type.

So basically, when converting, if you're lazy with the method names, Ruby will be lazy with the conversion.


From In Ruby why won't foo = true unless defined?(foo) make the assignment?

foo = true unless defined?(foo) #Leaves foo as nil

Because foo is defined as nil when defined?(foo) is called.


Iteration over ruby hashes aren't guaranteed to happen in any particular order. (It's not a bug, it's a feature)

Hash#sort is useful if you need a particular order.

Related question: Why are Ruby’s array of 1000 hashes' key and value pairs always in a particular order?


This one made me mad once:

1/2 == 0.5 #=> false
1/2 == 0   #=> true

1..5.each {|x| puts x}

doesn't work. You have to put the range into parentheses, like

(1..5).each {|x| puts x}

so it doesn't think you're calling 5.each. I think this is a precedence issue, just like the x = true and false gotcha.

참고URL : https://stackoverflow.com/questions/372652/what-are-the-ruby-gotchas-a-newbie-should-be-warned-about

반응형