为什么我觉得我的F#代码可以更简洁

为什么我觉得我的F#代码可以更简洁,f#,immutability,F#,Immutability,我是一名经验丰富的C#开发人员,试图自学F#。我花了一到三天的时间通读了一遍,试图了解语法和F#基本原理 作为一个练习,我试图通过这些问题来更好地理解语法和使用语言 我刚刚解决了。但是,我不太高兴我不得不跳过这些障碍来获得代表我的解决方案的数据结构。 我过去一直在研究解决这个问题的算法 我想知道是否有人能给我一些关于如何改进这段代码的建议?我的猜测是,F#值固有的不变性导致我不得不执行许多步骤来获得我想要的准确数据 这是我的代码: let main argv = //calculates the

我是一名经验丰富的C#开发人员,试图自学F#。我花了一到三天的时间通读了一遍,试图了解语法和F#基本原理

作为一个练习,我试图通过这些问题来更好地理解语法和使用语言

我刚刚解决了。但是,我不太高兴我不得不跳过这些障碍来获得代表我的解决方案的数据结构。 我过去一直在研究解决这个问题的算法

我想知道是否有人能给我一些关于如何改进这段代码的建议?我的猜测是,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

您可以对代码进行许多直接简化,但我认为这不是最好的方法

这就是我在F#中解决这个问题的方法:

它比你的要慢一点,因为它不使用硬编码的素数,它们是动态计算的,但在我的计算机上,仍然需要不到2秒的时间才能得到正确的答案

请注意,我没有使用任何类似列表的数据结构,在这个特定问题中也不需要依赖大整数

更新

以下是基于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}

您可以对代码进行许多直接简化,但我认为这不是最好的方法

这就是我在F#中解决这个问题的方法:

它比你的要慢一点,因为它不使用硬编码的素数,它们是动态计算的,但在我的计算机上,仍然需要不到2秒的时间才能得到正确的答案

请注意,我没有使用任何类似列表的数据结构,在这个特定问题中也不需要依赖大整数

更新

以下是基于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}


如果您的代码正确地运行了100%(并且您的问题满足了所有的要求),我会考虑将问题提交给CoDeVIEW StExchange。这类问题似乎更适合这里。删除评论可立即降低20%。)只要(a)您的代码按预期工作,(b)您的代码是真实代码,而不是示例代码,以及(c)您的代码包含在问题主体中,这个问题就可以适用。如果您希望通过同行评审来改进代码的各个方面,请将其发布在代码评审上。好的,我在寻找更通用的方法来解决F#中的不变性问题时,犹豫是否将问题发布在代码评审上。我期待更多的反应。但我现在把它贴在了代码审查上。如果你想避免不可变,你为什么要使用函数语言@ STIF?如果你的代码正确地运行100%(并且你的问题满足了所有的要求),我会考虑把这个问题提交给CoDeVIEW StExchange。这类问题似乎更适合这里。删除评论可立即降低20%。)只要(a)您的代码按预期工作,(b)您的代码是真实代码,而不是示例代码,以及(c)您的代码包含在问题主体中,这个问题就可以适用。如果您希望通过同行评审来改进代码的各个方面,请将其发布在代码评审上。好的,我在寻找更通用的方法来解决F#中的不变性问题时,犹豫是否将问题发布在代码评审上。我期待更多的反应。但我现在把它贴在了代码审查上。如果你想避免不变性,为什么要使用函数式语言@Stif?谢谢Gustavo。这确实是更简单的暴力方式。由于我的目标是学习F#,我想挑战自己,让自己编写实际的算法,而不是想出一个快速的解决方案。但是,为简单问题寻找复杂的解决方案会把你带到一个黑暗的区域,在那里提出建议并不容易,因为你意识到你一直在过度设计。我建议坚持使用简单的解决方案,并继续使用project Euler,它很快就会变得更复杂:)当然,也有简单但非暴力的方法来解决问题(例如
List.fold lcm 1[2..20]
适用于适当定义的
lcm
函数,该函数计算两个整数的最小公倍数,可立即有效返回正确结果)。@kvb I根据您的建议更新了答案。这实际上更快、更短。应该归功于你:)谢谢古斯塔沃。这确实是更简单的暴力方式。由于我的目标是学习F#,我想挑战自己,让自己编写实际的算法,而不是想出一个快速的解决方案。但是,为简单问题寻找复杂的解决方案会把你带到一个黑暗的区域,在那里提出建议并不容易,因为你意识到你一直在过度设计。我建议坚持使用简单的解决方案,并继续使用project Euler,它很快就会变得更复杂:)当然,也有简单但非暴力的方法来解决问题(例如
List.fold lcm 1[2..20]
适用于适当定义的
lcm
函数,该函数计算两个整数的最小公倍数,可立即有效返回正确结果)。@kvb I根据您的建议更新了答案。这实际上更快、更短。荣誉应该属于你:)