ทำไมอ็อบเจกต์ Regexp ถือว่าเป็น "เท็จ" ในรูบี?


16

ทับทิมมีความคิดที่เป็นสากลของ " truthiness " และ " falsiness "

ทับทิมไม่ได้มีสองชั้นที่เฉพาะเจาะจงสำหรับวัตถุบูลีนTrueClassและFalseClassมีอินสแตนซ์เดี่ยวแสดงโดยตัวแปรพิเศษtrueและfalseตามลำดับ

อย่างไรก็ตามtruthinessและfalsinessไม่ จำกัด เฉพาะกรณีของทั้งสองเรียนที่เป็นแนวคิดที่เป็นสากลและนำไปใช้กับทุกวัตถุเดียวในรูบี วัตถุทุกคนเป็นอย่างใดอย่างหนึ่งtruthyหรือfalsy กฎนั้นง่ายมาก โดยเฉพาะวัตถุสองอย่างเท่านั้นที่เป็นเท็จ :

  • nilอินสแตนซ์ของNilClassและ
  • falseอินสแตนซ์เดี่ยวของ FalseClass

ทุกวัตถุอื่น ๆ เดียวคือtruthy ซึ่งรวมถึงวัตถุที่ถือว่าเป็นเท็จในภาษาการเขียนโปรแกรมอื่นเช่น

  • ,Integer 0
  • ,Float 0.0
  • ที่ว่างเปล่า,String ''
  • ที่ว่างเปล่า,Array []
  • ที่ว่างเปล่า,Hash {}

กฎเหล่านี้สร้างขึ้นในภาษาและไม่สามารถระบุได้โดยผู้ใช้ ไม่มีto_boolการแปลงโดยนัยหรืออะไรที่คล้ายกัน

นี่คือใบเสนอราคาจากข้อกำหนดภาษา ISO Ruby :

6.6 ค่าบูลีน

วัตถุถูกแบ่งออกเป็นทั้งวัตถุ trueishหรือวัตถุ falseish

เท็จเท่านั้นและไม่มีวัตถุปลอม falseเป็นอินสแตนซ์เดียวของคลาสFalseClass(ดู 15.2.6) ซึ่งfalse-expressionประเมินค่า (ดู 11.5.4.8.3) nilเป็นเพียงตัวอย่างของคลาสNilClass(ดู 15.2.4) ซึ่งnil-expression จะประเมิน (ดู 11.5.4.8.2)

วัตถุอื่นที่ไม่ใช่falseและไม่มีการจัดประเภทเป็นวัตถุที่แท้จริง trueเป็นเพียงอินสแตนซ์เดียวของคลาสTrueClass(ดู 15.2.5) ซึ่งTrue-expression จะประเมิน (ดู 11.5.4.8.3)

Ruby / Spec ที่รันได้ดูเหมือนจะยอมรับ :

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

จากแหล่งข้อมูลทั้งสองนี้ฉันจะสมมติว่าRegexps นั้นเป็นความจริงแต่จากการทดสอบของฉันพวกเขาไม่ได้:

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

ผมทดสอบนี้ในYARV 2.7.0-preview1 , TruffleRuby 19.2.0.1และJRuby 9.2.8.0 การใช้งานทั้งสามเห็นด้วยกันและไม่เห็นด้วยกับข้อกำหนดภาษา ISO Ruby และการตีความ Ruby / Spec ของฉัน

อีกอย่างแม่นยำRegexpวัตถุที่เป็นผลมาจากการประเมินRegexp ตัวอักษรเป็นfalsyในขณะที่Regexpวัตถุที่เป็นผลมาจากการแสดงออกบางส่วนอื่น ๆ ที่มีtruthy :

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

นี่เป็นข้อบกพร่องหรือพฤติกรรมที่ต้องการหรือไม่?


สิ่งที่น่าสนใจRegex.new("a")คือความจริง
mrzasa

