Skip to content

Commit 8740969

Browse files
committed
deploy: ce655c1
0 parents  commit 8740969

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+38333
-0
lines changed

.nojekyll

Whitespace-only changes.

Dockerfile

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
FROM mcr.microsoft.com/dotnet/sdk:7.0
2+
3+
RUN apt-get update \
4+
&& apt-get -y upgrade \
5+
&& apt-get -y install python3 python3-pip python3-dev ipython3
6+
7+
RUN python3 -m pip install --no-cache-dir notebook jupyterlab
8+
9+
ARG NB_USER=fsdocs-user
10+
ARG NB_UID=1000
11+
ENV USER ${NB_USER}
12+
ENV NB_UID ${NB_UID}
13+
ENV HOME /home/${NB_USER}
14+
15+
RUN adduser --disabled-password \
16+
--gecos "Default user" \
17+
--uid ${NB_UID} \
18+
${NB_USER}
19+
20+
COPY . ${HOME}
21+
USER root
22+
RUN chown -R ${NB_UID} ${HOME}
23+
USER ${NB_USER}
24+
25+
ENV PATH="${PATH}:$HOME/.dotnet/tools/"
26+
27+
RUN dotnet tool install --global Microsoft.dotnet-interactive --version 1.0.410202
28+
29+
RUN dotnet-interactive jupyter install

NuGet.config

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<configuration>
3+
<solution>
4+
<add key="disableSourceControlIntegration" value="true" />
5+
</solution>
6+
<packageSources>
7+
<clear />
8+
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
9+
<add key="dotnet3-dev" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet3.1/nuget/v3/index.json" />
10+
<add key="dotnet-core" value="https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json" />
11+
<add key="dotnet-tools" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json" />
12+
<add key="PSGallery" value="https://www.powershellgallery.com/api/v2/" />
13+
</packageSources>
14+
</configuration>

