Ruby Gotchas mà một người mới nên cảnh báo về điều gì? [đóng cửa]


108

Gần đây tôi đã học ngôn ngữ lập trình Ruby, và nói chung nó là một ngôn ngữ tốt. Nhưng tôi khá ngạc nhiên khi thấy nó không đơn giản như tôi đã mong đợi. Chính xác hơn, "quy tắc ít bất ngờ nhất" dường như không được tôi tôn trọng lắm (tất nhiên điều này khá chủ quan). Ví dụ:

x = true and false
puts x  # displays true!

và nổi tiếng:

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

Những "Gotchas" khác mà bạn sẽ cảnh báo cho một người mới chơi Ruby là gì?


@ phrases.insert (0, p) OK @ phrases.insert (p) NOTHING xảy ra @phrases << p # OK
Anno2001

tại sao true and falsetrả về true?
Jürgen Paul

3
Bởi vì "x = true and false" thực sự được hiểu là "(x = true) và false". Đó là vấn đề ưu tiên toán tử: "và" có mức ưu tiên thấp hơn "=". Hầu hết các ngôn ngữ khác đều có thứ tự ưu tiên ngược lại, không hiểu sao họ lại chọn thứ tự này trong Rails, tôi thấy rất khó hiểu. Nếu bạn muốn hành vi "bình thường", chỉ cần gõ "x = (true và false)", sau đó x sẽ là false.
MiniQuark

4
Một giải pháp khác là sử dụng "&&" và "||" thay vì "và" và "hoặc": chúng hoạt động như mong đợi. Ví dụ: "x = true && false" kết quả là x là false.
MiniQuark

"Nguyên tắc ít ngạc nhiên nhất có nghĩa là nguyên tắc ít ngạc nhiên nhất của tôi ." từ en.wikipedia.org/wiki/Ruby_(programming_language)#Phiosystemhy Tương tự với Python. Tôi đã có câu trích dẫn tương tự về người tạo Python nhưng tôi quên nó ở đâu.
Darek Nędza

Câu trả lời:


59

Wikipedia Ruby gotchas

Từ bài báo:

  • Các tên bắt đầu bằng chữ in hoa được coi là hằng số, vì vậy các biến cục bộ phải bắt đầu bằng chữ thường.
  • Các ký tự $@không chỉ ra kiểu dữ liệu biến đổi như trong Perl, mà hoạt động như các toán tử phân giải phạm vi.
  • Để biểu thị số dấu phẩy động, người ta phải theo sau bằng chữ số 0 ( 99.0) hoặc chuyển đổi rõ ràng ( 99.to_f). Không đủ để thêm dấu chấm ( 99.), vì các số dễ bị ảnh hưởng bởi cú pháp phương thức.
  • Đánh giá Boolean của dữ liệu phi boolean là nghiêm ngặt: 0, ""[]tất cả đều được đánh giá để true. Trong C, biểu thức 0 ? 1 : 0đánh giá 0là (nghĩa là sai). Tuy nhiên, trong Ruby, nó cho kết quả 1, vì tất cả các số đều đánh giá true; chỉ nilfalseđánh giá đến false. Hệ quả của quy tắc này là các phương thức Ruby theo quy ước - ví dụ, tìm kiếm biểu thức chính quy - trả về số, chuỗi, danh sách hoặc các giá trị không sai khác khi thành công, nhưng nilkhi thất bại (ví dụ: không khớp). Quy ước này cũng được sử dụng trong Smalltalk, nơi chỉ các đối tượng đặc biệt truefalsecó thể được sử dụng trong biểu thức boolean.
  • Các phiên bản trước 1.9 thiếu kiểu dữ liệu ký tự (so với C, cung cấp kiểu charcho các ký tự). Điều này có thể gây ra bất ngờ khi cắt các chuỗi: "abc"[0]yields 97(một số nguyên, đại diện cho mã ASCII của ký tự đầu tiên trong chuỗi); để "a"sử dụng "abc"[0,1](chuỗi con có độ dài 1) hoặc "abc"[0].chr.
  • Ký hiệu statement until expression, không giống như các câu lệnh tương đương của các ngôn ngữ khác (ví dụ như do { statement } while (not(expression));trong C / C ++ / ...), thực sự không bao giờ chạy câu lệnh nếu đã có biểu thức true. Điều này là do statement until expressionthực sự là đường cú pháp qua

    until expression
      statement
    end

    , tương đương với trong C / C ++ while (not(expression)) statement;giống như statement if expressiontương đương với

    if expression
      statement
    end

    Tuy nhiên, ký hiệu

    begin
      statement
    end until expression

    trong Ruby thực tế sẽ chạy câu lệnh một lần ngay cả khi biểu thức đã đúng.

  • Bởi vì hằng số là tham chiếu đến các đối tượng, thay đổi những gì một hằng số đề cập đến sẽ tạo ra cảnh báo, nhưng việc sửa đổi bản thân đối tượng thì không. Ví dụ: Greeting << " world!" if Greeting == "Hello"không tạo ra lỗi hoặc cảnh báo. Điều này tương tự như finalcác biến trong Java, nhưng Ruby cũng có chức năng "đóng băng" một đối tượng, không giống như Java.

