Как я могу правильно связать пользовательские методы в Ruby?

Я пытаюсь сделать метод цепочки для следующих двух методов. После запуска этого кода я продолжал получать следующий вывод:

#<SimpleMath:0x007fc85898ab70>% 

Мой вопрос: как правильно связать методы в Ruby?

Вот мои коды:

class SimpleMath


    def add(a,b=0)
        a + b
        return self
    end


    def subtract(a,b=0)
         a - b
        return self
    end

end
newNumber = SimpleMath.new()
print newNumber.add(2,3).add(2)

person visBar    schedule 08.07.2014    source источник
comment
На что вы рассчитывали/надеялись?   -  person Frederick Cheung    schedule 09.07.2014
comment
Это это правильный способ цепочки методов. Вы возвращаете экземпляр, и это именно то, что отображается.   -  person Dave Newton    schedule 09.07.2014
comment
Когда я делаю newNumber.add(2,3), я хочу получить 5, а не #‹SimpleMath:0x007fc85898ab70›%   -  person visBar    schedule 09.07.2014
comment
... Тогда вам нужно будет изменить строковое представление класса по умолчанию.   -  person Dave Newton    schedule 09.07.2014
comment
Кроме того, что-то вроде add(2) не имеет смысла, если вы не сохраняете результат в экземпляре.   -  person Dave Newton    schedule 09.07.2014
comment
Вам также нужно где-то хранить результаты этих сложений/вычитаний.   -  person Frederick Cheung    schedule 09.07.2014


Ответы (4)


Вы пытаетесь сделать что-то подобное?

class SimpleMath
  def initialize
    @result = 0
  end

  #1 add function
  def add(val)
    @result += val
    self
  end

  #2 Subtract function
  def subtract(val)
    @result -= val
    self
  end

  def to_s
    @result
  end
end

newNumber = SimpleMath.new
p newNumber.add(2).add(2).subtract(1)

Для любого количества аргументов

class SimpleMath
  def initialize
    @result = 0
  end

  #1 add function
  def add(*val)
    @result += val.inject(&:+)
    self
  end

  #2 Subtract function
  def subtract(*val)
    @result -= val.inject(&:+)
    self
  end

  def to_s
    @result
  end
end

newNumber = SimpleMath.new
p newNumber.add(1, 1).add(1, 1, 1, 1).subtract(1)
person Rustam A. Gasanov    schedule 08.07.2014
comment
Рустам, я хотел, чтобы методы принимали как минимум два параметра. - person visBar; 09.07.2014
comment
@visBar, ты идешь - person Rustam A. Gasanov; 09.07.2014
comment
Это очень аккуратно. Спасибо. - person visBar; 09.07.2014
comment
Как я могу также заставить его получать массив в качестве параметра? - person visBar; 09.07.2014
comment
@visBar, используйте val.flatten.inject(&:+). Таким образом, вы можете передавать массивы или числа или массивы и числа... - person Rustam A. Gasanov; 09.07.2014
comment
@RustamA.Gasanov- это newNumber.add(1, 1).add(1, 1, 1, 1).subtract(1) дает мне <SimpleMath:0x007fcb7fa1c7a8 @result=5> этого хорошего Но если мне нужно получить только 5. Как я могу получить? во-вторых, как я могу вызвать обратный вызов для каждой функции. Вроде newNumber.add(1, 1) должно дать 2 - person Gupta; 19.05.2021

Давайте определим экземпляр вашего класса SimpleMath:

sm = SimpleMath.new #=> #<SimpleMath:0x000001020ca820>

Здесь следует отметить три вещи:

  • sm - это переменная. В Ruby переменные представлены строчными буквами, которые могут быть разделены символом подчеркивания (например, my_var).
  • в то время как можно добавить () после new, когда new не имеет аргументов (так называемых «параметров»), это необязательно и обычно не делается.
  • если ключевое слово return отсутствует, Ruby возвращает последнее вычисление, выполненное методом. Здесь вы обычно пишете последнюю строку просто как self, и это будет возвращено. Увы, это не имеет значения, поскольку возврат self с ключевым словом return или без него — это не то, что вам нужно.

Попробуйте следующее в IRB:

sm.add(2) #=> #<SimpleMath:0x000001020ca820>

Вы, несомненно, ожидали, что это вернет 2+0 #=> 2, но вместо этого оно вернуло self, что, как вы можете видеть выше, на самом деле равно sm (#<SimpleMath:0x000001020ca820>).

Вы можете исправить это, просто удалив строку:

