Fsharp: sequence
ai answer
xtodoF# Sequences (Seq) Tutorial
Sequences in F# (seq<'T>) are lazy, forward-only collections that represent a series of values. They are one of the most powerful and memory-efficient data structures in F#.
1. What is a Sequence?
- Lazy evaluation: Elements are computed only when needed (on-demand).
- Forward-only: You typically iterate once (no random access like lists).
- Infinite sequences are possible.
- Under the hood:
seq<'T>is an alias forSystem.Collections.Generic.IEnumerable<'T>.
Comparison with other collections:
| Feature | seq<'T> |
list<'T> |
array<'T> |
|---|---|---|---|
| Evaluation | Lazy | Eager | Eager |
| Memory | Low | Medium | Low |
| Access | Forward-only | Random | Random |
| Best for | Large/infinite data, streaming | Small fixed data | Performance-critical |
2. Creating Sequences
Sequence Expressions (most common)
// Empty sequence let emptySeq = Seq.empty<int> // Simple sequence let numbers = seq { 1; 2; 3; 4; 5 } // Range let range1 = seq { 1 .. 10 } let range2 = seq { 1 .. 2 .. 20 } // step by 2 // With yield let squares = seq { for i in 1..10 do yield i * i } // Or using yield! (for composing) let combined = seq { yield! seq { 1..5 } yield! seq { 10..12 } }
From other collections
let fromList = List.toSeq [1; 2; 3] let fromArray = Array.toSeq [|1; 2; 3|] let fromString = "hello" |> Seq.toArray // char sequence
Infinite sequences
// Natural numbers let naturals = Seq.initInfinite (fun i -> i + 1) // Fibonacci let fibs = Seq.unfold (fun (a, b) -> Some(a, (b, a + b))) (0, 1)
3. Core Operations
Transformation
let nums = seq { 1..10 } nums |> Seq.map (fun x -> x * x) // squares nums |> Seq.filter (fun x -> x % 2 = 0) // evens nums |> Seq.choose (fun x -> if x % 2 = 0 then Some(x) else None)
Aggregation
Seq.sum nums Seq.length nums // Note: forces evaluation Seq.fold (+) 0 nums Seq.reduce (+) nums // same as fold but no initial value
Taking / Skipping
Seq.take 5 nums // first 5 Seq.skip 3 nums // skip first 3 Seq.takeWhile (fun x -> x < 6) nums Seq.skipWhile (fun x -> x < 6) nums
Pairwise & Grouping
Seq.pairwise nums // [(1,2); (2,3); ...] Seq.windowed 3 nums // sliding window of 3 Seq.groupBy (fun x -> x % 3) nums
Sorting & Distinct
Seq.sort nums Seq.sortBy (fun x -> -x) nums // descending Seq.distinct (seq {1; 2; 2; 3; 3})
4. Lazy Evaluation in Action
let expensive = seq { printfn "Computing..." for i in 1..1000000 do yield i } // Nothing happens yet let firstFew = expensive |> Seq.take 5 |> Seq.toList // "Computing..." prints only when we force evaluation
Important: Many operations are lazy. Use Seq.toList, Seq.toArray, or Seq.iter to force evaluation.
5. Common Patterns & Idioms
Generating data
// All primes (naive) let isPrime n = seq { 2 .. int(sqrt(float n)) } |> Seq.forall (fun d -> n % d <> 0) let primes = Seq.initInfinite (fun i -> i + 2) |> Seq.filter isPrime
Processing files lazily
open System.IO let lines = File.ReadLines("large.log") // returns seq<string> lines |> Seq.filter (fun line -> line.Contains "ERROR") |> Seq.take 10 |> Seq.iter (printfn "%s")
Chaining operations (pipeline style)
seq { 1..100 } |> Seq.filter (fun x -> x % 3 = 0) |> Seq.map (fun x -> x * x) |> Seq.sum
6. Performance Tips
- Avoid
Seq.lengthon large/infinite sequences — it forces full evaluation. - Prefer
Seq.iterover converting to list when possible. - Use
Seq.cacheif you need to iterate multiple times:
let cached = expensive |> Seq.cache
- Be careful with side effects in lazy sequences.
7. Advanced: Custom Sequence Generators
let rec allFiles dir = seq { yield! Directory.GetFiles(dir) for subdir in Directory.GetDirectories(dir) do yield! allFiles subdir }
8. When to Use seq vs list
-
Use
seqfor:- Large datasets
- Streaming / I/O
- Infinite or very large computed series
- One-pass processing
-
Use
listfor:- Small, fixed collections
- When you need frequent random access
- Recursive algorithms (pattern matching is nicer)
Practice Exercises
- Generate the first 10 even numbers using
Seq.init. - Write a function that returns the first
nFibonacci numbers. - Process a large CSV lazily and compute average of a column.
- Create a sequence of all Pythagorean triples where
a² + b² = c²witha,b,c ≤ 100.
Next steps: Explore Seq.zip, Seq.collect, Seq.concat, and the Seq module documentation on Microsoft Learn.
Would you like me to expand on any section (e.g., more examples, performance comparison, or integration with async sequences seq { ... } + task)?