Quais são os Ruby Gotchas que um novato deve ser avisado? [fechadas]

108

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?

MiniQuark
fonte
@ fraseses.insert (0, p) OK @ fraseses.insert (p) NADA acontece @ frases << p # OK
Anno2001
por que true and falseretorna verdadeiro?
Jürgen Paul
3
Porque "x = verdadeiro e falso" é realmente interpretado como "(x = verdadeiro) e falso". É uma questão de precedência do operador: "e" tem uma prioridade inferior a "=". A maioria das outras linguagens tem a prioridade reversa, não sei porque escolheram esta ordem no Rails, acho muito confuso. Se você deseja o comportamento "normal", simplesmente digite "x = (verdadeiro e falso)", então x será falso.
MiniQuark
4
Outra solução é usar "&&" e "||" em vez de "e" e "ou": eles se comportam conforme o esperado. Por exemplo: "x = true && false" resulta em x sendo falso.
MiniQuark
“O princípio da menor surpresa significa princípio do menor a minha surpresa.” de en.wikipedia.org/wiki/Ruby_(programming_language)#Philosophy O mesmo vale para Python. Eu tinha uma citação semelhante sobre o criador do Python, mas esqueci onde estava.
Darek Nędza

Respostas:

59

Wikipedia Ruby gotchas

