О каких Ruby Gotchas следует предупреждать новичка? [закрыто]

108

Я недавно выучил язык программирования Ruby, и в целом это хороший язык. Но я был весьма удивлен, увидев, что все оказалось не так просто, как я ожидал. Точнее, «правило наименьшего удивления» мне показалось не очень уважаемым (конечно, это довольно субъективно). Например:

x = true and false
puts x  # displays true!

и знаменитые:

puts "zero is true!" if 0  # zero is true!

О каких еще проблемах вы бы предупредили новичка в Ruby?

MiniQuark
источник
@ phrases.insert (0, p) OK @ phrases.insert (p) НИЧЕГО не происходит @phrases << p # OK
Anno2001
почему true and falseвозвращается истина?
Юрген Пауль
3
Потому что «x = истина и ложь» фактически интерпретируется как «(x = истина) и ложь». Это вопрос приоритета оператора: «and» имеет более низкий приоритет, чем «=». Большинство других языков имеют обратный приоритет, я не знаю, почему они выбрали этот порядок в Rails, я нахожу это очень запутанным. Если вы хотите «нормального» поведения, просто введите «x = (true и false)», тогда x будет ложным.
MiniQuark
4
Другое решение - использовать "&&" и "||" вместо «и» и «или»: они ведут себя так, как ожидалось. Например: «x = true && false» приводит к тому, что x ложно.
MiniQuark
«Принцип наименьшего удивления означает принцип наименьшего моего удивления.» из en.wikipedia.org/wiki/Ruby_(programming_language)#Philosophy То же самое и с Python. У меня была похожая цитата о создателе Python, но я забыл, где она была.
Дарек Нендза

Ответы:

59

Википедия, подводные камни Ruby

Из статьи:

  • Имена, начинающиеся с заглавной буквы, рассматриваются как константы, поэтому локальные переменные должны начинаться со строчной буквы.
  • Символы $и @не указывают тип данных переменных, как в Perl, а скорее функционируют как операторы разрешения области видимости.
  • Для обозначения чисел с плавающей запятой необходимо указать нулевую цифру ( 99.0) или явное преобразование ( 99.to_f). Недостаточно добавлять точку ( 99.), потому что числа зависят от синтаксиса метода.
  • Булева оценка не-логических данных является строгим: 0, ""и []все оценены с true. В языке C выражение 0 ? 1 : 0оценивается как 0ложное. В Ruby, однако, он дает результат 1, как и все числа true; только nilи falseоценивать false. Следствием этого правила является то, что методы Ruby по соглашению - например, поиск по регулярным выражениям - возвращают числа, строки, списки или другие не ложные значения при успехе, но nilпри неудаче (например, несовпадении). Это соглашение также используется в Smalltalk, где в логических выражениях могут использоваться только специальные объекты trueи false.
  • В версиях до 1.9 отсутствует символьный тип данных (сравните с C, который предоставляет тип charдля символов). Это может вызвать сюрпризы при нарезке строк: "abc"[0]yields 97(целое число, представляющее код ASCII первого символа в строке); чтобы получить "a"использование "abc"[0,1](подстрока длины 1) или "abc"[0].chr.
  • Нотация statement until expression, в отличие от эквивалентных операторов других языков (например, do { statement } while (not(expression));в C / C ++ / ...), фактически никогда не запускает оператор, если выражение уже есть true. Это потому, что statement until expressionна самом деле синтаксический сахар

    until expression
      statement
    end

    , эквивалент которого в C / C ++ такой while (not(expression)) statement;же, как statement if expressionи эквивалент

    if expression
      statement
    end

    Однако обозначение

    begin
      statement
    end until expression

    в Ruby фактически выполнит оператор один раз, даже если выражение уже истинно.

  • Поскольку константы являются ссылками на объекты, изменение того, на что ссылается константа, вызывает предупреждение, а изменение самого объекта - нет. Например, Greeting << " world!" if Greeting == "Hello"не выдает ошибку или предупреждение. Это похоже на finalпеременные в Java, но в Ruby также есть возможность «заморозить» объект, в отличие от Java.

Некоторые функции, которые заметно отличаются от других языков:

  • Обычные операторы для условных выражений andи orне следуют обычным правилам приоритета: andне связывает более жестко, чем or. Рубин также операторы выражения ||и &&которые работают , как и ожидалось.

  • definside defне делает того, чего может ожидать программист Python:

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

    Это дает ошибку о том, что xне определено. Вам нужно использовать Proc.

