Neden bir Regexp nesnesi Ruby'de “falsili” kabul edilir?


16

Ruby'nin evrensel bir " doğruluk " ve " yanlışlık " fikri vardır .

Yakut yapar Boole nesneler için iki özel sınıfı bulunmakta, TrueClassve FalseClassözel değişken ile gösterilen tekil örnekleri ile trueve falsesırasıyla.

Ancak, doğruluk ve sahtelik bu iki sınıfın örnekleriyle sınırlı değildir, kavram evrenseldir ve Ruby'deki her bir nesne için geçerlidir. Her nesne ya doğrudur ya da sahtedir . Kurallar çok basit. Özellikle, sadece iki nesne yanlıştır :

Her bir diğer nesne olduğunu truthy . Bu kabul edilir hatta nesneleri içeren falsy gibi diğer programlama dillerinde,

Bu kurallar dile dahil edilmiştir ve kullanıcı tarafından tanımlanamaz. to_boolÖrtük dönüşüm veya benzeri bir şey yoktur .

İşte ISO Ruby Dil Şartnamesi'nden bir alıntı :

6.6 Boole değerleri

Bir amacı, bir ya da sınıflandırılır trueish nesne ya da bir falseish nesne .

Yalnızca yanlış ve sıfır , sahte nesnelerdir. Yanlış sınıf yalnızca örneği FalseClassbir (15.2.6 bakınız), buna ters sentezleme değerlendirir (11.5.4.8.3 bakınız). nil , NilClassbir nil-ifadesinin değerlendirdiği (bkz. 11.5.4.8.2) sınıfın tek örneğidir (bkz. 15.2.4).

Dışındaki nesneleri FALSE ve sıfır trueish nesneler sınıflandırılır. Gerçek sınıfın tek örneği TrueClass, bir (15.2.5 bakınız), buna doğru ifade değerlendirir (11.5.4.8.3 bakınız).

Yürütülebilir Ruby / Spec aynı fikirde :

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

Bu iki kaynağa göre, Regexps'nin de doğru olduğunu varsayabilirim , ancak testlerime göre, bunlar değil:

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

Bunu YARV 2.7.0-önizleme1 , TruffleRuby 19.2.0.1 ve JRuby 9.2.8.0'da test ettim . Her üç uygulama da birbiriyle hemfikir ve ISO Ruby Dil Şartnamesi ile Ruby / Spec hakkındaki yorumumla aynı fikirde değil.

Daha doğrusu, değişmez değerleri Regexpdeğerlendirmenin sonucu olan nesneler sahtedir , oysa başka bir ifadenin sonucu olan nesneler doğrudur :Regexp Regexp

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

Bu bir hata mı yoksa istenen davranış mı?


İlginç olan bu Regex.new("a")doğru.
mrzasa

!!//yanlıştır ama !!/r/doğrudur. Gerçekten garip.
max

@max benim için (RVM) Ruby 2.4.1 kullanarak !!/r/üretiyor false.
3limin4t0r

Üzgünüm benim kötü @ 3limin4t0r. Haklısın. Bir ünlem işareti bırakmak gibi gerçekten aptalca bir şey yapmış olmalıydım.
max

2
Bir hipotez, ben düşünüyorum //içinde if // thenbir test (için kısayol olarak yorumlanır if //=~nil then) bir Normal ifade örneği olarak değil, (yani falsy ne desen her zaman).
Casimir et Hippolyte

Yanıtlar:


6

Bu bir hata değil. Olan şu ki Ruby kodu yeniden yazıyor, böylece

if /foo/
  whatever
end

etkili bir şekilde olur

if /foo/ =~ $_
  whatever
end

Bu kodu normal bir komut dosyasında çalıştırıyorsanız (ve -eseçeneği kullanmıyorsanız ) bir uyarı görmelisiniz:

warning: regex literal in condition

Bu muhtemelen çoğu zaman biraz kafa karıştırıcıdır, bu yüzden uyarı verilir, ancak -eseçeneği kullanan bir satır için yararlı olabilir . Örneğin, belirli bir normal ifadeyle eşleşen tüm satırları

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

(Varsayılan argüman printolduğu $_sıra.)


