19

VS2010を使用してF#を学び始めたばかりです。以下は、フィボナッチ数列を生成する最初の試みです。私がやろうとしているのは、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

私の最初の問題は、最後のステートメントで、最後の行に「式のこのポイントまたはそれ以前の不完全な構造化構造」というエラーメッセージが表示されることです。ここで何が間違っているのかわかりません。

これは(c ++ / C#プログラマーから)かなり効率的な方法でリストを作成するための明白な方法のようですが、私がf#についてほとんど知らないことから、これはプログラムを実行する正しい方法ではないようです。 。私はこの気持ちで正しいですか?

4

11 に答える 11

51

他の投稿では、再帰関数を使用してwhileループを作成する方法を説明しています。これは、F#でSeqライブラリを使用する別の方法です。

// 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

説明については、プロジェクトオイラー問題のF#のソリューション2を参照してください。ここでは、最初の50オイラー問題が解決されています。私はあなたがこれらの解決策に興味があると思います。

于 2010-05-17T00:47:23.617 に答える
27

まず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

コードの2番目の問題は、要素を追加して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)
于 2010-05-16T22:43:14.993 に答える
14
let rec fibSeq p0 p1 = seq {
    yield p0
    yield! fibSeq p1 (p0+p1)
}
于 2013-03-21T07:47:47.533 に答える
7

これは、シーケンス式を使用した無限末尾再帰ソリューションです。それは非常に効率的で、わずか数秒で100,000番目の用語を生成します。「yield」演算子は、C#の「yieldreturn」や「yield!」と同じです。演算子は「yieldall」と読み替えることができますが、C#では「foreachitem ...yieldreturnitem」を実行する必要があります。

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

このアプローチは、C#の次のようなものです(再帰の代わりにwhile(true)ループを使用します)。

C#でフィボナッチ数列を検索します。【プロジェクトオイラー演習】

于 2010-05-24T17:36:13.687 に答える
5

はい、可変変数とwhileループは通常、コードがあまり機能していないことを示す良い兆候です。また、フィボナッチ数列は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]*)
于 2010-05-16T22:42:19.370 に答える
2

集計(フォールド)を使用するもの:

let fib n = 
  [1..n] |> List.fold (fun ac _ -> (ac |> List.take 2 |> List.sum) :: ac) [1;1] |> List.rev
于 2019-01-11T03:38:40.457 に答える
1

配列を持つもの:

let fibonacci n = [|1..n|] |> Array.fold (fun (a,b) _ -> b, a + b) (0,1) |> fst
于 2015-05-07T12:18:21.653 に答える
1

この関数「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;;
于 2017-08-19T22:43:18.160 に答える
0

これは、F#でフィボナッチ数列を生成することに関する.Netの第一人者ScottHanselmanによる優れた記事です。

let rec fib n = if n < 2 then 1 else fib (n-2) + fib(n-1)

http://www.hanselman.com/blog/TheWeeklySourceCode13FibonacciEdition.aspx

また、参照として他の言語と比較します

于 2010-05-16T22:41:59.773 に答える
0

もう1つのコデータ風の方法:

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
于 2015-04-21T16:27:33.460 に答える
-1

スコット・ハンゼルマンの優れた解決策は、フィボナッチ数列が始まる0を報告しません。

そこで、0を報告するための彼のソリューションへのマイナーな変更があります。シーケンスの最初の11項目を表示するために、0から10までの小さなリストを使用しました。

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番目のフィボナッチ数を計算するBinetの式が見つかった場合。残念ながら、整数の結果を最後に戻すには、いくつかの浮動小数点関数が必要です。[Binetのフィボナッチ式] [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になります。

于 2012-12-22T17:57:15.793 に答える