这个问题更像红宝石般的解决方案?

时间:2010-06-11 16:23:42

标签: ruby refactoring factorization

我正在学习ruby并通过解决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 个答案:

答案 0 :(得分:4)

正如其他人所说,Rubyists将使用方法或阻止方式而不是lambdas。

Ruby的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/0require 'backports'或执行loop
  • 首先检查块是否通过,可以改进each;你会经常看到这样的事情:

      def each
        return to_enum __method__ unless block_given?
        # ...
    

答案 1 :(得分:2)

不是一次性解决问题,而是查看问题的各个部分可能会帮助您更好地理解ruby。

第一部分是找出三角形的数字。由于这使用了自然数序列,因此您可以使用ruby中的范围来表示它。这是一个例子:

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

ruby​​中的数组被认为是可枚举的,而ruby提供了许多枚举数据的方法。使用此概念,您可以使用each方法迭代此数组,并传递一个对数字求和的块。

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.下一个sum = 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) 

答案 2 :(得分:0)

看起来你来自编写Ocaml或其他功能语言。在Ruby中,您可能希望使用更多def来定义方法。 Ruby就是要保持干净。但这也可能是个人偏好。

而不是loop do你可以while (faction_count(traingle_number(n)) < 500) do而不是一些可能对一行来说太多了。