问题
The example code here solves a project Euler problem:
Starting with the number 1 and moving to the right in a clockwise direction a 5 by 5 spiral is formed as follows:
21 22 23 24 25
20 7 8 9 10
19 6 1 2 11
18 5 4 3 12
17 16 15 14 13
It can be verified that the sum of the numbers on the diagonals is 101.
What is the sum of the numbers on the diagonals in a 1001 by 1001 spiral formed in the same way?
but my question is a matter of functional programming style rather than about how to get the answer (I already have it). I am trying to teach myself a bit about functional programming by avoiding imperative loops in my solutions, and so came up with the following recursive function to solve problem 28:
let answer =
let dimensions = 1001
let max_number = dimensions * dimensions
let rec loop total increment increment_count current =
if current > max_number then total
else
let new_inc, new_inc_count =
if increment_count = 4 then increment + 2, 0
else increment, increment_count + 1
loop (total + current) new_inc new_inc_count (current + increment)
loop 0 2 1 1
However, it seems to me my function is a bit of a mess. The following imperative version is shorter and clearer, even after taking into account the fact that F# forces you to explicitly declare variables as mutable and doesn't include a += operator:
let answer =
let dimensions = 1001
let mutable total = 1
let mutable increment = 2
let mutable current = 1
for spiral_layer_index in {1..(dimensions- 1) / 2} do
for increment_index in {1..4} do
current <- current + increment
total <- total + current
increment <- increment + 2
total
Disregarding the fact that people with more maths ability have solved the problem analytically, is there a better way to do this in a functional style? I also tried using Seq.unfold to create a sequence of values and then piping the resulting sequence into Seq.sum, but this ended up being even messier than my recursive version.
回答1:
Since you didn't describe the problem you're trying to solve, this answer is based only on the F# code you posted. I agree that the functional version is a bit messy, but I believe it could be clearer. I don't really understand the nested for
loop in your imperative solution:
for increment_index in {1..4} do
current <- current + increment
total <- total + current
You're not using the increment_index
for anything, so you could just multiply increment
and current
by four and get the same result:
total <- total + 4*current + 10*increment
current <- current + 4*increment
Then your imperative solution becomes:
let mutable total = 0
let mutable increment = 2
let mutable current = 1
for spiral_layer_index in {1..(dimensions- 1) / 2} do
total <- total + 4*current + 10*increment
current <- current + 4*increment
increment <- increment + 2
total
If you rewrite this to a recursive function, it becomes just:
let rec loop index (total, current, increment) =
if index > (dimensions - 1) / 2 then total
else loop (index + 1) ( total + 4*current + 10*increment,
current + 4*increment, increment + 2 )
let total = loop 1 (0, 2, 1)
The same thing could be also written using Seq.fold
like this (this is even more "functional", because in functional programming, you use recursion only to implement basic functions, like fold
that can then be re-used):
let total, _, _=
{1 .. (dimensions - 1) / 2} |> Seq.fold (fun (total, current, increment) _ ->
(total + 4*current + 10*increment, current + 4 * increment, increment + 2)) (0, 1, 2)
NOTE: I'm not sure if this actually implements what you want. It is just a simplification of your imperative solution and then rewrite of that using a recursive function...
回答2:
In fact, this is Project Euler Problem 28 and my F# solution circa November 21, 2011 is quite similar to one suggested in Tomas' answer:
let problem028 () =
[1..500]
|> List.fold (fun (accum, last) n ->
(accum + 4*last + 20*n, last + 8*n)) (1,1)
|> fst
Indeed, solution of the original problem takes just one-liner simple fold over the list of all involved squares with corners at diagonal nodes while threading through the accumulated sum and value of current diagonal element. Folding is one of the major idioms of functional programming; there is a great classic paper A tutorial on the universality and expressiveness of fold that covers many important facets of this core pattern.
来源:https://stackoverflow.com/questions/9560340/looping-vs-recursion-with-f