Tại sao một đối tượng Regapi lại được coi là FalsyF trong Ruby?


16

Ruby có một ý tưởng phổ quát của " truthiness " và " falsiness ".

Ruby không có hai lớp học cụ thể cho các đối tượng Boolean, TrueClassFalseClass, với trường hợp singleton biểu hiện bằng các biến đặc biệt truefalsetương ứng.

Tuy nhiên, truthinessfalsiness không giới hạn trường hợp của hai lớp học, khái niệm này là phổ biến và áp dụng cho mọi đối tượng duy nhất trong Ruby. Mỗi đối tượng là một trong hai truthy hoặc falsy . Các quy tắc rất đơn giản. Cụ thể, chỉ có hai đối tượng là giả mạo :

Mỗi đối tượng khác là sự thật . Điều này bao gồm đối tượng thậm chí được coi là falsy bằng các ngôn ngữ lập trình khác, chẳng hạn như

Các quy tắc này được tích hợp vào ngôn ngữ và không thể xác định được. Không có to_boolchuyển đổi ngầm hoặc bất cứ điều gì tương tự.

Dưới đây là trích dẫn từ Đặc tả ngôn ngữ ISO của Ruby :

6.6 Giá trị Boolean

Một đối tượng được phân loại thành một đối tượng thật hoặc một đối tượng giả .

Chỉ có saikhông là đối tượng giả. false là trường hợp duy nhất của lớp FalseClass(xem 15.2.6), theo đó một biểu thức sai đánh giá (xem 11.5.4.8.3). nil là ví dụ duy nhất của lớp NilClass(xem 15.2.4), theo đó một biểu thức nil đánh giá (xem 11.5.4.8.2).

Các đối tượng khác với falsenil được phân loại thành các đối tượng thật. true là ví dụ duy nhất của lớp TrueClass(xem 15.2.5), theo đó một biểu thức đúng đánh giá (xem 11.5.4.8.3).

Ruby / Spec thực thi dường như đồng ý :

it "considers a non-nil and non-boolean object in expression result as true" do
  if mock('x')
    123
  else
    456
  end.should == 123
end

Theo hai nguồn đó, tôi cho rằng đó Regexpcũng là sự thật , nhưng theo thử nghiệm của tôi, họ không:

if // then 'Regexps are truthy' else 'Regexps are falsy' end
#=> 'Regexps are falsy'

Tôi đã thử nghiệm điều này trên YARV 2.7.0-preview1 , TruffleRuby 19.2.0.1JRuby 9.2.8.0 . Tất cả ba triển khai đều đồng ý với nhau và không đồng ý với Đặc tả ngôn ngữ ISO của Ruby và cách giải thích của tôi về Ruby / Spec.

Chính xác hơn, Regexpđối tượng mà là kết quả của việc đánh giá Regexp literalsfalsy , trong khi Regexpđối tượng mà là kết quả của một số biểu hiện khác là truthy :

r = //
if r then 'Regexps are truthy' else 'Regexps are falsy' end
#=> 'Regexps are truthy'

Đây có phải là một lỗi, hoặc hành vi mong muốn?


Điều thú vị là đó Regex.new("a")là sự thật.
mrzasa

!!//là sai nhưng !!/r/là đúng Thực sự kỳ lạ.
tối đa

@max !!/r/sản xuất falsecho tôi bằng cách sử dụng (RVM) Ruby 2.4.1.
3limin4t0r

Xin lỗi tôi xấu @ 3limin4t0r. Bạn đúng rồi. Tôi đã phải làm một cái gì đó thực sự ngu ngốc như để lại một dấu chấm than.
tối đa

2
Một giả thuyết, tôi nghĩ rằng //trong đó if // thenđược hiểu là một bài kiểm tra (một lối tắt cho if //=~nil then) (đó luôn là giả mạo bất kể mẫu nào) và không phải là một ví dụ Regapi.
Casimir et Hippolyte

Câu trả lời:


6

Đây không phải là một lỗi. Điều đang xảy ra là Ruby đang viết lại mã để

if /foo/
  whatever
end

thực sự trở thành

if /foo/ =~ $_
  whatever
end

Nếu bạn đang chạy mã này trong một tập lệnh bình thường (và không sử dụng -etùy chọn) thì bạn sẽ thấy cảnh báo:

warning: regex literal in condition

Điều này có lẽ hơi khó hiểu trong hầu hết thời gian, đó là lý do tại sao cảnh báo được đưa ra, nhưng có thể hữu ích cho một dòng sử dụng -etùy chọn. Ví dụ: bạn có thể in tất cả các dòng khớp với biểu thức chính quy từ một tệp với

$ ruby -ne 'print if /foo/' filename

(Đối số mặc định print$_tốt.)


Xem thêm -n, -p, -a-ltùy chọn, cũng như số ít các phương pháp hạt nhân mà chỉ có sẵn khi -nhay -pđược sử dụng ( chomp, chop, gsubsub).
matt