Do artigo:

  • Os nomes que começam com uma letra maiúscula são tratados como constantes, portanto, as variáveis ​​locais devem começar com uma letra minúscula.
  • Os caracteres $e @não indicam o tipo de dados variável como em Perl, mas funcionam como operadores de resolução de escopo.
  • Para denotar números de ponto flutuante, deve-se seguir com um dígito zero ( 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.
  • Avaliação booleano de dados não é rigoroso booleanas: 0, ""e []são todos avaliados para true. Em C, a expressão é 0 ? 1 : 0avaliada como 0(ou seja, falsa). Em Ruby, entretanto, ele cede 1, como todos os números avaliam true; apenas nile falseavalie para false. 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, mas nilem caso de falha (por exemplo, incompatibilidade). Esta convenção também é usada em Smalltalk, onde apenas os objetos especiais truee falsepodem ser usados ​​em uma expressão booleana.
  • Versões anteriores a 1.9 não têm um tipo de dados de caractere (compare com C, que fornece tipo charpara caracteres). Isso pode causar surpresas ao fatiar strings: "abc"[0]yields 97(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 for true. Isso ocorre porque statement until expressioné realmente o açúcar sintático sobre

    until expression
      statement
    end
    

    , Cujo equivalente em C / C ++ é while (not(expression)) statement;tal como statement if expressioné um equivalente ao

    if expression
      statement
    end
    

    No entanto, a notação

    begin
      statement
    end until expression
    

    em Ruby irá de fato executar a instrução uma vez, mesmo se a expressão já for verdadeira.

  • Como as constantes são referências a objetos, alterar a que uma constante se refere gera um aviso, mas a modificação do próprio objeto não. Por exemplo, Greeting << " world!" if Greeting == "Hello"não gera um erro ou aviso. Isso é semelhante a finalvariá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 ande ornão seguem as regras normais de precedência: andnão se vincula mais apertado que or. Ruby também possui operadores de expressão ||e &&que funcionam conforme o esperado.

  • defpor dentro defnão faz o que um programador Python poderia esperar:

    def a_method
        x = 7
        def print_x; puts x end
        print_x
    end
    

    Isso dá um erro sobre xnão ter sido definido. Você precisa usar um Proc.

Características da linguagem

  • A omissão de parênteses em torno dos argumentos do método pode levar a resultados inesperados se os métodos aceitarem vários parâmetros. Os desenvolvedores Ruby afirmaram que a omissão de parênteses em métodos multiparâmetros pode não ser permitida em futuras versões Ruby; o interpretador Ruby atual (novembro de 2007) lança um aviso que encoraja o escritor a não omitir (), 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 chamado method_missing().
Andy
fonte
1
Ruby 1.9 também carece de tipo de dados de caractere. No 1.8, o operador de índice retornou um Fixnum; em 1.9, é equivalente a fatiar uma string de um caractere.
whitequark
38

Os novatos terão problemas com métodos de igualdade :

  • a == b : verifica se a e b são iguais. Isso é o mais útil.
  • a.eql? b : também verifica se a e b são iguais, mas às vezes é mais estrito (pode verificar se a e b têm o mesmo tipo, por exemplo). É usado principalmente em Hashes.
  • a.equal? b : verifica se aeb são o mesmo objeto (verificação de identidade).
  • a === b : usado em declarações case (eu li como " a corresponde a b ").

Esses exemplos devem esclarecer os primeiros 3 métodos:

a = b = "joe"

a==b       # true
a.eql? b   # true
a.equal? b # true (a.object_id == b.object_id)

a = "joe"
b = "joe"

a==b       # true
a.eql? b   # true
a.equal? b # false (a.object_id != b.object_id)

a = 1
b = 1.0

a==b       # true
a.eql? b   # false (a.class != b.class)
a.equal? b # false

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:

class Person
    attr_reader name
    def == (rhs)
      rhs.name == self.name  # compare person by their name
    end
    def eql? (rhs)
      self == rhs
    end
    # never override the equal? method!
end

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:

# === is usually simply an alias for ==
"joe" === "joe"  # true
"joe" === "bob"  # false

# but ranges match any value they include
(1..10) === 5        # true
(1..10) === 19       # false
(1..10) === (1..10)  # false (the range does not include itself)

# arrays just match equal arrays, but they do not match included values!
[1,2,3] === [1,2,3] # true
[1,2,3] === 2       # false

# classes match their instances and instances of derived classes
String === "joe"   # true
String === 1.5     # false (1.5 is not a String)
String === String  # false (the String class is not itself a String)

A declaração de caso é baseada no método === :

case a
  when "joe": puts "1"
  when 1.0  : puts "2"
  when (1..10), (15..20): puts "3"
  else puts "4"
end

é equivalente a isto:

if "joe" === a
  puts "1"
elsif 1.0 === a
  puts "2"
elsif (1..10) === a || (15..20) === a
  puts "3"
else
  puts "4"
end

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:

class Subnet
  [...]
  def include? (ip_address_or_subnet)
    [...]
  end
  def === (rhs)
    self.include? rhs
  end
end

case destination_ip
  when white_listed_subnet: puts "the ip belongs to the white-listed subnet"
  when black_listed_subnet: puts "the ip belongs to the black-listed subnet"
  [...]
end
MiniQuark
fonte
1
Além disso: a = 'строка'; b = 'строка'; pa == b; a = a.force_encoding 'ASCII-8BIT'; b = b.force_encoding 'UTF-8'; pa == b; pa === b; p a.eql? b; p a.equal? b
Nakilon
20
  • 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_missingou sendtiverem 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.

Dan Vinton
fonte
18

O código a seguir me surpreendeu. Acho que é um problema perigoso: fácil de encontrar e difícil de depurar.

(1..5).each do |number|
  comment = " is even" if number%2==0
  puts number.to_s + comment.to_s
end

Isso imprime:

1
2 is even
3
4 is even
5

Mas se eu apenas adicionar comment =algo antes do bloco ...

comment = nil
(1..5).each do |number|
  comment = " is even" if number%2==0
  puts number.to_s + comment.to_s
end

Então eu recebo:

1
2 is even
3 is even
4 is even
5 is even

Basicamente, quando uma variável é definida apenas dentro de um bloco, ela é destruída no final do bloco e, em seguida, é redefinida a nilcada 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:

comment = number%2==0 ? " is even" : nil

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.

MiniQuark
fonte
4
Você também pode sombrear a variável de escopo externa por (1..5) do | número; comentário | ..... Leia aqui stackoverflow.com/questions/1654637/…
Özgür
6
Isso me parece lógico. Esse escopo é típico de outras linguagens, é apenas a sintaxe que é diferente.
g.
No entanto, você pode escrever a = (b if c)para obter o efeito desejado, sem o ternário. Isso ocorre porque é b if cavaliado como nulo se cfor falsey.
Cameron Martin de
16

Ao chamar supersem argumentos, o método substituído é realmente chamado com os mesmos argumentos do método de substituição.

class A
  def hello(name="Dan")
    puts "hello #{name}"
  end
end

class B < A
  def hello(name)
    super
  end
end

B.new.hello("Bob") #=> "hello Bob"

Para realmente chamar supersem argumentos, você precisa dizer super().

Daniel Lucraft
fonte
3
Se B#hellotiver name = 42antes do super, ele dirá "olá 42".
Andrew Grimm
14

Os blocos e métodos retornam o valor da última linha por padrão. Adicionar putsinstruções ao final para fins de depuração pode causar efeitos colaterais desagradáveis

Andrew Grimm
fonte
11

Tive muitos problemas para entender variáveis ​​de classe, atributos de classe e métodos de classe. Este código pode ajudar um novato:

class A
  @@classvar = "A1"
  @classattr = "A2"
  def self.showvars
    puts "@@classvar => "+@@classvar
    puts "@classattr => "+@classattr
  end
end

A.showvars
  # displays:
  # @@classvar => A1
  # @classattr => A2

class B < A
  @@classvar = "B1"
  @classattr = "B2"
end

B.showvars
  # displays:
  # @@classvar => B1
  # @classattr => B2

A.showvars
  # displays:
  # @@classvar => B1   #Class variables are shared in a class hierarchy!
  # @classattr => A2   #Class attributes are not
MiniQuark
fonte
1
Sim, as variáveis ​​de classe podem ser complicadas. Acho que a maioria dos Rubistas experientes diria que é aconselhável evitá-los, já que normalmente existem outras maneiras de resolver um problema sem eles. Alguns entusiastas da linguagem até diriam que as variáveis ​​de classe do Ruby são mal projetadas em um nível de linguagem.
David J.
8

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.

Karina
fonte
Você pode usar a = b if a.nil?ou @a = b unless defined?(@a).
Andrew Grimm
8
  • 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.

dylanfm
fonte
Se você não planeja ir além do Ruby 1.8.6, ignore os parênteses o quanto quiser. Caso contrário, é melhor você usá-los.
Mike Woodhouse
7

Tive problemas com mixins que contêm métodos de instância e métodos de classe. Este código pode ajudar um novato:

module Displayable
  # instance methods here
  def display
    puts name
    self.class.increment_displays
  end
  def self.included(base)
    # This module method will be called automatically
    # after this module is included in a class.
    # We want to add the class methods to the class.
    base.extend Displayable::ClassMethods
  end
  module ClassMethods
    # class methods here
    def number_of_displays
      @number_of_displays # this is a class attribute
    end
    def increment_displays
      @number_of_displays += 1
    end
    def init_displays
      @number_of_displays = 0
    end
    # this module method will be called automatically
    # after this module is extended by a class.
    # We want to perform some initialization on a
    # class attribute.
    def self.extended(base)
      base.init_displays
    end
  end
end

class Person
  include Displayable
  def name; @name; end
  def initialize(name); @name=name; end
end

puts Person.number_of_displays # => 0
john = Person.new "John"
john.display # => John
puts Person.number_of_displays # => 1
jack = Person.new "Jack"
jack.display # => Jack
puts Person.number_of_displays # => 2

No início, pensei que poderia ter módulos com métodos de instância e métodos de classe simplesmente fazendo isto:

module Displayable
  def display
    puts name
    self.class.increment_displays
  end
  def self.number_of_displays  # WRONG!
    @number_of_displays
  end
  [...]
end

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).

MiniQuark
fonte
7

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) e 0...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 é:

  • fácil de digitar incorretamente e
  • fácil de perder com seus olhos enquanto olha o código