Một số tính năng khác biệt đáng kể so với các ngôn ngữ khác:

  • Các toán tử thông thường cho biểu thức điều kiện andor, không tuân theo các quy tắc thông thường về mức độ ưu tiên: andkhông ràng buộc chặt hơn or. Ruby cũng có các toán tử biểu thức ||&&hoạt động như mong đợi.

  • defbên trong defkhông làm những gì một lập trình viên Python có thể mong đợi:

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

    Điều này gây ra lỗi về việc xkhông được xác định. Bạn cần sử dụng a Proc.

Tính năng ngôn ngữ

  • Việc bỏ qua dấu ngoặc quanh các đối số của phương thức có thể dẫn đến kết quả không mong muốn nếu các phương thức nhận nhiều tham số. Các nhà phát triển Ruby đã tuyên bố rằng việc bỏ qua dấu ngoặc đơn trên các phương thức đa tham số có thể không được phép trong các phiên bản Ruby trong tương lai; hiện tại (tháng 11 năm 2007) trình thông dịch Ruby đưa ra một cảnh báo khuyến khích người viết không bỏ qua (), để tránh ý nghĩa mơ hồ của mã. Không sử dụng ()vẫn là một thực tế phổ biến và có thể đặc biệt tuyệt vời khi sử dụng Ruby như một ngôn ngữ lập trình miền cụ thể có thể đọc được của con người, cùng với phương thức được gọi method_missing().

1
Ruby 1.9 cũng thiếu kiểu dữ liệu ký tự. Trong 1.8, toán tử chỉ mục trả về một Fixnum; trong 1.9, nó tương đương với việc cắt một chuỗi một ký tự.
whitequark

38

Người mới sẽ gặp khó khăn với các phương pháp bình đẳng :

  • a == b : kiểm tra xem a và b có bằng nhau không. Điều này là hữu ích nhất.
  • a.eql? b : cũng kiểm tra xem a và b có bằng nhau hay không, nhưng đôi khi nó nghiêm ngặt hơn (nó có thể kiểm tra xem a và b có cùng kiểu không). Nó chủ yếu được sử dụng trong Hashes.
  • a. hệ quả? b : kiểm tra xem a và b có phải là cùng một đối tượng hay không (kiểm tra danh tính).
  • a === b : được sử dụng trong câu lệnh trường hợp (tôi đọc nó là " a khớp với b ").

Những ví dụ này sẽ làm rõ 3 phương pháp đầu tiên:

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

Lưu ý rằng == , eql? bằng nhau? nên luôn luôn đối xứng: nếu a == b thì b == a.

Cũng lưu ý rằng ==eql? cả hai đều được thực hiện trong lớp Object dưới dạng bí danh để bằng nhau? , vì vậy nếu bạn tạo một lớp mới và muốn ==eql? nghĩa là một cái gì đó khác ngoài danh tính rõ ràng, thì bạn cần ghi đè cả hai. Ví dụ:

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