Ayrıca bkz -n, -p, -ave -lseçenekler yanı sıra sadece mevcut olduğunda Çekirdek yöntemlerin avuç -nya -pkullanılmaktadır ( chomp, chop, gsubve sub).
matt

Orada da ayrıştırıcı bir ikinci bölümü yani uyarı duyulur. Orada neler olduğunu bilmiyorum.
matt

Bu soru için geçerli olan "ikinci bölüm" olduğuna inanıyorum. NODE_LITyazın T_REGEXP. Cevabınızda yayınladığınız dinamik bir Regexphazır bilgi , yani Regexpenterpolasyon kullanan bir hazır bilgi içindir /#{''}/.
Jörg W Mittag

@ JörgWMittag Doğru olduğunu düşünüyorum. Derleyicide ve üretilen bayt kodunda dolaşmak, dinamik regexp durumunda, ayrıştırma ağacının $_derleyicinin normal olarak işlediği bir düğüm olarak açıkça eklenmesi için yeniden yazılırken , statik durumda derleyici. Bu benim için bir utanç çünkü “hey, ayrıştırma ağacının nerede yeniden yazıldığını görebilirsiniz” güzel bir cevap veriyor.
matt

4

Bu (en iyi şekilde söyleyebildiğim kadarıyla) yakut dilinin belgelenmemiş bir özelliğinin sonucudur, bu en iyi şekilde bu spesifikasyonla açıklanır :

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

Genellikle aklınıza gelebilecek $_"Son dize tarafından okundu olarak gets"

Konuları daha da karmaşık hale getirmek, $_(bununla birlikte $-) küresel bir değişken değildir ; yerel kapsamı vardır .


Bir yakut senaryosu başladığında $_ == nil,.

Yani, kod:

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

Şu şekilde yorumlanıyor:

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

... Falsey'i döndürür.

Öte yandan, değişmez bir regexp (örneğin r = //veya Regexp.new('')) için, bu özel yorum geçerli değildir.

//doğrudur; tıpkı yakuttaki diğer tüm nesneler gibi nilve false.


Ruby komut dosyasını doğrudan komut satırında ( -ebayrakla) çalıştırmadığı sürece , ruby ​​ayrıştırıcısı bu kullanıma karşı bir uyarı görüntüler:

uyarı: regex literal in condition

Sen olabilir gibi bir şey ile, bir komut dosyası bu davranışın faydalanmak:

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

... Ancak sonuca yerel bir değişken atamak getsve bu değere karşı normal ifade kontrolünü açıkça yapmak daha normal olurdu .

Özellikle değişmez değer olarak tanımlandığında, bu denetimi boş bir normal ifade ile gerçekleştirmek için herhangi bir kullanım durumunun farkında değilim . Vurguladığınız sonuç aslında yakut geliştiricilerin çoğunu hazırlıksız yakalar.


Koşullu olarak sadece örnek olarak kullandım. !// #=> trueaynı davranışa sahiptir ve şartlı değildir. Beklendiği gibi davrandığı herhangi bir boolean bağlam (koşullu ya da değil) bulamadım.
Jörg W Mittag

@ JörgWMittag Şunu mu demek istediniz: !// ? true : falsereturn true? Sanırım bu yine aynı nokta - şu şekilde yorumlanıyor:!(// =~ nil) ? true : false
Tom Lord

$_ = 'hello world'Yukarıdaki kodu çalıştırmadan önce manuel olarak ayarlarsanız , farklı bir sonuç almalısınız - çünkü // =~ 'hello world'eşleşmez nil.
Tom Lord

Hayır, yani !// şartlı değerlendirme yapmadantrue . Belirttiğiniz özellik Regexpbir koşullu bir değişmezle ilgilidir, ancak bu örnekte koşullu yoktur, bu nedenle bu özellik geçerli değildir.
Jörg W Mittag

2
Ah .. Evet, çok şaşırtıcı. Davranış bağlantılı olsa da: puts !//; $_ = ''; puts !//- Sanırım ayrıştırıcı bir makro gibi genişletir; şartlı olması gerekmiyor mu?
Tom Lord
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.