2010-06-11 103 views
0

我正在学习Ruby并通过解决Project Euler中的问题来练习它。更多类似ruby的解决方案来解决这个问题?

这是我对问题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 

可以对这段代码做任何改进?

+1

是什么' - >'?那是我不知道的1.9个动作吗? – theIV 2010-06-11 16:29:54

+1

其新的lambda语法,而不是d = lambda {| x |如果你只有一个参数,你可以省略括号:' - > num {(num *(num + 1)}在ruby 1.9中你可以写成d = - >(x){x + 1} – RaouL 2010-06-11 16:35:21

+0

1))/ 2}' – 2010-06-11 18:15:17

回答

2

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

第一部分是找出三角形数字是什么。由于这是使用自然数序列,所以可以用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 

这也可以使用被称为注入,将传递什么是从先前元素返回当前元素的另一个枚举法进行。使用这个,你可以在一行中得到总和。在这个例子中,我使用了1.upto(10),它的功能与(1..10)相同。

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

通过此步进,第一时间这就是所谓,总和= 0,X = 1,故(总和+ X)= 1。然后它通过这对下一个元素等总和= 1,X (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) 
+0

再次读取您的代码,很明显,您已经了解了大多数这些方法,并且对于不完全阅读这些方法表示歉意。在重构的情况下,你有点过于沉重,但是正如ryanjm.mp所说,这是一个风格问题,但我认为你不会发现许多同意你风格的ruby人。如果您只使用一次,某些事情如将triangle_number lambda存储到变量中是不必要的。就可读性而言,factor_count作为一种方法比lambda更有意义,甚至可以在以后公开。祝你好运。 – lambdabutz 2010-06-11 17:02:32

+0

我对lambda表达了太多的兴趣,因为它对我来说是一件新事物:)说实话,我从来没有编写过任何支持函数式编程范式的语言,感谢您的建议 – RaouL 2010-06-11 17:07:53

0

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

而不是一个loop do你可以while (faction_count(traingle_number(n)) < 500) do,但对于一些可能太多的一行。

4

正如其他人所指出的,Ruby开发者会使用方法或块比lambda表达式的方法等等。

Ruby的Enumerable是一个非常强大的mixin,所以我觉得它在这里支付建立一个类似于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? 
        # ...