Các === phương pháp xử lý khác nhau. Trước hết nó không đối xứng (a === b không ngụ ý rằng b === a). Như tôi đã nói, bạn có thể đọc a === b là "a khớp với b". Đây là vài ví dụ:

# === 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)

Câu lệnh trường hợp dựa trên phương thức === :

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

tương đương với điều này:

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

Nếu bạn xác định một lớp mới có các cá thể đại diện cho một số loại vùng chứa hoặc phạm vi (nếu nó có một cái gì đó như phương thức include? Hoặc match? ), Thì bạn có thể thấy hữu ích khi ghi đè phương thức === như thế này:

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

1
Ngoài ra: a = 'строка'; b = 'строка'; pa == b; a = a.force_encoding 'ASCII-8BIT'; b = b.force_encoding 'UTF-8'; pa == b; pa === b; p a.eql? b; p a.equal? b
Nakilon


18

Đoạn mã sau đây làm tôi ngạc nhiên. Tôi nghĩ đó là một vấn đề nguy hiểm: vừa dễ gặp phải vừa khó gỡ lỗi.

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

Bản in này:

1
2 is even
3
4 is even
5

Nhưng nếu tôi chỉ thêm comment =bất cứ thứ gì trước khối ...

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

Sau đó, tôi nhận được:

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

Về cơ bản, khi một biến chỉ được xác định bên trong một khối, thì nó sẽ bị hủy ở cuối khối, và sau đó nó được đặt lại nilsau mỗi lần lặp. Đó thường là những gì bạn mong đợi. Nhưng nếu biến được xác định trước khối, thì biến bên ngoài được sử dụng bên trong khối, và giá trị của nó do đó không đổi giữa các lần lặp.

Một giải pháp sẽ là viết điều này thay thế:

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

Tôi nghĩ rằng rất nhiều người (bao gồm cả tôi) có xu hướng viết " a = b if c" thay vì " a = (c ? b : nil)", vì nó dễ đọc hơn, nhưng rõ ràng là nó có tác dụng phụ.


4
Bạn cũng có thể làm mờ biến phạm vi bên ngoài bằng (1..5) do | number; comment | ..... Đọc ở đây stackoverflow.com/questions/1654637/…
Özgür,

6
Điều này có vẻ hợp lý với tôi. Phạm vi này là điển hình của các ngôn ngữ khác, nó chỉ là cú pháp khác nhau.
g.

Tuy nhiên, bạn có thể viết a = (b if c)để có được hiệu quả mong muốn, mà không cần dấu ba chấm. Điều này là do b if cđánh giá là nil nếu clà falsey.
Cameron Martin

16

Khi gọi superkhông có đối số, phương thức được ghi đè thực sự được gọi với các đối số giống như phương thức ghi đè.

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"

Để thực sự gọi supermà không có đối số, bạn cần phải nói super().


3
Nếu B#helloname = 42trước dấu super, thì nó nói "xin chào 42".
Andrew Grimm

14

Các khối và phương thức trả về giá trị của dòng cuối cùng theo mặc định. Thêm putscâu lệnh vào cuối cho mục đích gỡ lỗi có thể gây ra các tác dụng phụ khó chịu



11

Tôi đã gặp rất nhiều khó khăn khi hiểu các biến lớp, thuộc tính lớp và phương thức lớp. Mã này có thể giúp một người mới:

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

1
Có, các biến lớp có thể phức tạp. Tôi nghĩ rằng hầu hết những người theo chủ nghĩa Ruby có kinh nghiệm sẽ nói rằng nên tránh chúng, vì thường có những cách khác để giải quyết vấn đề mà không có chúng. Một số người đam mê ngôn ngữ thậm chí sẽ nói rằng các biến lớp của Ruby được thiết kế kém ở cấp độ ngôn ngữ.
David J.

8

