Создание серии Fibonacci в F #
Я только начинаю изучать F #, используя VS2010, а ниже - моя первая попытка создания серии Fibonacci. Я пытаюсь создать список всех чисел меньше 400.
let fabList =
let l = [1;2;]
let mutable a = 1
let mutable b = 2
while l.Tail < 400 do
let c = a + b
l.Add(c)
let a = b
let b = c
Моя первая проблема заключается в том, что в последнем утверждении я получаю сообщение об ошибке "Неполная структурированная конструкция в этой строке или до этой точки в выражении" в последней строке. Я не понимаю, что я здесь делаю неправильно.
Хотя это, по-видимому, является очевидным способом создания списка довольно эффективным способом (от программиста С++/С#), из того, что мало мне известно о f #, похоже, это не кажется правильным способом сделать программу. Правильно ли я в этом чувстве?
Ответы
Ответ 1
Прежде всего, вы используете let
, как если бы это был оператор для изменения переменной, но это не так. В F # let
используется для объявления нового значения (которое может скрыть любые предыдущие значения с тем же именем). Если вы хотите написать код с помощью мутации, вам нужно использовать что-то вроде:
let c = a + b // declare new local value
l.Add(c)
a <- b // mutate value marked as 'mutable'
b <- c // .. mutate the second value
Вторая проблема с вашим кодом заключается в том, что вы пытаетесь изменить список F #, добавляя к нему элементы - списки F # неизменяемы, поэтому после их создания вы не можете их изменять (в частности, нет Add
член!). Если вы хотите написать это с помощью мутации, вы можете написать:
let fabList =
// Create a mutable list, so that we can add elements
// (this corresponds to standard .NET 'List<T>' type)
let l = new ResizeArray<_>([1;2])
let mutable a = 1
let mutable b = 2
while l.[l.Count - 1] < 400 do
let c = a + b
l.Add(c) // Add element to the mutable list
a <- b
b <- c
l |> List.ofSeq // Convert any collection type to standard F# list
Но, как уже отмечали другие, писать код таким образом не является идиоматическим решением F #. В F # вы должны использовать неизменяемые списки и рекурсию вместо циклов (например, while
). Например, например:
// Recursive function that implements the looping
// (it takes previous two elements, a and b)
let rec fibsRec a b =
if a + b < 400 then
// The current element
let current = a + b
// Calculate all remaining elements recursively
// using 'b' as 'a' and 'current' as 'b' (in the next iteration)
let rest = fibsRec b current
// Return the remaining elements with 'current' appended to the
// front of the resulting list (this constructs new list,
// so there is no mutation here!)
current :: rest
else
[] // generated all elements - return empty list once we're done
// generate list with 1, 2 and all other larger fibonaccis
let fibs = 1::2::(fibsRec 1 2)
Ответ 2
Другие сообщения сообщают вам, как писать цикл while с использованием рекурсивных функций. Это еще один способ, используя библиотеку Seq в F #:
// generate an infinite Fibonacci sequence
let fibSeq = Seq.unfold (fun (a,b) -> Some( a+b, (b, a+b) ) ) (0,1)
// take the first few numbers in the sequence and convert the sequence to a list
let fibList = fibSeq |> Seq.takeWhile (fun x -> x<=400 ) |> Seq.toList
для объяснения, пожалуйста, решение 2 в F # для проблем Эйлера проекта, где решены первые 50 проблем Эйлера. Думаю, вас будут интересовать эти решения.
Ответ 3
let rec fibSeq p0 p1 = seq {
yield p0
yield! fibSeq p1 (p0+p1)
}
Ответ 4
Здесь представлено бесконечное хвосто-рекурсивное решение с использованием выражений последовательности. Это довольно эффективно, производя 100 000-й срок всего за несколько секунд. Оператор "yield" аналогичен С# "yield return" и "yield!". оператор может быть прочитан как "yield all", где в С# вам нужно будет выполнить "foreach item... return return item".
https://stackoverflow.com/questions/2296664/code-chess-fibonacci-sequence/2892670#2892670
let fibseq =
let rec fibseq n1 n2 =
seq { let n0 = n1 + n2
yield n0
yield! fibseq n0 n1 }
seq { yield 1I ; yield 1I ; yield! (fibseq 1I 1I) }
let fibTake n = fibseq |> Seq.take n //the first n Fibonacci numbers
let fib n = fibseq |> Seq.nth (n-1) //the nth Fibonacci number
Этот подход похож на следующий в С# (который использует цикл while (true) вместо рекурсии):
Поиск последовательности Фибоначчи в С#. [Упражнение Эйлера Эйлера]
Ответ 5
Да, изменяемые переменные и в то время как циклы обычно являются хорошим признаком того, что ваш код не очень функциональный. Также ряд фибоначчи не начинается с 1,2 - он начинается с 0,1 или 1,1 в зависимости от того, кого вы спросите.
Вот как бы я это сделал:
let rec fabListHelper (a:int,b:int,n:int) =
if a+b < n then
a+b :: fabListHelper (b, a+b, n)
else
[];;
let fabList (n:int) = 0 :: 1 :: fabListHelper (0,1, n);;
(*> fabList 400;;
val it : int list = [0; 1; 1; 2; 3; 5; 8; 13; 21; 34; 55; 89; 144; 233; 377]*)
Ответ 6
Эта функция "fib" вернет список чисел Фибоначчи, которые не превышают 500
let rec fib a b =
let current = a + b
match current with
| _ when current >= 500 -> []
| _ -> current :: fib b current
let testFib = fib 1 2;;
Ответ 7
Еще один способ codata'ish:
let rec fib = seq {
yield! seq {0..1}
yield! Seq.map (fun(a,b)->a+b) <| Seq.zip fib (Seq.skip 1 fib)
}
let a = fib |> Seq.take 10 |> Seq.toList
Ответ 8
Один с массивом:
let fibonacci n = [|1..n|] |> Array.fold (fun (a,b) _ -> b, a + b) (0,1) |> fst
Ответ 9
Один с использованием агрегации (сложить):
let fib n =
[1..n] |> List.fold (fun ac _ -> (ac |> List.take 2 |> List.sum) :: ac) [1;1] |> List.rev
Ответ 10
Вот хорошая статья .Net Гуру Скотта Хансельмана о генерации ряда фибоначчи в F #
let rec fib n = if n < 2 then 1 else fib (n-2) + fib(n-1)
http://www.hanselman.com/blog/TheWeeklySourceCode13FibonacciEdition.aspx
Он также сравнивается с другими языками в качестве ссылки
Ответ 11
Отличное решение Scott Hanselman не сообщает о начале последовательности фибоначчи.
Итак, вот небольшое изменение в его решении, которое также сообщает 0.
Я использовал небольшой список от 0 до 10, чтобы отобразить первые 11 элементов последовательности.
let nums=[0..10]
let rec fib n = if n < 1 then 0 else if n < 2 then 1 else fib (n-2) + fib(n-1)
let finres = List.map fib nums
printfn "%A" finres
Я новый и некомпетентный по отношению к f # и до сих пор не полностью полностью понимающий его потребность. Но нашел это интересным тестом.
Просто для удовольствия: если найдена формула Бине, которая вычисляет n-е число Фибоначчи.
К сожалению, некоторые функции с плавающей запятой необходимы для получения целочисленного результата в конце:
[Формула Фибоначчи Бине] [1]
http://i.stack.imgur.com/nMkxf.png
let fib2 n = (1.0 / sqrt(5.0)) * ( (((1.0 + sqrt(5.0)) /2.0)**n) - (((1.0 - sqrt(5.0)) /2.0)**n) )
let fib2res = fib2 10.0
System.Console.WriteLine(fib2res)
let strLine = System.Console.ReadLine()
Быстрый и грязный перевод в f # будет таким, как показано выше. Я уверен, что другие могут улучшить это в вопросе стиля и эффективности. В примере вычисляется 10-й номер. Результат будет 55.