2010-06-11 47 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? 
        # ... 
    
相關問題