một điều tôi học được là sử dụng toán tử || = một cách cẩn thận. và đặc biệt cẩn thận nếu bạn đang xử lý boolean. tôi thường sử dụng || = b như một bắt tất cả để cung cấp cho 'a' một giá trị mặc định nếu mọi thứ khác không thành công và 'a' vẫn là con số không. nhưng nếu a sai và b đúng, thì a sẽ được gán đúng.


Bạn có thể sử dụng a = b if a.nil?hoặc @a = b unless defined?(@a).
Andrew Grimm

8
  • Các khối thực sự quan trọng để hiểu, chúng được sử dụng ở mọi nơi.

  • Bạn không cần dấu ngoặc đơn xung quanh các tham số phương thức. Bạn có sử dụng chúng hay không là tùy thuộc vào bạn. Một số người nói rằng bạn nên luôn sử dụng chúng .

  • Sử dụng nâng cao và cứu hộ để xử lý ngoại lệ, không ném và bắt.

  • Bạn có thể sử dụng ;nhưng không nhất thiết phải dùng trừ khi bạn muốn đặt nhiều thứ trên một dòng.


Nếu bạn không có kế hoạch vượt qua Ruby 1.8.6 thì hãy bỏ qua parens nhiều như bạn muốn. Nếu không, có lẽ bạn nên sử dụng chúng.
Mike Woodhouse

7

Tôi đã gặp sự cố với các mixin chứa các phương thức phiên bản phương thức lớp. Mã này có thể giúp một người mới:

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

Lúc đầu, tôi nghĩ rằng tôi có thể có các mô-đun với cả phương thức phiên bản phương thức lớp bằng cách đơn giản thực hiện điều này:

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

Thật không may, phương thức number_of_displays sẽ không bao giờ được bao gồm hoặc mở rộng vì nó là một "phương thức lớp mô-đun". Chỉ "các phương thức thể hiện mô-đun" mới có thể được đưa vào một lớp (dưới dạng các phương thức thể hiện) hoặc mở rộng thành một lớp (dưới dạng các phương thức lớp). Đây là lý do tại sao bạn cần đặt các phương thức thể hiện của mixin vào một mô-đun và các phương thức lớp của mixin của bạn vào một mô-đun khác (bạn thường đặt các phương thức của lớp vào một mô-đun con "ClassMethods"). Nhờ magic method, bạn có thể dễ dàng bao gồm cả phương thức instance và phương thức lớp chỉ trong một lệnh gọi đơn giản "include Displayable" (như trong ví dụ trên).

Mixin này sẽ tính từng màn hình trên cơ sở từng lớp . Bộ đếm là một thuộc tính lớp, vì vậy mỗi lớp sẽ có một lớp riêng (chương trình của bạn có thể sẽ thất bại nếu bạn dẫn xuất một lớp mới từ lớp Người vì bộ đếm @number_of_displays cho lớp dẫn xuất sẽ không bao giờ được khởi tạo). Bạn có thể muốn thay @number_of_displays bằng @@ number_of_displays để biến nó thành bộ đếm toàn cầu. Trong trường hợp này, mỗi hệ thống phân cấp lớp sẽ có bộ đếm riêng. Nếu bạn muốn một bộ đếm toàn cầu và duy nhất, có lẽ bạn nên đặt nó thành một thuộc tính mô-đun.

Tất cả những điều này chắc chắn không trực quan đối với tôi khi tôi bắt đầu với Ruby.

Mặc dù vậy, tôi vẫn không thể tìm ra cách đặt một số phương thức mixin này ở chế độ riêng tư hoặc được bảo vệ (chỉ phương thức displaynumber_of_displays nên được đưa vào làm phương thức công khai).


7

Chú ý đến ký hiệu Phạm vi.

(Ít nhất, hãy chú ý nhiều hơn lúc đầu tôi đã làm!)

Có sự khác biệt giữa 0..10 (hai chấm) và 0...10(ba chấm).

