# 序列

## 序列運算式

// Sequence that has an increment.
seq { 0 .. 10 .. 100 }


seq { for i in 1 .. 10 -> i * i }


seq { for i in 1 .. 10 do yield i * i }

// The 'yield' is implicit and doesn't need to be specified in most cases.
seq { for i in 1 .. 10 do i * i }


let (height, width) = (10, 10)

seq {
for row in 0 .. width - 1 do
for col in 0 .. height - 1 ->
(row, col, row*width + col)
}


if序列中使用的運算式是篩選準則。 例如，若要只產生質數序列，假設您有 類型的 int -> boolisprime 式，請建構序列，如下所示。

seq { for n in 1 .. 100 do if isprime n then n }


## yield! 關鍵字

// Repeats '1 2 3 4 5' ten times
seq {
for _ in 1..10 do
yield! seq { 1; 2; 3; 4; 5}
}


// Combine repeated values with their values
seq {
for x in 1..10 do
yield x
yield! seq { for i in 1..x -> i}
}


## 範例

// Recursive isprime function.
let isprime n =
let rec check i =
i > n/2 || (n % i <> 0 && check (i + 1))
check 2

let aSequence =
seq {
for n in 1..100 do
if isprime n then
n
}

for x in aSequence do
printfn "%d" x


let multiplicationTable =
seq {
for i in 1..9 do
for j in 1..9 ->
(i, j, i*j)
}


// Yield the values of a binary tree in a sequence.
type Tree<'a> =
| Tree of 'a * Tree<'a> * Tree<'a>
| Leaf of 'a

// inorder : Tree<'a> -> seq<'a>
let rec inorder tree =
seq {
match tree with
| Tree(x, left, right) ->
yield! inorder left
yield x
yield! inorder right
| Leaf x -> yield x
}

let mytree = Tree(6, Tree(2, Leaf(1), Leaf(3)), Leaf(9))
let seq1 = inorder mytree
printfn "%A" seq1


## 模組函式

FSharp.Collections 命名空間中的Seq 模組包含使用序列的函式。 這些函式也適用于清單、陣列、對應和集合，因為這些類型都是可列舉的，因此可以視為序列。

## 建立序列

let seqEmpty = Seq.empty
let seqOne = Seq.singleton 10


let seqFirst5MultiplesOf10 = Seq.init 5 (fun n -> n * 10)
Seq.iter (fun elem -> printf "%d " elem) seqFirst5MultiplesOf10


0 10 20 30 40


// Convert an array to a sequence by using a cast.
let seqFromArray1 = [| 1 .. 10 |] :> seq<int>

// Convert an array to a sequence by using Seq.ofArray.
let seqFromArray2 = [| 1 .. 10 |] |> Seq.ofArray


open System

let arr = ResizeArray<int>(10)

for i in 1 .. 10 do

let seqCast = Seq.cast arr


let seqInfinite =
Seq.initInfinite (fun index ->
let n = float (index + 1)
1.0 / (n * n * (if ((index + 1) % 2 = 0) then 1.0 else -1.0)))

printfn "%A" seqInfinite


Seq.展開 會從計算函式產生序列，該函式會接受狀態並加以轉換，以在序列中產生每個後續元素。 狀態只是用來計算每個元素的值，而且可以在計算每個元素時變更。 的第二個引數 Seq.unfold 是用來啟動序列的初始值。 Seq.unfold 會使用 狀態的選項類型，這可讓您藉由傳回 None 值來終止序列。 下列程式碼顯示作業所產生的兩個序列範例 和 seq1fibunfold 第一 seq1 個是簡單的序列，最多 20 個數字。 第二個 fib ， 會使用 unfold 來計算 Fibonacci 序列。 因為 Fibonacci 序列中的每個元素都是前兩個 Fibonacci 數位的總和，所以狀態值是一個 Tuple，其中包含序列中的前兩個數字。 初始值為 (1,1) ，序列中的前兩個數字。

let seq1 =
0 // Initial state
|> Seq.unfold (fun state ->
if (state > 20) then
None
else
Some(state, state + 1))

printfn "The sequence seq1 contains numbers from 0 to 20."

for x in seq1 do
printf "%d " x

let fib =
(1, 1) // Initial state
|> Seq.unfold (fun state ->
if (snd state > 1000) then
None
else
Some(fst state + snd state, (snd state, fst state + snd state)))

printfn "\nThe sequence fib contains Fibonacci numbers."
for x in fib do printf "%d " x


The sequence seq1 contains numbers from 0 to 20.

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

The sequence fib contains Fibonacci numbers.

2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597


// generateInfiniteSequence generates sequences of floating point
// numbers. The sequences generated are computed from the fDenominator
// function, which has the type (int -> float) and computes the
// denominator of each term in the sequence from the index of that
// term. The isAlternating parameter is true if the sequence has
// alternating signs.
let generateInfiniteSequence fDenominator isAlternating =
if (isAlternating) then
Seq.initInfinite (fun index ->
1.0 /(fDenominator index) * (if (index % 2 = 0) then -1.0 else 1.0))
else
Seq.initInfinite (fun index -> 1.0 /(fDenominator index))

