Более похожее на рубин решение этой проблемы? - PullRequest
0 голосов
/ 11 июня 2010

Я изучаю рубин и практикую его, решая задачи из Project Euler .

Это мое решение проблемы 12 .

# Project Euler problem: 12
# What is the value of the first triangle number to have over five hundred divisors?

require 'prime'

triangle_number = ->(num){ (num *(num + 1)) / 2 }

factor_count = ->(num) do
  prime_fac = Prime.prime_division(num)
  exponents = prime_fac.collect { |item| item.last + 1 }
  fac_count = exponents.inject(:*)
end

n = 2
loop do
  tn = triangle_number.(n)
  if factor_count.(tn) >= 500
    puts tn
    break
  end
  n += 1
end

Есть ли какие-либо улучшения, которые можно внести в этот фрагмент кода?

Ответы [ 3 ]

4 голосов
/ 12 июня 2010

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

Ruby's Enumerable - очень мощный миксин, поэтому я считаю, что здесь стоит создать перечислимый аналогичноPrime.Итак:

require 'prime'
class Triangular
  class << self
    include Enumerable
    def each
      sum = 0
      1.upto(Float::INFINITY) do |i|
        yield sum += i
      end
    end
  end
end

Это очень универсально.Просто проверяю это работает:

Triangular.first(4) # => [1, 3, 7, 10]

Хорошо.Теперь вы можете использовать его для решения вашей проблемы:

def factor_count(num)
  prime_fac = Prime.prime_division(num)
  exponents = prime_fac.collect { |item| item.last + 1 }
  exponents.inject(1, :*)
end

Triangular.find{|t| factor_count(t) >= 500}  # => 76576500

Примечания :

  • Float::INFINITY является новым для 1.9.2.Или используйте 1.0/0, require 'backports' или loop, если используете более раннюю версию.
  • each можно улучшить, сначала проверив, что блок передан;вы часто будете видеть такие вещи, как:

      def each
        return to_enum __method__ unless block_given?
        # ...
    
2 голосов
/ 11 июня 2010

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

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

(1..10).to_a => [1,2,3,4,5,6,7,8,9,10]

Массив в ruby ​​считается перечислимым, а ruby ​​предоставляет множество способов для перечисления данных.Используя это понятие, вы можете перебирать этот массив, используя каждый метод, и передавать блок, который суммирует числа.

sum = 0
(1..10).each do |x|
  sum += x
end

sum => 55

Это также можно сделать с помощью другого перечислимого метода, известного как inject, который передаст то, что возвращается изпредыдущий элемент к текущему элементу.Используя это, вы можете получить сумму в одну строку.В этом примере я использую 1.upto (10), который будет функционально работать так же, как (1..10).

1.upto(10).inject(0) {|sum, x| sum + x} => 55

Пройдя через это, при первом вызове, sum = 0,x = 1, поэтому (sum + x) = 1. Затем он передает это следующему элементу и поэтому sum = 1, x = 2, (sum + x) = 3. Следующая сумма = 3, x = 3, (sum+ x) = 6. sum = 6, x = 4, (sum + x) = 10. И т. д. и т. д.

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

РЕФАКТИВНОЕ РЕШЕНИЕ (хотя и неэффективное ввсе)

def factors(n)
  (1..n).select{|x| n % x == 0}
end

def triangle(n)
  (n * (n + 1)) / 2
end

n = 2

until factors(triangle(n)).size >= 500
  puts n
  n += 1
end

puts triangle(n) 
0 голосов
/ 11 июня 2010

Похоже, вы пришли от написания Ocaml или другого функционального языка. В Ruby вы бы хотели использовать больше def для определения ваших методов. Руби о том, чтобы оставаться чистым. Но это также может быть личным предпочтением.

И вместо loop do вы могли бы while (faction_count(traingle_number(n)) < 500) do, но для некоторых это может быть слишком много для одной строки.

...