não deve ser capaz de causar bugs devastadores em meus programas.

que que
fonte
1
Não é muito diferente de for (i=0; i<max; i++)efor (i=0; i<=max; i++)
g.
Tenho tentado descobrir qual é a diferença entre 0..10 e 0 ... 10.
Luis D Urraca,
6

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 parecido message_not_understood.

some_object.do_something(args)

é equivalente a

some_object.send(:do_something, args) # note the :

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.

Mike Woodhouse
fonte
Obrigado. Há uma coisa que odeio no método send : ele permite chamar métodos privados mesmo fora da classe! Ai.
MiniQuark
1
@MiniQuark: é isso que adoro no método de envio!
Andrew Grimm
6

Se você declarar um setter (também conhecido como modificador) usando attr_writerou attr_accessor(ou def foo=), tome cuidado ao chamá-lo de dentro da classe. Como as variáveis ​​são declaradas implicitamente, o interpretador sempre precisa resolver foo = bardeclarando uma nova variável chamada foo, em vez de chamar o método self.foo=(bar).

class Thing
  attr_accessor :foo
  def initialize
    @foo = 1      # this sets @foo to 1
    self.foo = 2  # this sets @foo to 2
    foo = 3       # this does *not* set @foo
  end
end

puts Thing.new.foo #=> 2

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 = 123ou self['value'] = 123.

AlexChaffee
fonte
5

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.

Chirantan
fonte
4

Uma coisa que me surpreendeu no passado é que a \nsequê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.