Tôi rất thích Ruby. Nhưng thứ chấm-chấm so với chấm-chấm-chấm này làm tôi khó chịu. Tôi nghĩ rằng một "tính năng" cú pháp kép tinh tế đó là:

  • dễ gõ nhầm và
  • dễ dàng bỏ sót bằng mắt khi nhìn lướt qua mã

không thể gây ra lỗi nghiêm trọng từng phần một trong các chương trình của tôi.


1
Không khác nhiều so với for (i=0; i<max; i++)for (i=0; i<=max; i++)
g.

Tôi đang cố gắng tìm hiểu sự khác biệt giữa 0..10 và 0 ... 10 là gì.
Luis D Urraca

6

Tôi nghĩ " and" và " or" là gật đầu với Perl, đó là một trong những "cha mẹ" rõ ràng hơn của Ruby (người nổi bật nhất là Smalltalk). Cả hai đều có mức độ ưu tiên thấp hơn nhiều (thực tế là thấp hơn phép gán, đó là nơi xuất phát hành vi được lưu ý) hơn &&||là toán tử bạn nên sử dụng.

Những điều khác cần lưu ý mà không rõ ràng ngay lập tức:

Bạn không thực sự gọi các phương thức / hàm, mặc dù nó có vẻ như vậy. Thay vào đó, như trong Smalltalk, bạn gửi tin nhắn đến một đối tượng. Vì vậy, method_missingthực sự là thích hơn message_not_understood.

some_object.do_something(args)

tương đương với

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

Biểu tượng được sử dụng rất rộng rãi. Đó là những điều bắt đầu :và chúng không rõ ràng ngay lập tức (tôi cũng không biết) nhưng bạn càng nắm bắt được chúng càng sớm thì càng tốt.

Ruby rất nổi tiếng về "kiểu gõ vịt", theo nguyên tắc chính là "nếu nó đi như vịt và quạc như vịt ..." cho phép thay thế không chính thức các đối tượng bằng một tập con chung các phương thức mà không có bất kỳ mối quan hệ kế thừa hoặc mixin rõ ràng nào.


Cảm ơn. Có một điều tôi ghét về phương thức gửi : nó cho phép bạn gọi các phương thức riêng tư ngay cả bên ngoài lớp! Ôi chao.
MiniQuark

1
@MiniQuark: đó là điều tôi thích ở phương thức gửi!
Andrew Grimm,

6

Nếu bạn khai báo một setter (hay còn gọi là mutator) bằng cách sử dụng attr_writerhoặc attr_accessor(hoặc def foo=), hãy cẩn thận khi gọi nó từ bên trong lớp. Vì các biến được khai báo ngầm định, trình thông dịch luôn phải giải quyết foo = barnhư khai báo một biến mới có tên foo, thay vì gọi phương thức 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

Điều này cũng áp dụng cho các đối tượng Rails ActiveRecord, các đối tượng này lấy các trình truy cập được xác định dựa trên các trường trong cơ sở dữ liệu. Vì chúng thậm chí không phải là các biến phiên bản @ -style, cách thích hợp để đặt các giá trị đó riêng lẻ là với self.value = 123hoặc self['value'] = 123.


5

Hiểu sự khác biệt giữa lớp Thời gian và Ngày. Cả hai đều khác nhau và đã tạo ra sự cố khi sử dụng chúng trong đường ray. Lớp Thời gian đôi khi xung đột với các thư viện lớp Thời gian khác có trong thư viện ruby ​​/ rails tiêu chuẩn. Cá nhân tôi đã mất rất nhiều thời gian để hiểu chính xác những gì đang diễn ra trong ứng dụng rails của tôi. Sau đó, tôi đã tìm ra khi tôi làm

Time.new

Nó đang đề cập đến một thư viện nào đó ở một địa điểm mà tôi thậm chí còn không biết.

Xin lỗi nếu tôi không rõ chính xác những gì tôi muốn nói. Nếu những người khác gặp phải vấn đề tương tự, vui lòng giải thích lại.


4

