Recentemente, aprendi a linguagem de programação Ruby e, em suma, é uma boa linguagem. Mas fiquei bastante surpreso ao ver que não era tão simples quanto eu esperava. Mais precisamente, a "regra da menor surpresa" não me pareceu muito respeitada (claro que isso é bastante subjetivo). Por exemplo:
x = true and false
puts x # displays true!
e o famoso:
puts "zero is true!" if 0 # zero is true!
Quais são as outras "pegadinhas" sobre as quais você alertaria um novato em Ruby?
true and false
retorna verdadeiro?Respostas:
Wikipedia Ruby gotchas
Do artigo:
$
e@
não indicam o tipo de dados variável como em Perl, mas funcionam como operadores de resolução de escopo.99.0
) ou uma conversão explícita (99.to_f
). É insuficiente acrescentar um ponto (99.
), porque os números são suscetíveis à sintaxe do método.0
,""
e[]
são todos avaliados paratrue
. Em C, a expressão é0 ? 1 : 0
avaliada como0
(ou seja, falsa). Em Ruby, entretanto, ele cede1
, como todos os números avaliamtrue
; apenasnil
efalse
avalie parafalse
. Um corolário dessa regra é que os métodos Ruby por convenção - por exemplo, pesquisas de expressão regular - retornam números, strings, listas ou outros valores não falsos em caso de sucesso, masnil
em caso de falha (por exemplo, incompatibilidade). Esta convenção também é usada em Smalltalk, onde apenas os objetos especiaistrue
efalse
podem ser usados em uma expressão booleana.char
para caracteres). Isso pode causar surpresas ao fatiar strings:"abc"[0]
yields97
(um inteiro, representando o código ASCII do primeiro caractere na string); para obter"a"
uso"abc"[0,1]
(uma substring de comprimento 1) ou"abc"[0].chr
.A notação
statement until expression
, ao contrário das instruções equivalentes de outras linguagens (por exemplo,do { statement } while (not(expression));
em C / C ++ / ...), na verdade nunca executa a instrução se a expressão já o fortrue
. Isso ocorre porquestatement until expression
é realmente o açúcar sintático sobre, Cujo equivalente em C / C ++ é
while (not(expression)) statement;
tal comostatement if expression
é um equivalente aoNo entanto, a notação
em Ruby irá de fato executar a instrução uma vez, mesmo se a expressão já for verdadeira.
Greeting << " world!" if Greeting == "Hello"
não gera um erro ou aviso. Isso é semelhante afinal
variáveis em Java, mas Ruby também tem a funcionalidade de "congelar" um objeto, ao contrário do Java.Alguns recursos que diferem notavelmente de outros idiomas:
Os operadores usuais para expressões condicionais
and
eor
não seguem as regras normais de precedência:and
não se vincula mais apertado queor
. Ruby também possui operadores de expressão||
e&&
que funcionam conforme o esperado.def
por dentrodef
não faz o que um programador Python poderia esperar:Isso dá um erro sobre
x
não ter sido definido. Você precisa usar umProc
.Características da linguagem
()
, para evitar o significado ambíguo do código. Não usar()
ainda é uma prática comum e pode ser especialmente interessante usar Ruby como uma linguagem de programação específica de domínio legível por humanos, junto com o método chamadomethod_missing()
.fonte
Os novatos terão problemas com métodos de igualdade :
Esses exemplos devem esclarecer os primeiros 3 métodos:
Observe que == , eql? e igual? deve ser sempre simétrico: se a == b então b == a.
Observe também que == e eql? são ambos implementados na classe Object como aliases para iguais? , então se você criar uma nova classe e quiser == e eql? para significar algo diferente de identidade simples, então você precisa substituir os dois. Por exemplo:
O método === se comporta de maneira diferente. Em primeiro lugar, não é simétrico (a === b não implica que b === a). Como eu disse, você pode ler a === b como "a corresponde a b". Aqui estão alguns exemplos:
A declaração de caso é baseada no método === :
é equivalente a isto:
Se você definir uma nova classe cujas instâncias representam algum tipo de contêiner ou intervalo (se tiver algo como um método include? Ou match? ), Pode ser útil substituir o método === desta forma:
fonte
Remendo do macaco . Ruby tem classes abertas, então seu comportamento pode ser alterado dinamicamente em tempo de execução ...
Os objetos podem responder a métodos indefinidos se
method_missing
ousend
tiverem sido substituídos. Isso explora a invocação do método baseado em mensagem do Ruby. O sistema ActiveRecord da Rails usa isso com grande efeito.fonte
O código a seguir me surpreendeu. Acho que é um problema perigoso: fácil de encontrar e difícil de depurar.
Isso imprime:
Mas se eu apenas adicionar
comment =
algo antes do bloco ...Então eu recebo:
Basicamente, quando uma variável é definida apenas dentro de um bloco, ela é destruída no final do bloco e, em seguida, é redefinida a
nil
cada iteração. Normalmente é isso que você espera. Mas se a variável é definida antes do bloco, então a variável externa é usada dentro do bloco e seu valor é, portanto, persistente entre as iterações.Uma solução seria escrever isso:
Acho que muitas pessoas (inclusive eu) tendem a escrever "
a = b if c
" em vez de "a = (c ? b : nil)
", porque é mais legível, mas obviamente tem efeitos colaterais.fonte
a = (b if c)
para obter o efeito desejado, sem o ternário. Isso ocorre porque éb if c
avaliado como nulo sec
for falsey.Ao chamar
super
sem argumentos, o método substituído é realmente chamado com os mesmos argumentos do método de substituição.Para realmente chamar
super
sem argumentos, você precisa dizersuper()
.fonte
B#hello
tivername = 42
antes dosuper
, ele dirá "olá 42".Os blocos e métodos retornam o valor da última linha por padrão. Adicionar
puts
instruções ao final para fins de depuração pode causar efeitos colaterais desagradáveisfonte
A herança não desempenha nenhum papel na determinação da visibilidade do método em Ruby.
fonte
Tive muitos problemas para entender variáveis de classe, atributos de classe e métodos de classe. Este código pode ajudar um novato:
fonte
uma coisa que aprendi foi usar o operador || = com cuidado. e tome cuidado especial se estiver lidando com booleanos. Eu geralmente usei a || = b como catch all para dar a 'a' um valor padrão se tudo mais falhasse e 'a' permanecesse nulo. mas se a for falso eb for verdadeiro, então a será atribuído como verdadeiro.
fonte
a = b if a.nil?
ou@a = b unless defined?(@a)
.Os blocos são muito importantes para entender, eles são usados em qualquer lugar.
Você não precisa de parênteses em torno dos parâmetros do método. Se você os usa ou não, depende de você. Alguns dizem que você deve sempre usá-los .
Use levantar e resgatar para lidar com exceções, não lançar e pegar.
Você pode usar,
;
mas não precisa, a menos que queira colocar várias coisas em uma linha.fonte
Tive problemas com mixins que contêm métodos de instância e métodos de classe. Este código pode ajudar um novato:
No início, pensei que poderia ter módulos com métodos de instância e métodos de classe simplesmente fazendo isto:
Infelizmente, o método number_of_displays nunca será incluído ou estendido porque é um "método de classe de módulo". Apenas "métodos de instância de módulo" podem ser incluídos em uma classe (como métodos de instância) ou estendidos em uma classe (como métodos de classe). É por isso que você precisa colocar os métodos de instância do seu mixin em um módulo, e os métodos de classe do seu mixin em outro módulo (normalmente você coloca os métodos de classe em um submódulo "ClassMethods"). Graças ao método mágico incluído , você pode facilitar a inclusão de métodos de instância e métodos de classe em apenas uma chamada simples "include Displayable" (como mostrado no exemplo acima).
Este mixin contará cada display por classe . O contador é um atributo de classe, então cada classe terá o seu próprio (seu programa provavelmente falhará se você derivar uma nova classe da classe Person, pois o contador @number_of_displays para a classe derivada nunca será inicializado). Você pode querer substituir @number_of_displays por @@ number_of_displays para torná-lo um contador global. Nesse caso, cada hierarquia de classes terá seu próprio contador. Se você deseja um contador global e exclusivo, provavelmente deve torná-lo um atributo de módulo.
Tudo isso definitivamente não era intuitivo para mim quando comecei com Ruby.
Ainda não consigo descobrir como tornar limpo alguns desses métodos mixin privados ou protegidos (apenas o método display e number_of_displays devem ser incluídos como métodos públicos).
fonte
Preste atenção à notação de alcance.
(Pelo menos, preste mais atenção do que eu inicialmente!)
Há uma diferença entre
0..10
(dois pontos) e0...10
(três pontos).Eu gosto muito de Ruby. Mas essa coisa de ponto-ponto versus ponto-ponto-ponto me incomoda. Acho que um "recurso" de sintaxe dupla tão sutil que é:
não deve ser capaz de causar bugs devastadores em meus programas.
fonte
for (i=0; i<max; i++)
efor (i=0; i<=max; i++)
Eu acho que "
and
" e "or
" são referências a Perl, que é um dos "pais" mais óbvios de Ruby (o outro mais proeminente é Smalltalk). Ambos têm precedência muito mais baixa (inferior à atribuição, na verdade, que é de onde vem o comportamento observado) do que&&
e||
quais são os operadores que você deve usar.Outras coisas a ter em conta que não são imediatamente óbvias:
Você realmente não chama métodos / funções, embora pareça que sim. Em vez disso, como em Smalltalk, você envia uma mensagem a um objeto. Então
method_missing
é realmente mais parecidomessage_not_understood
.é equivalente a
Os símbolos são amplamente usados. Essas são aquelas coisas que começam
:
e não são imediatamente óbvias (bem, não eram para mim), mas quanto mais cedo você as dominar, melhor.Ruby é grande em "digitação de pato", seguindo o princípio de que "se ele anda como um pato e grasna como um pato ..." que permite a substituição informal de objetos com um subconjunto comum de métodos sem qualquer herança explícita ou relacionamento mixin.
fonte
Se você declarar um setter (também conhecido como modificador) usando
attr_writer
ouattr_accessor
(oudef foo=
), tome cuidado ao chamá-lo de dentro da classe. Como as variáveis são declaradas implicitamente, o interpretador sempre precisa resolverfoo = bar
declarando uma nova variável chamada foo, em vez de chamar o métodoself.foo=(bar)
.Isso também se aplica a objetos Rails ActiveRecord, que obtêm acessores definidos com base em campos no banco de dados. Visto que eles nem mesmo são variáveis de instância no estilo @, a maneira adequada de definir esses valores individualmente é com
self.value = 123
ouself['value'] = 123
.fonte
Compreender a diferença entre as classes Time e Date. Ambos são diferentes e criaram problemas ao usá-los em trilhos. A classe Time às vezes entra em conflito com outras bibliotecas de classe Time presentes na biblioteca ruby / rails padrão. Pessoalmente, levei muito tempo para entender o que exatamente estava acontecendo no meu aplicativo Rails. Mais tarde, descobri quando fiz
Time.new
Referia-se a alguma biblioteca em um local que eu nem sabia.
Desculpe se não estou claro com o que quero dizer exatamente. Se outras pessoas enfrentaram problemas semelhantes, explique novamente.
fonte
Uma coisa que me surpreendeu no passado é que a
\n
sequência de escape caractere de nova linha ( ) - entre outras - não é suportada por strings entre aspas simples. A própria barra invertida é escapada. Você deve usar aspas duplas para que o escape funcione conforme o esperado.fonte
0 e '' são verdadeiros, como você apontou.
Você pode ter um método e um módulo / classe com o mesmo nome (o que faz sentido, porque o método realmente é adicionado ao Object e, portanto, tem seu próprio namespace).
Não há herança múltipla, mas freqüentemente "módulos mixin" são usados para adicionar métodos comuns a várias classes.
fonte
false
enil
são os falsos. Todos os outros são valores verdadeiros.Os métodos podem ser redefinidos e podem se tornar um fator de preocupação até que você descubra a causa. ( Admitidamente, este erro é provavelmente um pouco "mais difícil" de detectar quando a ação de um controlador Ruby on Rails é redefinida por engano! )
Corre:
Mas chame-o com os avisos habilitados e você verá o motivo:
fonte
Eu acho que é sempre bom usar .length nas coisas ... uma vez que o tamanho é suportado por quase tudo e Ruby tem tipos dinâmicos, você pode obter resultados realmente estranhos chamando .size quando você tem o tipo errado ... Eu prefiro muito mais obter a NoMethodError: método indefinido `length ', então geralmente nunca chamo size em objetos em Ruby.
me mordeu mais de uma vez.
Lembre-se também de que os objetos têm ids, então tento não usar variáveis call id ou object_id apenas para evitar confusão. Se eu precisar de um id em um objeto Users, é melhor chamá-lo de algo como user_id.
Apenas meus dois centavos
fonte
Eu sou novo no Ruby, e na minha primeira rodada eu encontrei um problema relacionado a alterar floats / strings para um inteiro. Comecei com os flutuadores e codifiquei tudo como f.to_int . Mas quando continuei e usei o mesmo método para strings, fiquei confuso quando comecei a executar o programa.
Aparentemente, uma string não tem um método to_int , mas floats e ints têm.
Parênteses arbitrários também me surpreenderam no início. Eu vi alguns códigos com e outros sem. Levei um tempo para perceber que os dois estilos são aceitos.
fonte
Relacionado à resposta de monkut, os
to_foo
métodos de Ruby dão uma ideia do quão estrita uma conversão eles farão.Os curtos como
to_i
,to_s
diga a ele para ser preguiçoso e converta-os para o tipo de destino, mesmo que não possam ser representados com precisão nesse formato. Por exemplo:As funções mais explícitas como
to_int
,to_s
significam que o objeto pode ser nativamente representado como esse tipo de dados. Por exemplo, aRational
classe representa todos os números racionais, portanto, pode ser representada diretamente como um inteiro Fixnum (ou Bignum) por chamadato_int
.Se você não pode chamar o método mais longo, significa que o objeto não pode ser nativamente representado naquele tipo.
Então, basicamente, ao converter, se você for preguiçoso com os nomes dos métodos, Ruby será preguiçoso com a conversão.
fonte
Do In Ruby, por que não
foo = true unless defined?(foo)
fará a atribuição?Porque
foo
é definido comonil
quandodefined?(foo)
é chamado.fonte
Não é garantido que a iteração sobre hashes ruby aconteça em uma ordem específica. (Isto não é um erro, é um recurso)
Hash#sort
é útil se você precisar de um pedido específico.Pergunta relacionada: Por que o array de 1000 hashes de pares de chave e valor de Ruby está sempre em uma ordem específica?
fonte
Este me deixou louco uma vez:
fonte
1/2
avalia para0
, que não é igual0.5
, independente do tipo. No entantoRational(1, 2) == 0.5
, e1.0 == 1
.não funciona. Você tem que colocar o intervalo entre parênteses, como
então ele não acha que você está ligando
5.each
. Acho que é uma questão de precedência, assim como ax = true and false
pegadinha.fonte