Ruby tem uma ideia universal de " veracidade " e " falsidade ".
Rubi faz ter duas classes específicas de objetos booleano, TrueClass
e FalseClass
, com as instâncias únicas indicadas pelas variáveis especiais true
e false
, respectivamente.
No entanto, truthiness e falsiness não estão limitados a instâncias dessas duas classes, o conceito é universal e se aplica a cada objeto em Ruby. Cada objeto é ou truthy ou Falsas . As regras são muito simples. Em particular, apenas dois objetos são falsos :
nil
, a instância singleton deNilClass
efalse
, a instância singleton deFalseClass
Todo outro objeto é verdadeiro . Isso inclui até objetos que são considerados falsos em outras linguagens de programação, como
Essas regras são incorporadas ao idioma e não podem ser definidas pelo usuário. Não há to_bool
conversão implícita ou algo semelhante.
Aqui está uma citação da ISO Ruby Language Specification :
6.6 Valores booleanos
Um objeto é classificado em um objeto trueish ou falso .
Somente false e nil são objetos falsos. false é a única instância da classe
FalseClass
(consulte 15.2.6), para a qual uma expressão falsa avalia (consulte 11.5.4.8.3). nil é a única instância da classeNilClass
(consulte 15.2.4), para a qual uma expressão nula é avaliada (consulte 11.5.4.8.2).Objetos diferentes de false e nil são classificados em objetos trueish. true é a única instância da classe
TrueClass
(consulte 15.2.5), para a qual uma expressão true é avaliada (consulte 11.5.4.8.3).
O executável Ruby / Spec parece concordar :
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
De acordo com essas duas fontes, eu assumiria que Regexp
s também são verdadeiros , mas de acordo com meus testes, eles não são:
if // then 'Regexps are truthy' else 'Regexps are falsy' end
#=> 'Regexps are falsy'
Eu testei isso no YARV 2.7.0-preview1 , no TruffleRuby 19.2.0.1 e no JRuby 9.2.8.0 . Todas as três implementações concordam entre si e discordam da ISO Ruby Language Specification e da minha interpretação do Ruby / Spec.
Mais precisamente, os Regexp
objetos resultantes da avaliação de Regexp
literais são falsos , enquanto os Regexp
objetos resultantes de alguma outra expressão são verdadeiros :
r = //
if r then 'Regexps are truthy' else 'Regexps are falsy' end
#=> 'Regexps are truthy'
Isso é um bug ou comportamento desejado?
Regex.new("a")
é verdade.!!//
é falso, mas!!/r/
é verdadeiro. Estranho mesmo.!!/r/
produzfalse
para mim usando (RVM) Ruby 2.4.1.//
emif // then
é interpretado como um teste (um atalho paraif //=~nil then
) (que é sempre Falsas qualquer que seja o padrão) e não como uma instância Regexp.Respostas:
Isso não é um bug. O que está acontecendo é que Ruby está reescrevendo o código para que
efetivamente se torna
Se você estiver executando esse código em um script normal (e não
-e
estiver usando a opção), deverá receber um aviso:Isso provavelmente é um pouco confuso na maioria das vezes, e é por isso que o aviso é dado, mas pode ser útil para uma linha usando a
-e
opção Por exemplo, você pode imprimir todas as linhas correspondentes a uma determinada regexp de um arquivo com(O argumento padrão para também
print
é$_
.)fonte
-n
,-p
,-a
e-l
opções, bem como o punhado de métodos de Kernel que estão disponíveis apenas quando-n
ou-p
são utilizadas (chomp
,chop
,gsub
esub
).NODE_LIT
com o tipoT_REGEXP
. O que você postou na sua resposta é para um literal dinâmicoRegexp
, ou seja, umRegexp
literal que usa interpolação, por exemplo/#{''}/
.$_
como um nó que o compilador lida normalmente, enquanto no caso estático é tratado pelo compilador. O que é uma pena para mim, porque "ei, você pode ver onde a árvore de análise é reescrita aqui" contribui para uma boa resposta.Este é o resultado de (até onde eu sei) um recurso não documentado da linguagem ruby, que é melhor explicado por esta especificação :
Geralmente, você pode considerar
$_
a "última string lida porgets
"Para tornar as coisas ainda mais confusas,
$_
(junto com$-
) não é uma variável global; tem escopo local .Quando um script ruby é iniciado
$_ == nil
,.Então, o código:
Está sendo interpretado como:
... O que retorna falsey.
Por outro lado, para um regexp não literal (por exemplo,
r = //
ouRegexp.new('')
), essa interpretação especial não se aplica.//
é verdade; assim como todos os outros objetos em ruby além denil
efalse
.A menos que seja executado um script ruby diretamente na linha de comando (ou seja, com o
-e
sinalizador), o analisador ruby exibirá um aviso contra esse uso:Você pode fazer uso desse comportamento em um script, com algo como:
... Mas seria mais normal atribuir uma variável local ao resultado
gets
e executar a verificação de expressão regular contra esse valor explicitamente.Não conheço nenhum caso de uso para executar essa verificação com um regex vazio , especialmente quando definido como um valor literal. O resultado que você destacou realmente pegaria a maioria dos desenvolvedores de rubi desprevenidos.
fonte
!// #=> true
tem o mesmo comportamento e não é condicional. Não consegui encontrar nenhum contexto booleano (condicional ou não), onde ele se comporta conforme o esperado.!// ? true : false
retornostrue
? Acho que este é o mesmo ponto novamente - ele está sendo interpretado como:!(// =~ nil) ? true : false
$_ = 'hello world'
antes de executar o código acima, deverá obter um resultado diferente - porque// =~ 'hello world'
, mas não correspondenil
.!//
sem a avaliação condicional detrue
. A especificação que você citou é sobre umRegexp
literal em uma condicional, mas neste exemplo, não há condicional, portanto, essa especificação não se aplica.puts !//; $_ = ''; puts !//
- Suponho que o analisador o expanda como uma macro; não precisa necessariamente estar dentro de um condicional?