TaskSeqAdvanced.fsx

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
(**
2+
3+
*)
4+
#r "nuget: FSharp.Control.TaskSeq,1.0.0"
5+
(**
6+
# Advanced Task Sequence Operations
7+
8+
This page covers advanced `TaskSeq<'T>` operations: grouping, stateful transformation with
9+
`mapFold`, deduplication, set-difference, partitioning, counting by key, lexicographic
10+
comparison, cancellation, and positional editing.
11+
12+
*)
13+
open System.Threading
14+
open System.Threading.Tasks
15+
open FSharp.Control
16+
(**
17+
-----------------------
18+
19+
## groupBy and groupByAsync
20+
21+
`TaskSeq.groupBy` partitions a sequence into groups by a key-projection function. The result
22+
is an array of `(key, elements[])` pairs, one per distinct key, in order of first occurrence.
23+
24+
> **Note:** `groupBy` consumes the entire source before returning. Do not use it on
25+
potentially infinite sequences.
26+
>
27+
28+
*)
29+
type Event = { EntityId: int; Payload: string }
30+
31+
let events : TaskSeq<Event> =
32+
TaskSeq.ofList
33+
[ { EntityId = 1; Payload = "A" }
34+
{ EntityId = 2; Payload = "B" }
35+
{ EntityId = 1; Payload = "C" }
36+
{ EntityId = 3; Payload = "D" }
37+
{ EntityId = 2; Payload = "E" } ]
38+
39+
// groups: (1, [A;C]), (2, [B;E]), (3, [D])
40+
let grouped : Task<(int * Event[])[]> =
41+
events |> TaskSeq.groupBy (fun e -> e.EntityId)
42+
(**
43+
`TaskSeq.groupByAsync` accepts an async key projection:
44+
45+
*)
46+
let groupedAsync : Task<(int * Event[])[]> =
47+
events |> TaskSeq.groupByAsync (fun e -> task { return e.EntityId })
48+
(**
49+
-----------------------
50+
51+
## countBy and countByAsync
52+
53+
`TaskSeq.countBy` counts how many elements map to each key, returning `(key, count)[]`:
54+
55+
*)
56+
let counts : Task<(int * int)[]> =
57+
events |> TaskSeq.countBy (fun e -> e.EntityId)
58+
// (1,2), (2,2), (3,1)
59+
(**
60+
-----------------------
61+
62+
## mapFold and mapFoldAsync
63+
64+
`TaskSeq.mapFold` threads a state accumulator through a sequence while simultaneously mapping
65+
each element to a result value. The output is a task returning a pair of `(result[], finalState)`:
66+
67+
*)
68+
// Number each word sequentially while building a running concatenation
69+
let words : TaskSeq<string> =
70+
TaskSeq.ofList [ "hello"; "world"; "foo" ]
71+
72+
let numbered : Task<string[] * int> =
73+
words
74+
|> TaskSeq.mapFold (fun count w -> $"{count}: {w}", count + 1) 0
75+
76+
// result: ([| "0: hello"; "1: world"; "2: foo" |], 3)
77+
(**
78+
`TaskSeq.mapFoldAsync` is the same but the mapping function returns `Task<'Result * 'State>`.
79+
80+
-----------------------
81+
82+
## scan and scanAsync
83+
84+
`TaskSeq.scan` is the streaming sibling of `fold`: it emits each intermediate state as a new
85+
element, starting with the initial state:
86+
87+
*)
88+
let numbers : TaskSeq<int> = TaskSeq.ofSeq (seq { 1..5 })
89+
90+
let runningTotals : TaskSeq<int> =
91+
numbers |> TaskSeq.scan (fun acc n -> acc + n) 0
92+
93+
// yields: 0, 1, 3, 6, 10, 15
94+
(**
95+
-----------------------
96+
97+
## distinct and distinctBy
98+
99+
`TaskSeq.distinct` removes duplicates (keeps first occurrence), using generic equality:
100+
101+
*)
102+
let withDups : TaskSeq<int> = TaskSeq.ofList [ 1; 2; 2; 3; 1; 4 ]
103+
104+
let deduped : TaskSeq<int> = withDups |> TaskSeq.distinct // 1, 2, 3, 4
105+
(**
106+
`TaskSeq.distinctBy` deduplicates by a key projection:
107+
108+
*)
109+
let strings : TaskSeq<string> =
110+
TaskSeq.ofList [ "hello"; "HELLO"; "world"; "WORLD" ]
111+
112+
let caseInsensitiveDistinct : TaskSeq<string> =
113+
strings |> TaskSeq.distinctBy (fun s -> s.ToLowerInvariant())
114+
// "hello", "world"
115+
(**
116+
> **Note:** both `distinct` and `distinctBy` buffer all unique keys in a hash set. Do not use
117+
them on potentially infinite sequences.
118+
>
119+
120+
`TaskSeq.distinctByAsync` accepts an async key projection.
121+
122+
-----------------------
123+
124+
## distinctUntilChanged
125+
126+
`TaskSeq.distinctUntilChanged` removes consecutive duplicates only — it does not buffer the
127+
whole sequence, so it is safe on infinite streams:
128+
129+
*)
130+
let run : TaskSeq<int> = TaskSeq.ofList [ 1; 1; 2; 2; 2; 3; 1; 1 ]
131+
132+
let noConsecDups : TaskSeq<int> = run |> TaskSeq.distinctUntilChanged
133+
// 1, 2, 3, 1
134+
(**
135+
-----------------------
136+
137+
## except and exceptOfSeq
138+
139+
`TaskSeq.except itemsToExclude source` returns elements of `source` that do not appear in
140+
`itemsToExclude`. The exclusion set is materialised eagerly before iteration:
141+
142+
*)
143+
let exclusions : TaskSeq<int> = TaskSeq.ofList [ 2; 4 ]
144+
let source : TaskSeq<int> = TaskSeq.ofSeq (seq { 1..5 })
145+
146+
let filtered : TaskSeq<int> = TaskSeq.except exclusions source // 1, 3, 5
147+
(**
148+
`TaskSeq.exceptOfSeq` accepts a plain `seq<'T>` as the exclusion set.
149+
150+
-----------------------
151+
152+
## partition and partitionAsync
153+
154+
`TaskSeq.partition` splits the sequence into two arrays in a single pass. Elements for which
155+
the predicate returns `true` go into the first array; the rest into the second:
156+
157+
*)
158+
let partitioned : Task<int[] * int[]> =
159+
source |> TaskSeq.partition (fun n -> n % 2 = 0)
160+
// trueItems: [|2;4|] falseItems: [|1;3;5|]
161+
(**
162+
`TaskSeq.partitionAsync` accepts an async predicate.
163+
164+
-----------------------
165+
166+
## compareWith and compareWithAsync
167+
168+
`TaskSeq.compareWith` performs a lexicographic comparison of two sequences using a custom
169+
comparer. It returns the first non-zero comparison result, or `0` if the sequences are
170+
element-wise equal and have the same length:
171+
172+
*)
173+
let a : TaskSeq<int> = TaskSeq.ofList [ 1; 2; 3 ]
174+
let b : TaskSeq<int> = TaskSeq.ofList [ 1; 2; 4 ]
175+
176+
let cmp : Task<int> =
177+
TaskSeq.compareWith (fun x y -> compare x y) a b
178+
// negative (a < b)
179+
(**
180+
`TaskSeq.compareWithAsync` accepts an async comparer.
181+
182+
-----------------------
183+
184+
## withCancellation
185+
186+
`TaskSeq.withCancellation token source` injects a `CancellationToken` into the underlying
187+
`IAsyncEnumerable<'T>`. This is equivalent to calling `.WithCancellation(token)` in C# and
188+
is useful when consuming sequences from libraries (e.g. Entity Framework Core) that require a
189+
token at the enumeration site:
190+
191+
*)
192+
let cts = new CancellationTokenSource()
193+
194+
let cancellable : TaskSeq<int> =
195+
source |> TaskSeq.withCancellation cts.Token
196+
(**
197+
-----------------------
198+
199+
## Positional editing
200+
201+
`TaskSeq.insertAt`, `TaskSeq.insertManyAt`, `TaskSeq.removeAt`, `TaskSeq.removeManyAt`, and
202+
`TaskSeq.updateAt` produce new sequences with an element inserted, removed, or replaced at a
203+
given zero-based index:
204+
205+
*)
206+
let original : TaskSeq<int> = TaskSeq.ofList [ 1; 2; 4; 5 ]
207+
208+
let inserted : TaskSeq<int> = original |> TaskSeq.insertAt 2 3 // 1,2,3,4,5
209+
210+
let removed : TaskSeq<int> = original |> TaskSeq.removeAt 1 // 1,4,5
211+
212+
let updated : TaskSeq<int> = original |> TaskSeq.updateAt 0 99 // 99,2,4,5
213+
214+
let manyInserted : TaskSeq<int> =
215+
original
216+
|> TaskSeq.insertManyAt 2 (TaskSeq.ofList [ 10; 11 ])
217+
// 1, 2, 10, 11, 4, 5
218+
219+
let manyRemoved : TaskSeq<int> = original |> TaskSeq.removeManyAt 1 2 // 1, 5
220+

0 commit comments

Comments
 (0)