2013-10-13 4 views
2

Я не получаю правильные результаты из следующего метода обезьяны ремонтного в Integer:Почему инъекция рубина не суммируется правильно?

def harm 
    1 + (2..self).inject{|sum, x| sum + 1/x.to_r} 
end 

2.harm #=> 3 

он должен вернуть 3/2 вместо, где моя ошибка?

+0

он делает именно то, что вы ему говорите. Говорить, что он «не суммирует правильно», вводит в заблуждение. – sevenseacat

ответ

2

Вот наконечник (вам необходимо пройти начальное значение метода inject):

def harm 
    1 + (2..2).inject(0){|sum, x| sum + 1/x.to_r} 
end 

harm # => (3/2) 

Документация Enumerable#inject:

Если указать блок, то для каждого элемента перечислить блок передается значение аккумулятора (памятка) и элемент. Если вы укажете вместо этого символ, то каждый элемент в коллекции будет передан именованному методу memo. В любом случае результат становится новым значением для памятки. В конце итерации конечное значение memo является возвращаемым значением для метода.

Если вы явно не указали начальное значение для memo, тогда первый элемент коллекции используется как начальное значение memo.

+0

Что такое начальное значение по умолчанию? –

+1

Вам не нужно * нужно * передать начальное значение, начальное значение будет первым элементом в массиве, если вы явно не передадите его. – meagar

+0

@meagar Попробуйте использовать тот же код в вашем IRB с начальным аргументом как '0' и без' 0'. Вы можете увидеть diff .. и причина также объясняется в документации .. Я также связал и вставил сюда. . :) –

4

Есть две проблемы:

  1. Когда вы итерацию по замкнутому диапазоне, например, 2..2, ничего на самом деле не происходит:

    (0..0).inject(){|s, x| s+= 99 } 
    # => 0 
    

    Именно поэтому вы получите 3, а 1 + 2 - 3.

  2. Если вы не передать аргумент в inject, он использует первое значение вы передаете в итератор в качестве исходного комментария, т.е. 2:

    (2..2).inject(){|s, x| s+= 99 } 
    #=> 2 
    

    Ввода в 0 получает вас фактическую итерацию:

    (2..2).inject(0){|s, x| s+= 99 } 
    #=> 99 
    

Так что попробуйте это вместо того, чтобы в вашем методе:

1 + (2..self).inject(0){|sum, x| sum + 1/x.to_r} 

Standalone: ​​

1 + (2..2).inject(0){|sum, x| sum + 1/x.to_r} 
#=> 3/2 
+1

Ваше первое предложение верно, но '1 + 2 + 1/2.to_r' - нет. Это так, результатом будет '7/2'. – sawa

+0

Отредактировано это ... «inject» ведет себя по-другому, чем я думал, когда в –

1

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

class Integer 
    def harm 
    return 0 if self == 0 
    return -(-self).harm if self < 0 
    (1 .. self).map { |n| Rational 1, n }.reduce :+ 
    end 
end 

0.harm #=> 0 
2.harm #=> 3/2 
7.harm #=> 363/140 
-2.harm #=> (-3/2) 

Примечания, однако, что для большого числа, это очень читаемый код становится неэффективным, так как он готовит массив в памяти перед выполнением суммирования.

+0

не было начального напоминания @Boris ... Долгое время не вижу .. :) Как дела? –

+0

@ Послушайте свои сообщения, я был так занят, что еще не смог ответить :-) Приятно слышать от вас, во всяком случае, и приятно видеть, что вы активны :-) –

+0

@boris .. Я рад, что наконец я получил ваш ответ ... :) –