Ngoài ra còn có một phần thứ hai của trình phân tích cú pháp nơi cảnh báo đó được phát ra. Tôi không biết những gì đang xảy ra ở đó mặc dù.
matt

Tôi tin rằng "phần thứ hai" là phần thực sự áp dụng cho câu hỏi này. NODE_LITvới loại T_REGEXP. Một trong những bạn đã đăng trong câu trả lời của bạn là cho một nghĩa đen độngRegexp , tức là một Regexpnghĩa đen sử dụng nội suy, ví dụ /#{''}/.
Jörg W Mittag

@ JörgWMittag Tôi nghĩ bạn đúng. Chọc vào trình biên dịch và mã byte được tạo, có vẻ như trong trường hợp regrec động, cây phân tích được viết lại để thêm $_vào một cách rõ ràng như một nút mà trình biên dịch xử lý như bình thường, trong trường hợp tĩnh, tất cả được xử lý bởi trình biên dịch. Đó là một sự xấu hổ đối với tôi bởi vì hey hey, bạn có thể thấy nơi mà cây phân tích được viết lại ở đây, đó là một câu trả lời hay.
matt

4

Đây là kết quả của (theo như tôi có thể nói) một tính năng không có giấy tờ của ngôn ngữ ruby, được giải thích tốt nhất bởi thông số này :

it "matches against $_ (last input) in a conditional if no explicit matchee provided" do
  -> {
    eval <<-EOR
    $_ = nil
    (true if /foo/).should_not == true
    $_ = "foo"
    (true if /foo/).should == true
    EOR
  }.should complain(/regex literal in condition/)
end

Bạn thường có thể nghĩ $_là "chuỗi cuối cùng được đọc bởi gets"

Để làm cho vấn đề thậm chí khó hiểu hơn, $_(cùng với $-) không phải là một biến toàn cầu; nó có phạm vi địa phương .


Khi một kịch bản ruby ​​bắt đầu , $_ == nil.

Vì vậy, mã:

// ? 'Regexps are truthy' : 'Regexps are falsey'

Đang được giải thích như:

(// =~ nil) ? 'Regexps are truthy' : 'Regexps are falsey'

... Trả về falsey.

Mặt khác, đối với một biểu thức chính quy không theo nghĩa đen (ví dụ r = //hoặc Regexp.new('')), cách giải thích đặc biệt này không được áp dụng.

//là sự thật; giống như tất cả các đối tượng khác trong ruby ​​bên cạnh nilfalse.


Trừ khi chạy tập lệnh ruby ​​trực tiếp trên dòng lệnh (nghĩa là với -ecờ), trình phân tích cú pháp ruby ​​sẽ hiển thị cảnh báo chống lại việc sử dụng đó:

cảnh báo: regex nghĩa đen trong điều kiện

Bạn có thể sử dụng hành vi này trong một tập lệnh, với nội dung như:

puts "Do you want to play again?"
gets
# (user enters e.g. 'Yes' or 'No')
/y/i ? play_again : back_to_menu

... Nhưng sẽ bình thường hơn khi gán một biến cục bộ cho kết quả getsvà thực hiện kiểm tra regex với giá trị này một cách rõ ràng.

Tôi không biết về bất kỳ trường hợp sử dụng nào để thực hiện kiểm tra này với biểu thức chính quy trống , đặc biệt khi được xác định là giá trị bằng chữ. Kết quả mà bạn nêu bật thực sự sẽ khiến hầu hết các nhà phát triển ruby ​​mất cảnh giác.


Tôi chỉ sử dụng điều kiện làm ví dụ. !// #=> truecó hành vi tương tự và không có điều kiện. Tôi không thể tìm thấy bất kỳ bối cảnh boolean nào (có điều kiện hay không), nơi nó hoạt động như mong đợi.
Jörg W Mittag

@ JörgWMittag Ý bạn là ví dụ như !// ? true : falsetrả về true? Tôi nghĩ rằng đây lại là cùng một điểm - nó được giải thích như sau:!(// =~ nil) ? true : false
Tom Lord

Nếu bạn đặt thủ công $_ = 'hello world'trước khi chạy mã ở trên, thì bạn sẽ nhận được một kết quả khác - bởi vì // =~ 'hello world', nhưng không khớp nil.
Tom Lord

Không, ý tôi là !// không có các điều kiện đánh giá true. Thông số kỹ thuật bạn đã trích dẫn là về một Regexpnghĩa đen trong một điều kiện, nhưng trong ví dụ này, không có điều kiện, vì vậy thông số kỹ thuật này không áp dụng.
Jörg W Mittag

2
À .. Vâng, rất đáng ngạc nhiên. Hành vi dường như được liên kết, mặc dù: puts !//; $_ = ''; puts !//- Tôi cho rằng vì trình phân tích cú pháp mở rộng nó như một macro; nó không nhất thiết phải ở trong một điều kiện?
Tom Lord
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.