// The harmonic alternating series is like the harmonic series
// except that it has alternating signs.
let harmonicAlternatingSeries = generateInfiniteSequence (fun index -> float index) true

// This is the series of reciprocals of the odd numbers.
let oddNumberSeries = generateInfiniteSequence (fun index -> float (2 * index - 1)) true

// This is the series of recipocals of the squares.
let squaresSeries = generateInfiniteSequence (fun index -> float (index * index)) false

// This function sums a sequence, up to the specified number of terms.
let sumSeq length sequence =
(0, 0.0)
|>
Seq.unfold (fun state ->
let subtotal = snd state + Seq.item (fst state + 1) sequence
if (fst state >= length) then
None
else
Some(subtotal, (fst state + 1, subtotal)))

// This function sums an infinite sequence up to a given value
// for the difference (epsilon) between subsequent terms,
// up to a maximum number of terms, whichever is reached first.
let infiniteSum infiniteSeq epsilon maxIteration =
infiniteSeq
|> sumSeq maxIteration
|> Seq.pairwise
|> Seq.takeWhile (fun elem -> abs (snd elem - fst elem) > epsilon)
|> List.ofSeq
|> List.rev
|> snd

// Compute the sums for three sequences that converge, and compare
// the sums to the expected theoretical values.
let result1 = infiniteSum harmonicAlternatingSeries 0.00001 100000
printfn "Result: %f  ln2: %f" result1 (log 2.0)

let pi = Math.PI
let result2 = infiniteSum oddNumberSeries 0.00001 10000
printfn "Result: %f pi/4: %f" result2 (pi/4.0)

// Because this is not an alternating series, a much smaller epsilon
// value and more terms are needed to obtain an accurate result.
let result3 = infiniteSum squaresSeries 0.0000001 1000000
printfn "Result: %f pi*pi/6: %f" result3 (pi*pi/6.0)


## 取得子序列

Seq.filterSeq.choose 就像清單可用的對應函式，不同之處在于在評估時序專案之前，不會進行篩選和選擇。

Seq.truncate 會從另一個序列建立序列，但會將序列限制為指定的專案數目。 Seq.take 會建立新的序列，其中只包含序列開頭的指定專案數目。 如果序列中的專案少於您指定的所要接受的專案， Seq.take 則會 System.InvalidOperationException 擲回 。 和 Seq.truncate 之間的差異 Seq.take 在於 Seq.truncate ，如果元素數目小於您指定的數目，則不會產生錯誤。

let mySeq = seq { for i in 1 .. 10 -> i*i }
let truncatedSeq = Seq.truncate 5 mySeq
let takenSeq = Seq.take 5 mySeq

let truncatedSeq2 = Seq.truncate 20 mySeq
let takenSeq2 = Seq.take 20 mySeq

let printSeq seq1 = Seq.iter (printf "%A ") seq1; printfn ""

// Up to this point, the sequences are not evaluated.
// The following code causes the sequences to be evaluated.
truncatedSeq |> printSeq
truncatedSeq2 |> printSeq
takenSeq |> printSeq
// The following line produces a run-time error (in printSeq):
takenSeq2 |> printSeq


1 4 9 16 25
1 4 9 16 25 36 49 64 81 100
1 4 9 16 25
1 4 9 16 25 36 49 64 81 100


// takeWhile
let mySeqLessThan10 = Seq.takeWhile (fun elem -> elem < 10) mySeq
mySeqLessThan10 |> printSeq

// skip
let mySeqSkipFirst5 = Seq.skip 5 mySeq
mySeqSkipFirst5 |> printSeq

// skipWhile
let mySeqSkipWhileLessThan10 = Seq.skipWhile (fun elem -> elem < 10) mySeq
mySeqSkipWhileLessThan10 |> printSeq


1 4 9
36 49 64 81 100
16 25 36 49 64 81 100


## 轉換序列

Seq.pairwise 會建立新的序列，其中輸入序列的後續元素會分組為 Tuple。

let printSeq seq1 = Seq.iter (printf "%A ") seq1; printfn ""
let seqPairwise = Seq.pairwise (seq { for i in 1 .. 10 -> i*i })
printSeq seqPairwise

printfn ""
let seqDelta = Seq.map (fun elem -> snd elem - fst elem) seqPairwise
printSeq seqDelta


Seq.windowed 就像 Seq.pairwise 是 ，不同之處在于它不會產生 Tuple 序列，而是會產生陣列序列，其中包含相鄰元素的複本， (序列中的視窗) 。 您可以指定每個陣列中想要的相鄰元素數目。