return self

от add и subtract:

class SimpleMath
  def add(a,b=0)
    a + b
  end

  def subtract(a,b=0)
    a - b
  end
end

Теперь

sm = SimpleMath.new
sm.add(2) #=> 2

Однако, если мы попытаемся связать еще один add, у нас будет другая проблема:

sm.add(2).add(2,3) #=> NoMethodError: undefined method `add' for 2:Fixnum

Это сообщение очень ясное: класс Fixnum, экземпляром которого является 2, не имеет метода экземпляра с именем add. Это потому, что вы определили его для класса SimpleMath, а не для Fixnum.

Когда Ruby выполняет sm.add(2).add(3,4), он сначала вычисляет sm.add(2) #=> 2, что сокращает выражение до 2.add(3,4). Затем он пытается отправить метод add (с двумя его параметрами) в 2, но обнаруживает, что класс 2.class #=> Fixnum не имеет метода экземпляра add; отсюда и исключение.

Мы можем исправить эту ошибку, определив вместо этого эти методы для класса Fixnum:

class Fixnum
  def add(a,b=0)
    a + b
  end

  def subtract(a,b=0)
    a - b
  end
end

Вы можете подтвердить, что эти методы были добавлены в класс Fixnum, выполнив:

Fixnum.instance_methods.sort

Теперь другая проблема:

sm = Fixnum.new #=> NoMethodError: undefined method `new' for Fixnum:Class

О боже, в классе Fixnum нет метода new! Это потому, что экземпляры Fixnum являются целыми числами, которые нельзя создать. Вы можете легко подтвердить, что целые числа являются экземплярами Fixnum:

72.class #=> Fixnum
-3.class #=> Fixnum

Таким образом, мы можем вызвать метод add, отправив его в любой экземпляр Fixnum:

72.add(2) #=> 2
-3.add(2) #=> 2

Теперь попробуем связать add операций:

72.add(2).add(3,4)       #=> 7
72.add(2000000).add(3,4) #=> 7

Не исключение, но и не цепочка. Чтобы исправить это, нужно еще раз изменить методы:

class Fixnum
  def add(b=0)
    puts "in add, self = #{self}, b = #{b}"
    self + b
  end

  def subtract(b=0)
    puts "in subtract, self = #{self}, b = #{b}"
    self - b
  end
end

Я добавил оператор puts в каждый метод на случай, если потребуется дополнительная отладка. Мы удалим их, когда код заработает правильно. Давайте проверим:

2.add(3)                    #=> 5
  in add, self = 2, b = 3
5.add                       #=> 5
  in add, self = 5, b = 0
5.add(7)                    #=> 12
  in add, self = 5, b = 7
2.add(3).add.add(7)         #=> 12
  in add, self = 2, b = 3
  in add, self = 5, b = 0
  in add, self = 5, b = 7

2.subtract(5)               #=> -3
  in subtract, self = 2, b = 5
-3.subtract                 #=> -3
  in subtract, self = -3, b = 0
2.subtract(5).subtract      #=> -3
  in subtract, self = 2, b = 5
  in subtract, self = -3, b = 0

2.add(3).subtract(5).add(7) #=>  7
  in add, self = 2, b = 3
  in subtract, self = 5, b = 5
  in add, self = 0, b = 7

Успех! Возьми?

person Cary Swoveland    schedule 09.07.2014
comment
Кэри, спасибо за разъяснение. Я думаю, что мое понимание цепочек теперь твердое. - person visBar; 14.07.2014

Другой способ — создать пайплайн с помощью гема chainable_methods.

Описано в статье

require 'chainable_methods'

module SimpleMath
  include ChainableMethods

  def add(a, b=0)
    a + b
  end

  def subtract(a, b=0)
    a - b    
  end
end

SimpleMath.
  chain_from(5).
  add(5).
  add(5).
  subtract(3).
  unwrap
person merqlove    schedule 30.10.2016

Этот парень (tjackiw.tumblr.com) использует этот как вопрос интервью и дает очень четкое представление о том, как и почему правильный ответ похож на следующий:

class Interpreter

  def initialize(&block)
    instance_eval(&block)
  end

  def at(time)
    @time = time
    self
  end

  def when(date)
    @date = date
    self
  end

  def we(*people)
    @people = people
    self
  end

  def going(where)
    @where = where
    self
  end

  def output
    [@people.join(' and '), "are going", @where, @date, "at", @time].join(' ')
  end

end
person doub1ejack    schedule 01.02.2016