F# 创建中间值时,我应该存储它吗?
我正在努力学习F#,所以我访问了,目前正在学习 13195的主要因素是5,7, 13和29 最大的素数是多少 数字600851475143的系数 需要考虑的一些事项:F# 创建中间值时,我应该存储它吗?,f#,functional-programming,primes,factorization,F#,Functional Programming,Primes,Factorization,我正在努力学习F#,所以我访问了,目前正在学习 13195的主要因素是5,7, 13和29 最大的素数是多少 数字600851475143的系数 需要考虑的一些事项: 我的首要任务是学习良好的功能性习惯 我的第二个优先事项是我希望它是快速和高效的 在下面的代码中,我已经标记了这个问题所涉及的部分 let isPrime(n:int64) = let rec check(i:int64) = i > n / 2L or (n % i <> 0L &am
let isPrime(n:int64) =
let rec check(i:int64) =
i > n / 2L or (n % i <> 0L && check(i + 1L))
check(2L)
let greatestPrimeFactor(n:int64) =
let nextPrime(prime:int64):int64 =
seq { for i = prime + 1L to System.Int64.MaxValue do if isPrime(i) then yield i }
|> Seq.skipWhile(fun v -> n % v <> 0L)
|> Seq.hd
let rec findNextPrimeFactor(number:int64, prime:int64):int64 =
if number = 1L then prime else
//************* No variable
(fun p -> findNextPrimeFactor(number / p, p))(nextPrime(prime))
//*************
//************* Variable
let p = nextPrime(prime)
findNextPrimeFactor(number / p, p)
//*************
findNextPrimeFactor(n, 2L)
let isPrime(n:int64)=
让rec检查(i:int64)=
i>n/2L或(n%i 0L和检查(i+1L))
检查(2L)
设greatestPrimeFactor(n:int64)=
让NextTime(素数:int64):int64=
seq{for i=prime+1L to System.Int64.MaxValue do如果isPrime(i)则产生i}
|>轻快地滑行(乐趣->n%v 0L)
|>Seq.hd
让rec查找dnextprimefactor(数字:int64,素数:int64):int64=
如果数字=1L,则为其他值加素数
//*************无变量
(乐趣p->findNextPrimeFactor(数字/p,p))(下一次(素数))
//*************
//*************变数
设p=nexttime(prime)
findNextPrimeFactor(编号/p,p)
//*************
findNextPrimeFactor(n,2L)
更新
根据一些反馈,我将代码重构为快10倍
module Problem3
module private Internal =
let execute(number:int64):int64 =
let rec isPrime(value:int64, current:int64) =
current > value / 2L or (value % current <> 0L && isPrime(value, current + 1L))
let rec nextPrime(prime:int64):int64 =
if number % prime = 0L && isPrime(prime, 2L) then prime else nextPrime(prime + 1L)
let rec greatestPrimeFactor(current:int64, prime:int64):int64 =
if current = 1L then prime else nextPrime(prime + 1L) |> fun p -> greatestPrimeFactor(current / p, p)
greatestPrimeFactor(number, 2L)
let execute() = Internal.execute(600851475143L)
模块问题3
模块专用内部=
让我们执行(编号:int64):int64=
let rec isPrime(值:int64,当前值:int64)=
电流>值/2L或(值%current 0L&&isPrime(值,电流+1L))
let rec nexttime(prime:int64):int64=
如果数字%prime=0L&&isPrime(prime,2L),则在下一个时间为else(prime+1L)
let rec greatestPrimeFactor(当前:int64,素数:int64):int64=
如果当前值=1L,则为下一次的其他值加素数(素数+1L)|>fun p->greatestPrimeFactor(当前值/p,p)
最大优先因子(编号,2L)
let execute()=内部.execute(600851475143L)
更新
我要感谢大家的建议。这个最新版本是我收到的所有建议的汇编
module Problem3
module private Internal =
let largestPrimeFactor number =
let rec isPrime value current =
current > value / 2L || (value % current <> 0L && isPrime value (current + 1L))
let rec nextPrime value =
if number % value = 0L && isPrime value 2L then value else nextPrime (value + 1L)
let rec find current prime =
match current / prime with
| 1L -> prime
| current -> nextPrime (prime + 1L) |> find current
find number (nextPrime 2L)
let execute() = Internal.largestPrimeFactor 600851475143L
模块问题3
模块专用内部=
设最大PrimeFactor数=
让rec isPrime值当前=
当前>值/2L | |(值%current 0L&&isPrime值(当前+1L))
让rec nexttime值=
如果数字%value=0L&&isPrime值2L,则值else nextPrime(值+1L)
让rec找到当前素数=
将当前/素数与
|1L->prime
|当前->下一次时间(初始值+1L)|>查找当前值
查找号码(下一次2L)
let execute()=Internal.largestPrimeFactor 600851475143L
变量p实际上是名称绑定,而不是变量。使用名称绑定是一种不错的风格。而且它更具可读性。nexttime
的惰性风格很好,它实际上在整个程序中只对每个数字进行一次初始测试
我的解决方案
let problem3 =
let num = 600851475143L
let rec findMax (n:int64) (i:int64) =
if n=i || n<i then
n
elif n%i=0L then
findMax (n/i) i
else
findMax n (i+1L)
findMax num 2L
我认为带有临时绑定的代码更容易阅读。创建一个匿名函数,然后像在另一种情况下那样立即将其应用于一个值,这是非常罕见的。如果您真的想避免使用临时值,我认为在F#中最惯用的方法是使用
(|>)
操作符将值导入匿名函数,但我仍然认为这不太可读。关于“良好的函数习惯”或更好的实践,我看到了三件小事。在序列中使用收益率比仅仅过滤要难一些。类型推断语言中不必要的类型注释会导致重构困难,并使代码更难阅读。如果您觉得很困难,请不要过火,尝试删除每个类型注释。最后,生成一个lambda函数,该函数只接受一个值作为临时变量,这会降低可读性
就个人风格而言,我更喜欢更多的空格,当数据分组在一起有意义时,只使用元组参数
我会这样写你的原始代码
let isPrime n =
let rec check i =
i > n / 2L || (n % i <> 0L && check (i + 1L))
check 2L
let greatestPrimeFactor n =
let nextPrime prime =
seq {prime + 1L .. System.Int64.MaxValue}
|> Seq.filter isPrime
|> Seq.skipWhile (fun v -> n % v <> 0L)
|> Seq.head
let rec findNextPrimeFactor number prime =
if number = 1L then
prime
else
let p = nextPrime(prime)
findNextPrimeFactor (number / p) p
findNextPrimeFactor n 2L
让iPrime n=
让rec检查i=
i>n/2L | |(n%i 0L&检查(i+1L))
检查2L
设最大主因子n=
设NextTime素数=
seq{prime+1L..System.Int64.MaxValue}
|>Seq.filter iPrime
|>轻快地滑行(乐趣->n%v 0L)
|>序号头
让rec查找dnextprimefactor数prime=
如果数量=1L,则
首要的
其他的
设p=nexttime(prime)
findNextPrimeFactor(编号/p)p
findNextPrimeFactor n 2L
更新后的代码最适合您的方法。你必须使用一种不同的算法,比如尹朱的答案来加快速度。我编写了一个测试来检查F#是否使“check”函数尾部递归,并且确实如此。函数编程在实践中变得更容易、更自动化,所以如果第一次尝试时没有完全正确,不要担心 考虑到这一点,让我们以您的示例代码为例:
let rec findNextPrimeFactor(number:int64, prime:int64):int64 =
if number = 1L then prime else
//************* No variable
(fun p -> findNextPrimeFactor(number / p, p))(nextPrime(prime))
//*************
//************* Variable
let p = nextPrime(prime)
findNextPrimeFactor(number / p, p)
//*************
您的no variable
版本很奇怪,不要使用它。我喜欢带有显式let绑定的版本
另一种写作方式是:
nextPrime(prime) |> fun p -> findNextPrimeFactor(number / p, p)
这样写是可以的,偶尔也有用,但还是有点奇怪。大多数情况下,我们使用|>
来转换值,而不需要为变量命名(以“无点”样式)。尝试预测您的函数将如何使用,如果可能,重新编写它,以便可以在不显式声明变量的情况下与管道操作符一起使用。例如:
let rec findNextPrimeFactor number prime =
match number / prime with
| 1L -> prime
| number' -> nextPrime(prime) |> findNextPrimeFactor number'
没有其他命名参数:)
好的,现在我们已经解决了这个问题,让我们看看您的
isPrim
let rec findNextPrimeFactor number prime =
match number / prime with
| 1L -> prime
| number' -> nextPrime(prime) |> findNextPrimeFactor number'
let isPrime(n:int64) =
let rec check(i:int64) =
i > n / 2L or (n % i <> 0L && check(i + 1L))
check(2L)
let isPrime n = seq { 2L .. n / 2L } |> Seq.exists (fun i -> n % i = 0L) |> not
let maxFactor n =
seq { 2L .. n - 1L } // test inputs
|> Seq.filter isPrime // primes
|> Seq.filter (fun x -> n % x = 0L) // factors
|> Seq.max // result