Một điều khiến tôi chú ý trong quá khứ là \nchuỗi thoát ký tự dòng mới ( ) — trong số những ký tự khác — không được hỗ trợ bởi các chuỗi trong dấu nháy đơn. Dấu gạch chéo ngược tự nó được thoát ra. Bạn phải sử dụng dấu ngoặc kép để thoát hoạt động như mong đợi.


1
Và điều đó có gì khác so với những ngôn ngữ khác?
Robert Gamble

Java, dành cho một. Dấu ngoặc kép trong Java chỉ có thể được sử dụng để bao quanh một ký tự, không phải Chuỗi.
John Topley

1
Điều này phù hợp với bất kỳ ngôn ngữ nào cho phép bạn sử dụng dấu ngoặc kép cho chuỗi và là lý do tại sao chúng làm như vậy.
singpolyma

@John: true, nhưng '\ n' trong Java vẫn sẽ là ký tự dòng mới.
Jorn

1
Nhưng trong Java dấu nháy đơn chỉ tạo ra các giá trị kiểu char. Không phải chuỗi. Đó là sự khác biệt.
jmucchiello

4
x = (true and false) # x is false

0 và '' là đúng, như bạn đã chỉ ra.

Bạn có thể có một phương thức và một mô-đun / lớp có cùng tên (điều này có ý nghĩa, vì phương thức thực sự được thêm vào Đối tượng và do đó có không gian tên riêng của nó).

Không có đa kế thừa, nhưng "mô-đun mixin" thường được sử dụng để thêm các phương thức chung cho nhiều lớp.


0 == true // argh trình biên dịch c trong não tôi đang nổ tung !!
kenny

1
0 == true cho sai trong Ruby. Giá trị 0 là true có ý nghĩa vì true là một đối tượng trong Ruby. Trong C 0 chỉ xảy ra có cùng một biểu diễn là sai.
Jules

Trong một điều kiện trong Ruby, chỉ falsenillà sai. Tất cả những thứ khác đều là giá trị đích thực.
rubyprince

4

Các phương pháp có thể được xác định lại và có thể trở thành vấn đề nhức nhối cho đến khi bạn phát hiện ra nguyên nhân. ( Phải thừa nhận rằng lỗi này có lẽ "khó" hơn một chút để phát hiện khi hành động của bộ điều khiển Ruby on Rails được xác định lại do nhầm lẫn! )

#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

Chạy:

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

Nhưng hãy gọi nó với cảnh báo được bật và bạn có thể thấy lý do:

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

Tôi sẽ +100 việc sử dụng cảnh báo nếu có thể.
Andrew Grimm

3