!!//เป็นเท็จ แต่!!/r/เป็นความจริง แปลกแน่นอน
สูงสุด

@max !!/r/ผลิตขึ้นfalseสำหรับฉันโดยใช้ (RVM) Ruby 2.4.1
3limin4t0r

ขออภัย @ 3limin4t0r ของฉันไม่ดี คุณพูดถูก ฉันต้องทำบางสิ่งที่โง่จริงๆ ๆ เหมือนทิ้งเครื่องหมายอุทานออกมา
สูงสุด

2
สมมติฐานฉันคิดว่า//ในif // thenถูกตีความว่าเป็นการทดสอบ (ทางลัดสำหรับif //=~nil then) (ที่มักจะเป็นสิ่งที่ผิดเพี้ยนไม่ว่ารูปแบบ) และไม่เป็นอินสแตนซ์ Regexp
Casimir et Hippolyte

คำตอบ:


6

นี่ไม่ใช่ข้อผิดพลาด สิ่งที่เกิดขึ้นคือ Ruby กำลังเขียนรหัสใหม่เพื่อให้

if /foo/
  whatever
end

ได้อย่างมีประสิทธิภาพกลายเป็น

if /foo/ =~ $_
  whatever
end

หากคุณใช้งานรหัสนี้ในสคริปต์ปกติ (และไม่ได้ใช้-eตัวเลือก) คุณควรเห็นคำเตือน:

warning: regex literal in condition

นี่อาจจะค่อนข้างสับสนตลอดเวลาซึ่งเป็นสาเหตุที่ได้รับคำเตือน แต่อาจมีประโยชน์สำหรับหนึ่งบรรทัดโดยใช้-eตัวเลือก ตัวอย่างเช่นคุณสามารถพิมพ์บรรทัดทั้งหมดที่ตรงกับ regexp ที่กำหนดจากไฟล์ด้วย

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

(อาร์กิวเมนต์เริ่มต้นสำหรับprintเป็น$_เช่นกัน)


ดูเพิ่มเติม-n, -p, -aและ-lตัวเลือกเช่นเดียวกับกำมือของวิธีการเคอร์เนลที่มีอยู่เฉพาะเมื่อ-nหรือ-pจะใช้ ( chomp, chop, gsubและsub)
แม

นอกจากนี้ยังมีส่วนที่สองของ parserที่เตือนจะถูกปล่อยออกมา ฉันไม่รู้ว่าเกิดอะไรขึ้นที่นั่น
แม

ฉันเชื่อว่า "ส่วนที่สอง" เป็นส่วนที่ใช้กับคำถามนี้ได้จริง มีประเภทNODE_LIT T_REGEXPหนึ่งที่คุณโพสต์ในคำตอบของคุณเป็นแบบไดนามิกRegexpที่แท้จริงกล่าวคือตัวอักษรที่ใช้การแก้ไขเช่นRegexp /#{''}/
Jörg W Mittag

@ JörgWMittagฉันคิดว่าคุณพูดถูก โผล่ไปรอบ ๆ ในคอมไพเลอร์และไบต์ที่สร้างขึ้นมันดูเหมือนว่าในกรณีของไดนามิก regexp ต้นไม้แยกจะถูกเขียนใหม่เพื่อเพิ่มอย่างชัดเจน$_เป็นโหนดที่คอมไพเลอร์จัดการตามปกติในขณะที่ในกรณีคงที่มันทั้งหมดเกี่ยวข้องกับ ผู้รวบรวม ซึ่งเป็นความอัปยศสำหรับฉันเพราะ“ เฮ้คุณสามารถดูที่ต้นไม้แยกเขียนใหม่ที่นี่” ทำให้คำตอบที่ดี
แม

4

นี่คือผลลัพธ์ของ (เท่าที่ฉันสามารถบอกได้) คุณลักษณะที่ไม่มีเอกสารของภาษาทับทิมซึ่งอธิบายได้ดีที่สุดโดยสเป็คนี้ :

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