Особенности языка

  • Отсутствие круглых скобок вокруг аргументов метода может привести к неожиданным результатам, если методы принимают несколько параметров. Разработчики Ruby заявили, что отсутствие скобок в многопараметрических методах может быть запрещено в будущих версиях Ruby; текущий (ноябрь 2007 г.) интерпретатор Ruby выдает предупреждение, которое побуждает автора не пропускать (), чтобы избежать неоднозначного значения кода. Отказ от использования ()по-прежнему является обычной практикой, и может быть особенно полезно использовать Ruby как сам читаемый предметно-ориентированный язык программирования вместе с вызываемым методом method_missing().
Энди
источник
1
В Ruby 1.9 также отсутствует символьный тип данных. В версии 1.8 оператор индекса возвращал Fixnum; в 1.9 это эквивалентно разрезанию односимвольной строки.
whitequark 01
38

У новичков будут проблемы с методами равенства :

  • a == b : проверяет, равны ли a и b. Это самый полезный.
  • a.eql? b : также проверяет, равны ли a и b, но иногда это более строго (например, может проверяться, что a и b имеют один и тот же тип). В основном он используется в хэшах.
  • a.equal? b : проверяет, являются ли a и b одним и тем же объектом (проверка личности).
  • a === b : используется в операторах case (я прочитал это как « a соответствует b »).

Эти примеры должны прояснить первые 3 метода:

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

Обратите внимание, что == , eql? и равный? всегда должен быть симметричным: если a == b, то b == a.

Также обратите внимание, что == и eql? оба реализованы в классе Object как равные псевдонимы ? , так что если вы создаете новый класс и хотите == и eql? для обозначения чего-то другого, кроме простой идентичности, вам необходимо переопределить их оба. Например:

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

Метод === ведет себя иначе. Прежде всего, он не симметричен (a === b не означает, что b === a). Как я уже сказал, вы можете читать a === b как «a соответствует b». Вот несколько примеров:

# === 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)

Оператор case основан на методе === :

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

эквивалентно этому:

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

Если вы определяете новый класс, экземпляры которого представляют собой какой-то контейнер или диапазон (если у него есть что-то вроде метода include? Или match? ), Вы можете найти полезным переопределить метод === следующим образом:

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
источник
1
Также: a = 'строка'; b = 'строка'; pa == b; a = a.force_encoding 'ASCII-8BIT'; b = b.force_encoding 'UTF-8'; pa == b; pa === b; p a.eql? б; p a.equal? b
Накилон
20
Дэн Винтон
источник
18

Следующий код меня удивил. Я считаю, что это опасная ошибка: и легко столкнуться, и трудно отладить.

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

Это печатает:

1
2 is even
3
4 is even
5

Но если я просто добавлю comment =что-нибудь перед блоком ...

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

Тогда я получаю:

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

Обычно, когда переменная определяется только внутри блока, она уничтожается в конце блока, а затем сбрасывается nilна каждую итерацию. Обычно это то, чего вы ожидаете. Но если переменная будет определена перед блоком, то внешняя переменная используется внутри блока, и его значение , следовательно , сохраняются между итераций.

Одним из решений было бы написать вместо этого:

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

Я думаю, что многие люди (включая меня) склонны писать « a = b if c» вместо « a = (c ? b : nil)», потому что это более читабельно, но, очевидно, у него есть побочные эффекты.

MiniQuark
источник
4
Вы можете просто затенять переменную внешней области видимости (1..5) do | number; comment | ..... Прочтите здесь stackoverflow.com/questions/1654637/…
Озгюр,
6
Мне это кажется логичным. Эта область видимости типична для других языков, отличается только синтаксис.
г.
Однако вы можете написать, a = (b if c)чтобы получить желаемый эффект, без троичности. Это потому, что b if cесли cложно, то значение равно нулю.
Кэмерон Мартин
16

При вызове superбез аргументов замещенный метод фактически вызывается с теми же аргументами, что и замещающий метод.

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"

Чтобы действительно позвонить superбез аргументов, вам нужно сказать super().

Дэниел Люкрафт
источник
3
Если B#helloесть name = 42до того super, то он говорит «привет 42».
Эндрю Гримм
14

