2016-01-07 30 views
1

我是一個有經驗的C#開發人員試圖教自己的F#。我花了一天或三天的時間閱讀試圖瞭解語法和F#基礎知識的F# wikibook爲什麼我感覺我的F#代碼可以更簡潔

作爲練習,我試圖去通過Project Euler問題得到語法和使用語言的工作更好的感覺。

我剛剛解決了problem 5。但是我不必爲了獲得代表我的解決方案的數據結構而跳過這些環節。 我用this blogpost來解決問題的算法。

我想知道如果任何人都可以給我一些指點,以這段代碼如何改進?我的猜測是,F#值的內在不變性是造成我一定要執行很多步驟來獲得我想要的確切數據...

這是我的代碼:

let main argv = 
//calculates the prime factors of a number 
let findPrimeFactors x = 
    let primes = [|2I;3I;5I;7I;11I;13I;17I;19I|] 
    let rec loop acc counter = function 
     | x when x = 1I -> failwith "A PRIME IS BY DEFINITION GREATER THAN 1" 
     | x when primes |> Array.contains x -> x :: acc 
     | x when counter = primes.Length -> failwith "MY LIST OF KNOWN PRIMES IS NOT BIG ENOUGH" 
     | x when x%primes.[counter]=0I-> loop (primes.[counter]::acc) (counter) (x/primes.[counter]) 
     | x -> loop acc (counter + 1) x 

    let primeFactor = loop [] 0 x |> List.rev 
    primeFactor 

//calculates the prime factors for each of the numbers between 2 and n 
//then, for each of the prime factorizations it tries to find the highest power for each occurring prime factor 
let findPrimeFactorsPowers n = 
    //builds a map of all the prime factor powers for all prime factorizations 
    let rec addCounterFactorPowers factorPowers = function 
     | counter when counter = n -> factorPowers 
     | (counter : int) -> addCounterFactorPowers ((findPrimeFactors (counter|>bigint) |> List.countBy (fun x-> x)) @ factorPowers) (counter + 1) 
    let allFactorPowers = addCounterFactorPowers [] 2 
    //group all the powers per prime factor 
    let groupedFactorPowers = allFactorPowers |> List.groupBy (fun (factor, power) -> factor) 
    //get the highest power per prime factor 
    let maxFactorPowers = groupedFactorPowers |> List.map (fun (key, powers) -> (key, powers |> List.map (fun (factor, power) -> power) |> List.max)) 

    //return the result 
    maxFactorPowers   

let n = 20; 
let primeFactorSet = findPrimeFactorsPowers n 
printfn "%A" primeFactorSet 

let smallestNumberDivisableByAllNumbersBelown = (primeFactorSet |> List.fold (fun state (factor, power) -> state * pown factor power) 1I) 
printfn "Result %A" smallestNumberDivisableByAllNumbersBelown 

System.Console.ReadKey(true)|>ignore 
0 // return an integer exit code 
+3

如果你的代碼正確運行,100%(和你的問題符合在http所有要求://codereview.stackexchange .com/help/on-topic),我會考慮將此問題發佈到CodeReview stackexchange。這種問題似乎更適合那裏。 –

+0

刪除評論,即時減少20%。 :) – Daniel

+0

這個問題可能是適用於[代碼審查(http://codereview.stackexchange.com/help),只要(一)你的代碼按預期工作,(b)您的代碼是真正的代碼,而不是示例代碼,以及(c)您的代碼包含在問題的正文中。如果您希望通過同行評審來改進代碼的各個方面,請將其發佈在代碼評審中。 – Phrancis

回答

5

有許多直接簡化,你可以適用於你的代碼,但我不認爲這是最好的方法。

這是我會解決F#這個問題的方式:

let rec tryDivide n m = 
    if m = 1 then true 
    else 
     if n % m = 0 then tryDivide n (m-1) 
     else false 

let rec findIt i m = 
    if tryDivide i m then i 
    else findIt (i+1) m 

findIt 1 20 

這是比你慢一點,因爲它不使用硬編碼的素數,他們在計算上的蒼蠅,但它仍然需要在我的電腦上少於2秒才能得到正確的答案。說我沒有使用任何類似列表的數據結構,並無需依靠大整數在這一特定問題

注意。

UPDATE

下面是基於KVB的提出的解決方案更好的方法:

let rec gcd x y = if y = 0 then abs x else gcd y (x % y) 

let lcm a b = 
    match (a, b) with 
    | (_, 0) | (0, _) -> 0 
    | (x, y) -> abs ((x/(gcd x y)) * y) 

Seq.fold lcm 1 {2..20} 
+0

謝謝古斯塔沃。這確實是比較容易的暴力方式。由於我的目標是學習F#,我想挑戰自己編碼實際算法,而不是提出一個快速解決方案。 – Stif

+0

但是,對於簡單問題尋找複雜的解決方案會將您帶到一個暗區,在這個區域不容易提出建議,因爲您一直意識到您過度設計。我會建議堅持簡單的解決方案,並繼續與歐拉項目,它會很快變得更加複雜:) – Gustavo

+2

當然,也有簡單但不是蠻力的方式來解決問題(例如'List.fold lcm 1 [2。 .20]'爲一個適當定義的'lcm'函數計算兩個整數的最小公倍數,它可以即時有效地返回正確的結果。 – kvb

相關問題