โดยทั่วไปคุณอาจคิดว่า$_เป็น "สตริงสุดท้ายที่อ่านโดยgets"

การทำให้เรื่องสับสนมากขึ้น$_(รวมถึง$-) ไม่ใช่ตัวแปรทั่วโลก มันมีขอบเขตในท้องถิ่น


เมื่อสคริปต์ทับทิมเริ่มต้น$_ == nil.

ดังนั้นรหัส:

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

กำลังถูกตีความเช่น:

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

... ผลตอบแทนที่ผิดพลาด

ในทางกลับกันสำหรับregexp ที่ไม่ใช่ตัวอักษร (เช่นr = //หรือRegexp.new('')) การตีความพิเศษนี้ใช้ไม่ได้

//คือความจริง เช่นเดียวกับวัตถุอื่น ๆ ทั้งหมดในทับทิมนอกจากและnilfalse


หากไม่ได้รันสคริปต์ ruby ​​โดยตรงบนบรรทัดคำสั่ง (เช่นมีการ-eตั้งค่าสถานะ) ตัวแยกวิเคราะห์ ruby ​​จะแสดงคำเตือนการใช้งานดังกล่าว:

คำเตือน: สภาพตัวอักษร regex

คุณสามารถใช้ประโยชน์จากพฤติกรรมนี้ในสคริปต์โดยมีลักษณะดังนี้:

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

... แต่มันจะเป็นเรื่องปกติที่จะกำหนดตัวแปรท้องถิ่นให้กับผลลัพธ์getsและทำการตรวจสอบ regex เทียบกับค่านี้อย่างชัดเจน

ฉันไม่ได้ตระหนักถึงกรณีการใช้งานใด ๆ สำหรับการดำเนินการตรวจสอบนี้ด้วยregex ที่ว่างเปล่าโดยเฉพาะอย่างยิ่งเมื่อกำหนดเป็นค่าที่แท้จริง ผลลัพธ์ที่คุณไฮไลต์จะทำให้นักพัฒนาทับทิมส่วนใหญ่ระวัง


ฉันใช้เงื่อนไขเป็นเพียงตัวอย่างเท่านั้น !// #=> trueมีพฤติกรรมเดียวกันและไม่อยู่ในเงื่อนไข ฉันไม่พบบริบทบูลีนใด ๆ (มีเงื่อนไขหรือไม่) ซึ่งทำงานตามที่คาดไว้
Jörg W Mittag

@ JörgWMittagคุณหมายถึงเช่น!// ? true : falseคืนtrueหรือไม่ ฉันคิดว่านี่คือจุดเดียวกันอีกครั้ง - มันถูกตีความเช่น:!(// =~ nil) ? true : false
Tom Lord

หากคุณตั้งค่าด้วยตนเอง$_ = 'hello world'ก่อนที่จะใช้โค้ดข้างต้นแล้วคุณควรจะได้รับผลที่แตกต่างกัน - เพราะแต่ไม่ตรงกับ// =~ 'hello world' nil
ทอมลอร์ด

ไม่มีผมหมายถึง!// โดยไม่มีเงื่อนไขtrueประเมิน ข้อมูลจำเพาะที่คุณอ้างถึงเป็นRegexpตัวอักษรในเงื่อนไข แต่ในตัวอย่างนี้ไม่มีเงื่อนไขดังนั้นข้อกำหนดนี้จึงไม่มีผลบังคับใช้
Jörg W Mittag

2
อ่า .. น่าประหลาดใจมาก ดูเหมือนว่าพฤติกรรมจะเชื่อมโยงกัน: puts !//; $_ = ''; puts !//- ฉันคิดว่าเพราะ parser ขยายมันเหมือนมาโคร ไม่จำเป็นต้องอยู่ในเงื่อนไขหรือไม่
Tom Lord
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.