По умолчанию блоки и методы возвращают значение последней строки. Добавление putsоператоров в конец в целях отладки может вызвать неприятные побочные эффекты.

Эндрю Гримм
источник
11

У меня было много проблем с пониманием переменных класса, атрибутов класса и методов класса. Этот код может помочь новичку:

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
источник
1
Да, переменные класса могут быть сложными. Я думаю, что наиболее опытные рубисты сказали бы, что их следует избегать, поскольку обычно есть другие способы решить проблему без них. Некоторые энтузиасты языка могут даже сказать, что переменные класса Ruby плохо разработаны на уровне языка.
Дэвид Дж
8

я научился осторожно использовать оператор || =. и будьте особенно осторожны, если имеете дело с логическими значениями. Я обычно использовал a || = b как уловку, чтобы дать 'a' значение по умолчанию, если все остальное не удалось, а 'a' оставалось равным нулю. но если a ложно, а b истинно, тогда a будет присвоено значение true.

Karina
источник
Вы можете использовать a = b if a.nil?или @a = b unless defined?(@a).
Эндрю Гримм
8
  • Блоки действительно важно понимать, они используются везде.

  • Вам не нужны скобки вокруг параметров метода. Используете вы их или нет - решать вам. Некоторые говорят, что вы всегда должны их использовать .

  • Для обработки исключений используйте подъём и спасение, а не метод throw и catch.

  • Вы можете использовать, ;но не обязаны, если не хотите поместить несколько вещей в одну строку.

dylanfm
источник
Если вы не планируете выходить за рамки Ruby 1.8.6, игнорируйте скобки сколько угодно. В противном случае вам, вероятно, лучше их использовать.
Майк Вудхаус
7

У меня была проблема с Mixins , которые содержат методы экземпляра и методы класса. Этот код может помочь новичку:

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

Сначала я думал, что могу иметь модули как с методами экземпляра, так и с методами класса, просто сделав следующее:

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

К сожалению, метод number_of_displays никогда не будет включен или расширен, потому что это «метод класса модуля». Только «методы экземпляра модуля» могут быть включены в класс (как методы экземпляра) или расширены в класс (как методы класса). Вот почему вам нужно поместить методы экземпляра вашего миксина в модуль, а методы класса вашего миксина - в другой модуль (вы обычно помещаете методы класса в подмодуль «ClassMethods»). Благодаря включенному магическому методу вы можете упростить включение как методов экземпляра, так и методов класса всего в один простой вызов «include Displayable» (как показано в примере выше).

Этот миксин будет считать каждое отображение для каждого класса . Счетчик является атрибутом класса, поэтому у каждого класса будет свой собственный (ваша программа, вероятно, выйдет из строя, если вы получите новый класс из класса Person, поскольку счетчик @number_of_displays для производного класса никогда не будет инициализирован). Вы можете заменить @number_of_displays на @@ number_of_displays, чтобы сделать его глобальным счетчиком. В этом случае у каждой иерархии классов будет свой счетчик. Если вам нужен глобальный и уникальный счетчик, вам, вероятно, следует сделать его атрибутом модуля.

Все это было определенно не интуитивно для меня, когда я начинал с Ruby.

Я до сих пор не могу понять, как чисто сделать некоторые из этих методов миксина частными или защищенными (только метод display и number_of_displays должны быть включены как общедоступные методы).

оборота MiniQuark
источник
7

Обратите внимание на обозначение Range.

(По крайней мере, уделите больше внимания, чем я изначально!)

Есть разница между 0..10 (две точки) и 0...10(три точки).

Мне очень нравится Руби. Но эта штука точка-точка против точки-точки-точки меня беспокоит. Я думаю, что такая тонкая "особенность" двойного синтаксиса:

  • легко ошибиться, и
  • легко пропустить взглядом, просматривая код

не должны приводить к серьезным ошибкам в моих программах.

очереди
источник
1
Не сильно отличается от for (i=0; i<max; i++)иfor (i=0; i<=max; i++)
g.
Я пытался выяснить, в чем разница между 0..10 и 0 ... 10.
Луис Д Уррака
6

Я думаю, что « and» и « or» - это отсылки к Perl, который является одним из наиболее очевидных «родителей» Ruby (самым известным из которых является Smalltalk). Они оба имеют гораздо более низкий приоритет (ниже чем уступки, на самом деле, что , когда поведение отмечено происходит от) , чем &&и ||которые операторов вы должны использовать.