let seqNumbers = [ 1.0; 1.5; 2.0; 1.5; 1.0; 1.5 ] :> seq<float>
let seqWindows = Seq.windowed 3 seqNumbers
let seqMovingAverage = Seq.map Array.average seqWindows
printfn "Initial sequence: "
printSeq seqNumbers
printfn "\nWindows of length 3: "
printSeq seqWindows
printfn "\nMoving average: "
printSeq seqMovingAverage


1.0 1.5 2.0 1.5 1.0 1.5

Windows of length 3:
[|1.0; 1.5; 2.0|] [|1.5; 2.0; 1.5|] [|2.0; 1.5; 1.0|] [|1.5; 1.0; 1.5|]

Moving average:
1.5 1.666666667 1.5 1.333333333


## 具有多個序列的作業

Seq.zipSeq.zip3 會採用兩或三個序列，並產生 Tuple 序列。 這些函式 就像清單可用的對應函式。 沒有對應的功能，無法將一個序列分成兩個或多個序列。 如果您需要序列的這項功能，請將序列轉換成清單，並使用 List.unzip

## 排序、比較和群組

let sequence1 = seq { 1 .. 10 }
let sequence2 = seq { 10 .. -1 .. 1 }

// Compare two sequences element by element.
let compareSequences =
Seq.compareWith (fun elem1 elem2 ->
if elem1 > elem2 then 1
elif elem1 < elem2 then -1
else 0)

let compareResult1 = compareSequences sequence1 sequence2
match compareResult1 with
| 1 -> printfn "Sequence1 is greater than sequence2."
| -1 -> printfn "Sequence1 is less than sequence2."
| 0 -> printfn "Sequence1 is equal to sequence2."
| _ -> failwith("Invalid comparison result.")


Seq.countBy 會採用一個函式，以產生每個元素的 索引鍵稱為值 。 每個元素上呼叫這個函式，就會為每個元素產生索引鍵。 Seq.countBy 然後傳回包含索引鍵值的序列，以及產生索引鍵每個值的專案數目計數。

let mySeq1 = seq { 1.. 100 }

let printSeq seq1 = Seq.iter (printf "%A ") seq1

let seqResult =
mySeq1
|> Seq.countBy (fun elem ->
if elem % 3 = 0 then 0
elif elem % 3 = 1 then 1
else 2)

printSeq seqResult


(1, 34) (2, 33) (0, 33)


let sequence = seq { 1 .. 100 }

let printSeq seq1 = Seq.iter (printf "%A ") seq1

let sequences3 =
sequences
|> Seq.groupBy (fun index ->
if (index % 3 = 0) then 0
elif (index % 3 = 1) then 1
else 2)

sequences3 |> printSeq


(1, seq [1; 4; 7; 10; ...]) (2, seq [2; 5; 8; 11; ...]) (0, seq [3; 6; 9; 12; ...])


let binary n =
let rec generateBinary n =
if (n / 2 = 0) then [n]
else (n % 2) :: generateBinary (n / 2)

generateBinary n
|> List.rev
|> Seq.ofList

printfn "%A" (binary 1024)

let resultSequence = Seq.distinct (binary 1024)
printfn "%A" resultSequence


let inputSequence = { -5 .. 10 }
let printSeq seq1 = Seq.iter (printf "%A ") seq1

printfn "Original sequence: "
printSeq inputSequence

printfn "\nSequence with distinct absolute values: "
let seqDistinctAbsoluteValue = Seq.distinctBy (fun elem -> abs elem) inputSequence
printSeq seqDistinctAbsoluteValue


## 唯讀和快取序列

Seq.readonly 會建立序列的唯讀複本。 Seq.readonly 當您有讀寫集合，例如陣列，而且您不想修改原創組合時，會很有用。 此函式可用來保留資料封裝。 在下列程式碼範例中，會建立包含陣列的類型。 屬性會公開陣列，但不會傳回陣列，而是傳回使用 Seq.readonly 從陣列建立的序列。

type ArrayContainer(start, finish) =
let internalArray = [| start .. finish |]
member this.RangeArray = internalArray

let newArray = new ArrayContainer(1, 10)
let rangeSeq = newArray.RangeSeq
let rangeArray = newArray.RangeArray

// These lines produce an error:
//let myArray = rangeSeq :> int array
//myArray[0] <- 0

// The following line does not produce an error.
// It does not preserve encapsulation.
rangeArray[0] <- 0


Seq.cache 會建立序列的預存版本。 使用 Seq.cache 以避免重新評估序列，或當您有多個使用序列的執行緒時，但您必須確定每個元素只會在一次時採取動作。 當您有多個執行緒所使用的序列時，可以有一個執行緒來列舉和計算原始序列的值，而其餘執行緒可以使用快取序列。

## 在序列上執行計算

Seq.foldSeq.reduce 和 Seq.scan就像清單可用的對應函式一樣。 序列支援列出支援之這些函式完整變化的子集。 如需詳細資訊和範例，請參閱 清單