Tại sao những người thiết lập Ruby cần “tự”. trình độ trong lớp?


76

Các bộ cài đặt Ruby - dù được tạo bằng (c)attr_accessorcách thủ công hay bằng tay - dường như là các phương thức duy nhất cần self.đủ điều kiện khi được truy cập trong chính lớp đó. Điều này dường như chỉ đặt Ruby vào thế giới ngôn ngữ:

  • Tất cả các phương thức cần self/ this(như Perl, và tôi nghĩ Javascript)
  • Không có phương thức nào yêu cầu self/ thisis (C #, Java)
  • Chỉ những người thiết lập mới cần self/ this(Ruby?)

So sánh tốt nhất là C # và Ruby, vì cả hai ngôn ngữ đều hỗ trợ các phương thức truy cập hoạt động theo cú pháp giống như các biến cá thể của lớp: foo.x = y, y = foo.x. C # gọi chúng là thuộc tính.

Đây là một ví dụ đơn giản; chương trình tương tự trong Ruby rồi đến C #:

class A
  def qwerty; @q; end                   # manual getter
  def qwerty=(value); @q = value; end   # manual setter, but attr_accessor is same 
  def asdf; self.qwerty = 4; end        # "self." is necessary in ruby?
  def xxx; asdf; end                    # we can invoke nonsetters w/o "self."
  def dump; puts "qwerty = #{qwerty}"; end
end

a = A.new
a.xxx
a.dump

lấy đi self.qwerty =()và nó không thành công (Ruby 1.8.6 trên Linux & OS X). Bây giờ C #:

using System;

public class A {
  public A() {}
  int q;
  public int qwerty {
    get { return q; }
    set { q = value; }
  }
  public void asdf() { qwerty = 4; } // C# setters work w/o "this."
  public void xxx()  { asdf(); }     // are just like other methods
  public void dump() { Console.WriteLine("qwerty = {0}", qwerty); }
}

public class Test {
  public static void Main() {
    A a = new A();
    a.xxx();
    a.dump();
  }
}

Câu hỏi: Điều này có đúng không? Có những dịp nào khác ngoài những người thiết lập mà bản thân là cần thiết? Tức là, có những trường hợp nào khác mà một phương thức Ruby không thể được gọi mà không có bản thân nó?

Chắc chắn có rất nhiều trường hợp mà bản thân trở nên cần thiết. Đây không phải là duy nhất đối với Ruby, chỉ cần rõ ràng:

using System;

public class A {
  public A() {}
  public int test { get { return 4; }}
  public int useVariable() {
    int test = 5;
    return test;
  }
  public int useMethod() {
    int test = 5;
    return this.test;
  }
}

public class Test {
  public static void Main() {
    A a = new A();
    Console.WriteLine("{0}", a.useVariable()); // prints 5
    Console.WriteLine("{0}", a.useMethod());   // prints 4
  }
}

Cùng một sự mơ hồ được giải quyết theo cùng một cách. Nhưng trong khi tinh tế, tôi đang hỏi về trường hợp

  • Một phương pháp đã được xác định, và
  • Không có biến cục bộ nào được xác định và

Chúng ta gặp phải

qwerty = 4

không rõ ràng — đây là một lệnh gọi phương thức hay một phép gán biến cục bộ mới?


@Mike Stone

Chào! Tôi hiểu và đánh giá cao những điểm bạn đã làm và ví dụ của bạn thật tuyệt. Hãy tin tôi khi tôi nói, nếu tôi có đủ danh tiếng, tôi sẽ bỏ phiếu cho câu trả lời của bạn. Tuy nhiên, chúng tôi vẫn không đồng ý:

  • về vấn đề ngữ nghĩa, và
  • về điểm trung tâm của thực tế

Đầu tiên tôi khẳng định, không phải không có sự mỉa mai, chúng ta đang có một cuộc tranh luận ngữ nghĩa về ý nghĩa của 'sự mơ hồ'.

Khi nói đến phân tích cú pháp và ngữ nghĩa ngôn ngữ lập trình (chủ đề của câu hỏi này), chắc chắn bạn sẽ thừa nhận một phổ rộng của khái niệm 'sự mơ hồ'. Hãy chỉ áp dụng một số ký hiệu ngẫu nhiên:

  1. mơ hồ: mơ hồ về từ vựng (lex phải 'nhìn về phía trước')
  2. Mơ hồ: mơ hồ về ngữ pháp (yacc phải trì hoãn phân tích cây phân tích cú pháp)
  3. AMBIGUOUS: sự mơ hồ khi biết mọi thứ tại thời điểm thực thi

(và có cả rác từ 2-3 nữa). Tất cả các danh mục này được giải quyết bằng cách thu thập thêm thông tin theo ngữ cảnh, ngày càng tìm kiếm nhiều hơn trên toàn cầu. Vì vậy, khi bạn nói,

"qwerty = 4" là UNAMBIGUOUS trong C # khi không có biến nào được xác định ...

Tôi không thể đồng ý hơn. Nhưng cũng tương tự, tôi đang nói

"qwerty = 4" là không rõ ràng trong ruby ​​(vì nó hiện đang tồn tại)

"qwerty = 4" không rõ ràng trong C #

Và chúng tôi vẫn chưa mâu thuẫn với nhau. Cuối cùng, đây là nơi chúng tôi thực sự không đồng ý: Có thể hoặc không thể triển khai ruby ​​mà không có bất kỳ cấu trúc ngôn ngữ nào khác như vậy,

Đối với "qwerty = 4", ruby ​​UNAMBIGUOUSLY gọi một setter hiện có nếu
không có biến cục bộ nào được xác định

Bạn nói không. Tôi nói "có; một ruby ​​khác có thể tồn tại hoạt động giống hệt như hiện tại ở mọi khía cạnh, ngoại trừ "qwerty = 4" xác định một biến mới khi không có setter nào và không có cục bộ nào tồn tại, nó gọi setter nếu có tồn tại và gán cho cục bộ nếu có. Tôi hoàn toàn chấp nhận rằng tôi có thể sai. Trên thực tế, một lý do tại sao tôi có thể sai sẽ rất thú vị.

Hãy để tôi giải thích.

Hãy tưởng tượng bạn đang viết một ngôn ngữ OO mới với các phương thức truy cập giống như các bản sao (như ruby ​​& C #). Có thể bạn sẽ bắt đầu với những ngữ pháp khái niệm như:

  var = expr    // assignment
  method = expr // setter method invocation

Nhưng trình biên dịch phân tích cú pháp (thậm chí không phải thời gian chạy) sẽ bị lỗi, bởi vì ngay cả sau khi tất cả các đầu vào được kiểm tra, không có cách nào để biết ngữ pháp nào phù hợp. Bạn đang phải đối mặt với một sự lựa chọn cổ điển. Tôi không thể chắc chắn về các chi tiết, nhưng về cơ bản ruby ​​làm được điều này:

  var = expr    // assignment (new or existing)
  // method = expr, disallow setter method invocation without .

đó là lý do tại sao nó không mơ hồ, trong khi và C # thực hiện điều này:

  symbol = expr // push 'symbol=' onto parse tree and decide later
                // if local variable is def'd somewhere in scope: assignment
                // else if a setter is def'd in scope: invocation

Đối với C #, 'sau' vẫn đang ở thời gian biên dịch.

Tôi chắc rằng ruby ​​cũng có thể làm như vậy, nhưng 'sau' sẽ phải ở trong thời gian chạy, vì như ben đã chỉ ra, bạn không biết cho đến khi câu lệnh được thực thi thì trường hợp nào áp dụng.

Câu hỏi của tôi không bao giờ có ý nghĩa "tôi có thực sự cần 'cái tôi' không?" hoặc "điều gì có thể tránh được sự mơ hồ?" Thay vì tôi muốn biết tại sao lại có sự lựa chọn cụ thể này? Có lẽ đó không phải là hiệu suất. Có thể nó vừa hoàn thành công việc, hoặc tốt nhất là luôn cho phép cục bộ 1 lót ghi đè một phương thức (một yêu cầu trường hợp khá hiếm) ...

Nhưng tôi gợi ý rằng ngôn ngữ động nhất có thể là ngôn ngữ trì hoãn quyết định này lâu nhất và chọn ngữ nghĩa dựa trên thông tin theo ngữ cảnh nhất: vì vậy nếu bạn không có ngôn ngữ cục bộ và bạn đã xác định một setter, nó sẽ sử dụng setter . Đây không phải là lý do tại sao chúng ta thích ruby, smalltalk, objc, bởi vì việc gọi phương thức được quyết định trong thời gian chạy, mang lại khả năng biểu cảm tối đa?


PHP cũng yêu cầu $this->khi truy cập các biến cá thể. Đó là chuyến đi của tôi mọi lúc.
Chloe

Một bộ thu rõ ràng chỉ cần thiết cho các phương thức lớp, không phải phương thức cá thể.
Todd A. Jacobs,

Tôi đồng ý - tôi cũng không thích phương pháp giải quyết tình trạng hỗn loạn này. Vi phạm nguyên tắc ít bất ngờ nhất.
Dogweather

@Dogweather Matz nói rõ ý của anh ấy bằng cách ít ngạc nhiên nhất : Mọi người đều có một nền tảng riêng. ... họ có thể ngạc nhiên bởi các khía cạnh khác nhau của ngôn ngữ. Sau đó, họ đến gặp tôi và nói, "Tôi rất ngạc nhiên bởi tính năng này của ngôn ngữ, do đó, Ruby vi phạm nguyên tắc ít gây ngạc nhiên nhất." Chờ đợi. Chờ đợi. Nguyên tắc ít ngạc nhiên nhất không chỉ dành cho bạn. 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 . Và nó có nghĩa là nguyên tắc ít ngạc nhiên nhất sau khi bạn học Ruby rất tốt.
Kelvin

Câu trả lời:


19

Điều quan trọng cần nhớ ở đây là các phương thức Ruby có thể được định nghĩa (un) tại bất kỳ thời điểm nào, do đó, để giải quyết sự mơ hồ một cách thông minh, mọi phép gán sẽ cần chạy mã để kiểm tra xem có phương thức nào có tên được gán vào lúc đó không. của sự phân công.


18
Điều này là không chính xác, lý do thực sự là chỉ đơn giản là nêu rõ x=5khởi tạo một biến cục bộ xghi đè lên bất kỳ bộ cài đặt hiện có nào self.x=. Để giải quyết sự mơ hồ này, nếu bạn muốn gọi setter, x=bạn cần phải nói rõ ràng những gì bạn muốn làm bằng cách nêu rõself.x=
bbozo

85

Chà, tôi nghĩ lý do trường hợp này xảy ra là vì qwerty = 4không rõ ràng — bạn đang định nghĩa một biến mới được gọi là qwertyhay gọi là setter? Ruby giải quyết sự mơ hồ này bằng cách nói rằng nó sẽ tạo ra một biến mới, do đó, nó self.là bắt buộc.

Đây là một trường hợp khác mà bạn cần self.:

class A
  def test
    4
  end
  def use_variable
    test = 5
    test
  end
  def use_method
    test = 5
    self.test
  end
end
a = A.new
a.use_variable # returns 5
a.use_method   # returns 4

Như bạn có thể thấy, quyền truy cập vào testlà không rõ ràng, vì vậy self.bắt buộc phải có.

Ngoài ra, đây là lý do tại sao ví dụ C # thực sự không phải là một so sánh tốt, bởi vì bạn xác định các biến theo cách không rõ ràng bằng cách sử dụng bộ thiết lập. Nếu bạn đã xác định một biến trong C # có cùng tên với trình truy cập, bạn sẽ cần đủ điều kiện gọi đến trình truy cập this., giống như trường hợp Ruby.


17

Bởi vì nếu không sẽ không thể thiết lập các biến cục bộ bên trong các phương thức. variable = some_valuelà mơ hồ. Ví dụ:

class ExampleClass
  attr_reader :last_set
  def method_missing(name, *args)
    if name.to_s =~ /=$/
      @last_set = args.first
    else
      super
    end
  end

  def some_method
    some_variable = 5 # Set a local variable? Or call method_missing?
    puts some_variable
  end
end

Nếu selfkhông được yêu cầu cho người định cư, some_methodsẽ tăng NameError: undefined local variable or method 'some_variable'. Tuy nhiên, phương pháp này hoạt động như dự định:

example = ExampleClass.new
example.blah = 'Some text'
example.last_set #=> "Some text"
example.some_method # prints "5"
example.last_set #=> "Some text"

8
Haha. Tôi đã định tán thành câu trả lời này, thì nhận ra nó là của tôi từ một năm trước. ;-)
Ajedi32

Tôi nghĩ rằng đây là câu trả lời duy nhất nói rõ lý do tại sao bạn không thể đi mà không có self..
amoebe

1
Tôi nên nói thêm rằng, lý do Java và ilk của nó bỏ đi vì là ngôn ngữ được nhập tĩnh, trình biên dịch có thể xác định trước nếu có xung đột và đưa ra lỗi thích hợp cho phù hợp. Động (ngôn ngữ kịch bản), gõ yếu (php) và vịt gõ (ruby / python) ngôn ngữ không có xa xỉ như vậy, than ôi
Shayne
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.