John Topley
fonte
1
E isso é diferente de qual outra linguagem?
Robert Gamble
Java, por exemplo. As aspas simples em Java só podem ser usadas para incluir um único caractere, não Strings.
John Topley
1
Isso está de acordo com qualquer linguagem que permite usar aspas simples para strings, e é por isso que o fazem.
singpolyma
@John: true, mas '\ n' em Java ainda será o caractere de nova linha.
Jorn
1
Mas em Java as aspas simples apenas criam valores do tipo char. Não são cordas. Essa é a diferença.
jmucchiello
4
x = (true and false) # x is false

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.

singpolyma
fonte
0 == true // argh, o compilador c em meu cérebro está explodindo !!
Kenny
1
0 == verdadeiro dá falso em Ruby. Esse 0 é verdadeiro faz sentido porque verdadeiro é um objeto em Ruby. Em C 0 apenas acontece de ter a mesma representação como falsa.
Jules
Em uma condição em Ruby, apenas falsee nilsão os falsos. Todos os outros são valores verdadeiros.
rubyprince
4

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! )

#demo.rb
class Demo

  def hello1
    p "Hello from first definition"
  end

  # ...lots of code here...
  # and you forget that you have already defined hello1

  def hello1
    p "Hello from second definition"
  end

end
Demo.new.hello1

Corre:

$ ruby demo.rb
=> "Hello from second definition"

Mas chame-o com os avisos habilitados e você verá o motivo:

$ ruby -w demo.rb
demo.rb:10: warning: method redefined; discarding old hello1
=> "Hello from second definition"
Zabba
fonte
Eu teria +100 o uso de avisos se pudesse.
Andrew Grimm,
3

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

Danmayer
fonte
2

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.

irb(main):003:0* str_val = '5.0'
=> "5.0"
irb(main):006:0> str_val.to_int
NoMethodError: undefined method `to_int' for "5.0":String
        from (irb):6
irb(main):005:0* str_val.to_i
=> 5


irb(main):007:0> float_val = 5.0
=> 5.0
irb(main):008:0> float_val.to_int
=> 5
irb(main):009:0> float_val.to_i
=> 5
irb(main):010:0>

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.

Monkut
fonte
2

Relacionado à resposta de monkut, os to_foométodos de Ruby dão uma ideia do quão estrita uma conversão eles farão.

Os curtos como to_i, to_sdiga 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:

"10".to_i == 10
:foo.to_s == "foo"

As funções mais explícitas como to_int, to_ssignificam que o objeto pode ser nativamente representado como esse tipo de dados. Por exemplo, a Rationalclasse representa todos os números racionais, portanto, pode ser representada diretamente como um inteiro Fixnum (ou Bignum) por chamada to_int.

Rational(20,4).to_int == 5

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.

Lucas
fonte
1
É "preguiçoso" a palavra certa aqui?
Andrew Grimm,
1

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?

Andrew Grimm
fonte
4
isso não é válido a partir de 1.9: "No Ruby 1.9, no entanto, os elementos hash são iterados em sua ordem de inserção" da linguagem de programação Ruby
Özgür
0

Este me deixou louco uma vez:

1/2 == 0.5 #=> false
1/2 == 0   #=> true
Andrei
fonte
Acredito que isso se comportaria exatamente da mesma maneira em Java, C e C ++.
Larry de
É engraçado, eu nem pensei nisso, mas se você abrir o irb e tentar fazer isso, faz sentido: então (1/2) é um Fixnum e (0,5) é um Float. E sabemos que Fixnim! = Float.
DemitryT
2
@DemitryT Acho que o motivo mais simples é que 1/2avalia para 0, que não é igual 0.5, independente do tipo. No entanto Rational(1, 2) == 0.5, e 1.0 == 1.
Max Nanasy
soluço de linguagem universal aqui. isso é algo que alguém novo em Ruby e programação deve saber.
dtc
0
1..5.each {|x| puts x}

não funciona. Você tem que colocar o intervalo entre parênteses, como

(1..5).each {|x| puts x}

então ele não acha que você está ligando 5.each. Acho que é uma questão de precedência, assim como a x = true and falsepegadinha.

Andrew Grimm
fonte
Eu o chamaria de parênteses. Em segundo lugar, se algum código parecer ter um problema de valor de retorno / precedência, ele deve ser colocado entre parênteses de qualquer maneira. Então, para mim, não há nada de especial neste "pegadinho". Você pode continuar escrevendo todas as "pegadinhas" combinacionais, mas isso seria perda de tempo. Francamente, cara, mesmo que você tivesse o resultado esperado nisso, eu ainda preferiria cercar com parênteses.
Özgür