Другие вещи, о которых следует знать, не сразу очевидны:

На самом деле вы не вызываете методы / функции, хотя это вроде как выглядит. Вместо этого, как в Smalltalk, вы отправляете сообщение объекту. Так method_missingдействительно больше нравится message_not_understood.

some_object.do_something(args)

эквивалентно

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

Символы очень широко используются. Это те вещи, которые начинаются с, :и они не сразу очевидны (ну, они не были для меня), но чем раньше вы с ними разберетесь, тем лучше.

Ruby хорошо разбирается в «утином типе», следуя принципу, что «если он ходит, как утка, и крякает, как утка ...», что позволяет неформальную замену объектов общим подмножеством методов без какого-либо явного наследования или взаимосвязи примешивания.

Майк Вудхаус
источник
Спасибо. Есть одна вещь, которую я ненавижу в методе send : он позволяет вызывать частные методы даже вне класса! Ой.
MiniQuark
1
@MiniQuark: вот что мне нравится в методе отправки!
Эндрю Гримм
6

Если вы объявляете сеттер (он же мутатор) с помощью attr_writeror attr_accessor(или def foo=), будьте осторожны, вызывая его изнутри класса. Поскольку переменные объявляются неявно, интерпретатор всегда должен разрешать foo = barкак объявление новой переменной с именем foo, а не вызывать метод 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

Это также относится к объектам Rails ActiveRecord, для которых средства доступа определяются на основе полей в базе данных. Поскольку это даже не переменные экземпляра @-стиля, правильный способ установить эти значения индивидуально с помощью self.value = 123или self['value'] = 123.

оборота AlexChaffee
источник
5

Понимание разницы между классом времени и даты. Оба они разные и создают проблемы при использовании их в рельсах. Класс Time иногда конфликтует с другими библиотеками классов Time, присутствующими в стандартной библиотеке ruby ​​/ rails. Лично мне потребовалось много времени, чтобы понять, что именно происходит в моем приложении rails. Позже я подумал, когда сделал

Time.new

Речь шла о какой-то библиотеке в месте, о котором я даже не знал.

Извините, если я не совсем понимаю, что хочу сказать. Если другие сталкивались с подобными проблемами, пожалуйста, объясните еще раз.

Chirantan
источник
4

В прошлом меня зацепило то, что \nescape-последовательность символа новой строки ( ), среди прочего, не поддерживается строками в одинарных кавычках. Сама обратная косая черта экранируется. Вы должны использовать двойные кавычки, чтобы экранирование работало должным образом.

Джон Топли
источник
1
И это отличается от какого другого языка?
Роберт Гэмбл,
Например, Java. Одиночные кавычки в Java могут использоваться только для заключения одного символа, но не для строк.
Джон Топли
1
Это соответствует любому языку, который позволяет использовать одинарные кавычки для строк, и именно поэтому они это делают.
singpolyma
@John: верно, но '\ n' в Java по-прежнему будет символом новой строки.
Йорн
1
Но в Java одинарные кавычки создают только значения типа char. Не струны. Вот в чем разница.
jmucchiello
4
x = (true and false) # x is false

0 и '' верны, как вы отметили.

У вас может быть метод и модуль / класс с одним и тем же именем (что имеет смысл, потому что метод фактически добавляется к Object и, следовательно, имеет собственное пространство имен).

Множественного наследования не существует, но часто используются «миксин-модули» для добавления общих методов к нескольким классам.

сингполима
источник
0 == true // argh компилятор c в моем мозгу взрывается !!
Кенни
1
0 == true дает false в Ruby. То, что 0 истинно, имеет смысл, потому что истина - это объект в Ruby. В C 0 просто оказывается то же представление, что и false.
Жюль
В условии в Ruby, только falseи nilявляются ложными. Все остальные - истинные ценности.
rubyprince
4

Методы можно переопределить, и они могут стать скетчем, пока вы не обнаружите причину. ( Следует признать, что эту ошибку, вероятно, немного «сложнее» обнаружить, когда действие контроллера Ruby on Rails переопределяется по ошибке! )

#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

Бегать:

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

Но вызовите его с включенными предупреждениями, и вы увидите причину:

$ ruby -w demo.rb
demo.rb:10: warning: method redefined; discarding old hello1
=> "Hello from second definition"
Забба
источник
Если бы я мог, я бы добавил +100 к использованию предупреждений.
Эндрю Гримм
3

