我正试图用F#包围函数式编程。我正在努力解决Project Euler问题,我觉得我只是在F#中编写过程代码。例如,这是我对#3的解决方案。
let Calc() =
let mutable limit = 600851475143L
let mutable factor = 2L // Start with the lowest prime
while factor < limit do
if limit % factor = 0L then
begin
limit <- limit / factor
end
else factor <- factor + 1L
limit
这很好用,但我真正做的就是如何在c#中解决这个问题并将其转换为F#语法。回顾我的几个解决方案,这已成为一种模式。我认为我应该能够在不使用mutable
的情况下解决这个问题,但是我无法在程序上思考这个问题。
答案 0 :(得分:4)
为什么不用递归?
let Calc() =
let rec calcinner factor limit =
if factor < limit then
if limit % factor = 0L then
calcinner factor (limit/factor)
else
calcinner (factor + 1L) limit
else limit
let limit = 600851475143L
let factor = 2L // Start with the lowest prime
calcinner factor limit
答案 1 :(得分:4)
对于算法问题(如项目Euler),您可能希望使用递归来编写大多数迭代(如John建议的那样)。但是,如果您使用例如,即使是可变的命令性代码也是有意义的。哈希表或数组并关心性能。
F#工作得很好的一个领域(遗憾的是)Euler练习项目没有真正涵盖的是设计数据类型 - 所以如果你有兴趣从另一个角度学习F#,请看看Designing with types at F# for Fun and Profit
在这种情况下,您还可以使用Seq.unfold
来实现解决方案(通常,您通常可以使用Seq
函数编写解决方案以处理序列处理问题 - 尽管此处看起来并不优雅)
let Calc() =
// Start with initial state (600851475143L, 2L) and generate a sequence
// of "limits" by generating new states & returning limit in each step
(600851475143L, 2L)
|> Seq.unfold (fun (limit, factor) ->
// End the sequence when factor is greater than limit
if factor >= limit then None
// Update limit when divisible by factor
elif limit % factor = 0L then
let limit = limit / factor
Some(limit, (limit, factor))
// Update factor
else
Some(limit, (limit, factor + 1L)) )
// Take the last generated limit value
|> Seq.last
答案 2 :(得分:2)
在函数式编程中,当我认为可变时,我认为堆和尝试编写功能更强大的代码时,应该使用堆栈而不是堆。
那么如何在堆栈上获取与函数一起使用的值?
让result01 = List.filter(有趣的x - &gt; x%2 = 0)[0; 1; 2; 3; 4; 5]
这里一个函数和一个值列表被硬编码到List.filter参数中。
let divisibleBy2 = fun x -> x % 2 = 0 let values = [0;1;2;3;4;5] let result02 = List.filter divisibleBy2 values
这里list.filter的函数参数绑定到divisibleBy2,list.filter的list参数绑定到值。
let result03 = [0;1;2;3;4;5] |> List.filter divisibleBy2
这里list.filter的list参数被前传到list.filter函数。
let result04 = [ for i in 1 .. 5 -> i] |> List.filter divisibleBy2
现在我们已经拥有堆栈中的所有数据,我们如何仅使用堆栈处理数据?
通常与函数式编程一起使用的模式之一是将数据放入结构中,然后使用递归函数一次处理一个项目。结构可以是列表,树,图等,并且通常使用区分联合来定义。具有一个或多个自引用的数据结构通常与递归函数一起使用。
所以这里有一个例子,我们取一个列表并将所有值乘以2,并在我们前进时将结果放回堆栈。保存新值的堆栈上的变量是accumulator
。
let mult2 values =
let rec mult2withAccumulator values accumulator =
match values with
| headValue::tailValues ->
let newValue = headValue * 2
let accumulator = newValue :: accumulator
mult2withAccumulator tailValues accumulator
| [] ->
List.rev accumulator
mult2withAccumulator values []
我们使用累加器作为函数的参数而未定义可变的存储在堆栈中。此方法也使用模式匹配和列表区分联合。当我们处理输入列表中的项目时,累加器保存新值,然后当列表中没有更多项目([])时,我们只需反转列表以正确的顺序获取新列表,因为新项目是连接的到accumulator
。
要了解您需要查看的列表的数据结构(区别联合),所以这里是
type list =
| Item of 'a * List
| Empty
注意项目定义的结尾如何List
引用自身,并且列表可以是一个空列表,当与模式匹配一起使用时[]
。
如何构建列表的快速示例
empty list - []
list with one int value - 1::[]
list with two int values - 1::2::[]
list with three int values - 1::2::3::[]
这是与定义的所有类型相同的函数。
let mult2 (values : int list) =
let rec mult2withAccumulator (values : int list) (accumulator : int list) =
match (values : int list) with
| (headValue : int)::(tailValues : int list) ->
let (newValue : int) = headValue * 2
let (accumulator : int list) =
(((newValue : int) :: (accumulator : int list)) : int list)
mult2withAccumulator tailValues accumulator
| [] ->
((List.rev accumulator) : int list)
mult2withAccumulator values []
因此,将值放到堆栈上并使用带有模式匹配的自引用区分联合将有助于解决函数式编程中的许多问题。