Tôi nghĩ rằng luôn tốt khi sử dụng .length trên mọi thứ ... vì kích thước được hỗ trợ bởi hầu hết mọi thứ và Ruby có các kiểu động, bạn có thể nhận được kết quả thực sự kỳ lạ khi gọi. Kích thước khi bạn chọn sai loại ... Tôi rất muốn nhận được a NoMethodError: phương thức không xác định `length ', vì vậy tôi thường không bao giờ gọi kích thước trên các đối tượng trong Ruby.

cắn tôi nhiều hơn một lần.

Cũng nên nhớ các đối tượng có id, vì vậy tôi cố gắng không sử dụng các biến gọi id hoặc object_id chỉ để tránh nhầm lẫn. Nếu tôi cần một id trên một đối tượng Người dùng, tốt nhất nên gọi nó là user_id.

Chỉ hai xu của tôi


2

Tôi mới làm quen với ruby ​​và trong vòng đầu tiên của tôi, tôi đã gặp sự cố liên quan đến việc thay đổi số float / string thành số nguyên. Tôi bắt đầu với float và mã hóa mọi thứ là f.to_int . Nhưng khi tôi tiếp tục và sử dụng cùng một phương pháp cho các chuỗi, tôi đã gặp phải một đường cong khi chạy chương trình.

Rõ ràng một chuỗi không có phương thức to_int , nhưng float và int thì có.

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>

Lúc đầu tôi cũng dùng dấu ngoặc đơn tùy tiện. Tôi thấy một số mã có và một số không có. Tôi đã mất một lúc để nhận ra rằng cả hai phong cách đều được chấp nhận.


2

Liên quan đến phản hồi của Monut, to_foocác phương pháp của Ruby gợi ý về mức độ nghiêm ngặt của một chuyển đổi mà họ sẽ thực hiện.

Những cái ngắn gọn như to_i, to_sbảo nó lười và chuyển chúng thành kiểu đích ngay cả khi chúng không thể được biểu diễn chính xác ở định dạng đó. Ví dụ:

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

Các chức năng rõ ràng dài hơn như to_int, to_scó nghĩa là đối tượng có thể được biểu diễn nguyên bản dưới dạng loại dữ liệu đó. Ví dụ, Rationallớp biểu diễn tất cả các số hữu tỉ, vì vậy nó có thể được biểu diễn trực tiếp dưới dạng số nguyên Fixnum (hoặc Bignum) bằng cách gọi to_int.

Rational(20,4).to_int == 5

Nếu bạn không thể gọi phương thức dài hơn, điều đó có nghĩa là đối tượng không thể được biểu diễn nguyên bản trong kiểu đó.

Vì vậy, về cơ bản, khi chuyển đổi, nếu bạn lười với tên phương thức, thì Ruby sẽ lười chuyển đổi.


1
"Lười biếng" có phải là từ thích hợp ở đây?
Andrew Grimm


1

Việc lặp lại các hàm băm ruby ​​không được đảm bảo sẽ xảy ra theo bất kỳ thứ tự cụ thể nào. (Nó không phải là một lỗi, đó là một tính năng)

Hash#sort rất hữu ích nếu bạn cần một đơn đặt hàng cụ thể.

Câu hỏi liên quan: Tại sao mảng 1000 cặp khóa và giá trị băm của Ruby luôn theo một thứ tự cụ thể?


4
đây là không hợp lệ như của 1.9: "Trong Ruby 1.9, tuy nhiên, các yếu tố băm được lặp theo thứ tự chèn của họ" từ Ruby Lập trình Ngôn ngữ
Özgür

0

Điều này đã làm tôi phát điên một lần:

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

Tôi tin rằng điều này sẽ hoạt động giống hệt như trong Java, C và C ++.
Larry

Điều đó thật buồn cười, tôi thậm chí còn không nghĩ về nó, nhưng nếu bạn mở irb và thử điều này, nó sẽ có lý: Vì vậy, (1/2) là một Fixnum và (0,5) là một Float. Và chúng tôi biết rằng Fixnim! = Float.
DemitryT

2
@DemitryT Tôi nghĩ lý do đơn giản hơn là 1/2đánh giá 0không bằng nhau 0.5, bất kể loại nào. Tuy nhiên Rational(1, 2) == 0.5, và 1.0 == 1.
Max Nanasy

ngôn ngữ phổ thông nấc cụt ở đây. đây là điều mà một người mới lập trình ruby ​​AND nên biết.
dtc

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

không hoạt động. Bạn phải đặt phạm vi trong dấu ngoặc đơn, như

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

vì vậy nó không nghĩ rằng bạn đang gọi 5.each. Tôi nghĩ đây là một vấn đề được ưu tiên, giống như x = true and falsegotcha.


Thay vào đó tôi sẽ gọi nó là dấu ngoặc đơn. Thứ hai, nếu bất kỳ mã nào trông giống như có vấn đề về giá trị trả về / mức độ ưu tiên, thì nó phải được bao quanh bởi dấu ngoặc đơn. Vì vậy, đối với tôi, không có gì đặc biệt trên "gotcha" này. Tuy nhiên, bạn có thể tiếp tục viết mọi "gotchas" tổ hợp, điều đó sẽ rất lãng phí thời gian. Thành thật mà nói, ngay cả khi bạn đã có kết quả mong đợi về điều này, tôi vẫn thích xung quanh bằng dấu ngoặc đơn hơn.
Özgür
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.