Я думаю, что всегда полезно использовать .length для вещей ... поскольку размер поддерживается почти всем, а Ruby имеет динамические типы, вы можете получить действительно странные результаты, вызывая .size, когда у вас неправильный тип ... Я бы предпочел получить a NoMethodError: undefined метод `length ', поэтому я обычно никогда не вызываю size для объектов в Ruby.

укусил меня не раз.

Также помните, что у объектов есть идентификаторы, поэтому я стараюсь не использовать переменные call id или object_id, чтобы избежать путаницы. Если мне нужен идентификатор объекта Users, лучше всего назвать его как-то вроде user_id.

Только мои два цента

Данмайер
источник
2

Я новичок в Ruby, и в моем первом раунде я столкнулся с проблемой, касающейся изменения чисел с плавающей запятой / строк на целое число. Я начал с поплавков и закодировал все как f.to_int . Но когда я продолжил и применил тот же метод для строк, у меня возникла кривая, когда дело дошло до запуска программы.

По сути, у строки нет метода to_int , но есть числа с плавающей точкой и целые числа.

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>

Произвольные скобки меня поначалу тоже подбросили. Я видел какой-то код с и без. Мне потребовалось некоторое время, чтобы понять, что оба стиля приемлемы.

monkut
источник
2

Связанные с ответом monkut, to_fooметоды Ruby намекают, насколько строго они будут выполнять преобразование.

Короткие, например to_i, to_sскажите ему, чтобы он был ленив, и преобразовать их в целевой тип, даже если они не могут быть точно представлены в этом формате. Например:

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

Более длинные явные функции, такие как to_int, to_sозначают, что объект может быть изначально представлен как этот тип данных. Например, Rationalкласс представляет все рациональные числа, поэтому его можно напрямую представить как целое число Fixnum (или Bignum) путем вызова to_int.

Rational(20,4).to_int == 5

Если вы не можете вызвать более длинный метод, это означает, что объект не может быть изначально представлен в этом типе.

Итак, в основном, при преобразовании, если вы ленивы с именами методов, Ruby будет ленив с преобразованием.

Люк
источник
1
Подходит ли здесь слово «ленивый»?
Эндрю Гримм
1

Итерация по рубиновым хешам не гарантируется в каком-либо конкретном порядке. (Это не ошибка, это особенность)

Hash#sort пригодится, если вам нужен конкретный заказ.

Связанный вопрос: почему массив Ruby из 1000 пар ключей и значений хешей всегда находится в определенном порядке?

Эндрю Гримм
источник
4
это не действует с версии 1.9: «Однако в Ruby 1.9 элементы хэша повторяются в порядке их вставки» из языка программирования Ruby
Озгюр,
0

Однажды меня это рассердило:

1/2 == 0.5 #=> false
1/2 == 0   #=> true
Андрей
источник
Я считаю, что это будет вести себя точно так же в Java, C и C ++.
Ларри
Забавно, я даже не подумал об этом, но если вы откроете irb и попробуете это, это станет логичным: Итак, (1/2) - это Fixnum, а (0.5) - это Float. И мы знаем, что Fixnim! = Float.
DemitryT
2
@DemitryT Я думаю, что более простая причина заключается в том, что 1/2вычисляется значение 0, которое не равно 0.5, независимо от типа. Однако Rational(1, 2) == 0.5, и 1.0 == 1.
Max Nanasy
универсальный языковой сбой здесь. это то, что должен знать кто-то новичок в программировании и рубине.
dtc
0
1..5.each {|x| puts x}

не работает. Вы должны заключить диапазон в круглые скобки, например

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

так что он не думает, что ты звонишь 5.each. Я думаю, что это проблема приоритета, как и x = true and falseошибка.

Эндрю Гримм
источник
Вместо этого я бы назвал это скобками. Во-вторых, если какой-либо код выглядит как имеющий проблему с возвращаемым значением / приоритетом, его все равно следует заключить в круглые скобки. Так что, на мой взгляд, в этой "ловушке" нет ничего особенного. Вы можете продолжать писать все комбинационные "подводные камни", хотя это было бы пустой тратой времени. Честно говоря, дружище, даже если бы у вас был ожидаемый результат, я все же предпочел бы заключить его в круглые скобки.
Озгюр