From aa63838591e12a7c138e5a430ba1ab358606e2d9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 10 Mar 2026 13:06:01 +0000 Subject: [PATCH 1/2] feat: add map, filter, iter, exists, forall, toList, toArray to Queue and Deque modules Adds commonly needed collection functions to Queue and Deque that were present in F# List but missing from these modules. Addresses part of #152 (Align Collection Module functions with FSharp.Collections). New functions for both Queue and Deque: - map : transform each element, returning the same collection type - filter : keep elements matching a predicate - iter : apply a side-effecting function to each element (FIFO order) - exists : test whether any element satisfies a predicate - forall : test whether all elements satisfy a predicate - toList : convert to list in FIFO order - toArray : convert to array in FIFO order All seven functions are O(n) and preserve FIFO element order. 21 new tests added (QueueTest and DequeTest), all passing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/FSharpx.Collections/Deque.fs | 29 +++++ src/FSharpx.Collections/Deque.fsi | 107 +++++++++++-------- src/FSharpx.Collections/Queue.fs | 49 +++++++++ src/FSharpx.Collections/Queue.fsi | 73 ++++++++----- tests/FSharpx.Collections.Tests/DequeTest.fs | 62 ++++++++++- tests/FSharpx.Collections.Tests/QueueTest.fs | 67 +++++++++++- 6 files changed, 316 insertions(+), 71 deletions(-) diff --git a/src/FSharpx.Collections/Deque.fs b/src/FSharpx.Collections/Deque.fs index 2a056164..d6f852a8 100644 --- a/src/FSharpx.Collections/Deque.fs +++ b/src/FSharpx.Collections/Deque.fs @@ -244,5 +244,34 @@ module Deque = let inline toSeq(q: Deque<'T>) = q :> seq<'T> + ///O(n). Returns a list of the deque elements in FIFO order. + let toList(q: Deque<'T>) : 'T list = + q.front @ List.rev q.rBack + + ///O(n). Returns an array of the deque elements in FIFO order. + let toArray(q: Deque<'T>) : 'T[] = + Array.ofSeq q + + ///O(n). Returns a new deque whose elements are the results of applying the given function to each element. + let map (f: 'T -> 'U) (q: Deque<'T>) : Deque<'U> = + Deque<'U>(List.map f q.front, List.map f q.rBack) + + ///O(n). Returns a new deque containing only the elements of the original for which the given predicate returns true. + let filter (predicate: 'T -> bool) (q: Deque<'T>) : Deque<'T> = + Deque<'T>(List.filter predicate q.front, List.filter predicate q.rBack) + + ///O(n). Applies the given function to each element of the deque. + let iter (f: 'T -> unit) (q: Deque<'T>) = + List.iter f q.front + List.iter f (List.rev q.rBack) + + ///O(n). Returns true if any element of the deque satisfies the given predicate. + let exists (predicate: 'T -> bool) (q: Deque<'T>) : bool = + List.exists predicate q.front || List.exists predicate q.rBack + + ///O(n). Returns true if all elements of the deque satisfy the given predicate. + let forall (predicate: 'T -> bool) (q: Deque<'T>) : bool = + List.forall predicate q.front && List.forall predicate q.rBack + let inline tryUnconj(q: Deque<'T>) = q.TryUnconj diff --git a/src/FSharpx.Collections/Deque.fsi b/src/FSharpx.Collections/Deque.fsi index 6a3da772..cfa70b4b 100644 --- a/src/FSharpx.Collections/Deque.fsi +++ b/src/FSharpx.Collections/Deque.fsi @@ -12,136 +12,157 @@ type Deque<'T> = interface System.Collections.Generic.IReadOnlyCollection<'T> ///O(1). Returns a new deque with the element added to the end. - member Conj : 'T -> Deque<'T> + member Conj: 'T -> Deque<'T> ///O(1). Returns a new deque with the element added to the beginning. - member Cons : 'T -> Deque<'T> + member Cons: 'T -> Deque<'T> ///O(1) amortized, O(n), worst case. Returns the first element. - member Head : 'T + member Head: 'T ///O(1) amortized, O(n), worst case. Returns option first element. - member TryHead : 'T option + member TryHead: 'T option ///O(1) amortized, O(n), worst case. Returns a new deque of the elements before the last element. - member Initial : Deque<'T> + member Initial: Deque<'T> ///O(1) amortized, O(n), worst case. Returns a new deque of the elements before the last element. - member TryInitial : Deque<'T> option + member TryInitial: Deque<'T> option ///O(1). Returns true if the deque has no elements. - member IsEmpty : bool + member IsEmpty: bool ///O(1) amortized, O(n), worst case. Returns the last element. - member Last : 'T + member Last: 'T ///O(1) amortized, O(n), worst case. Returns option last element. - member TryLast : 'T option + member TryLast: 'T option ///O(1). Returns the count of elememts. - member Length : int + member Length: int ///O(1). Returns deque reversed. - member Rev : Deque<'T> + member Rev: Deque<'T> ///O(1) amortized, O(n), worst case. Returns a new deque of the elements trailing the first element. - member Tail : Deque<'T> + member Tail: Deque<'T> ///O(1) amortized, O(n), worst case. Returns option deque of the elements trailing the first element. - member TryTail : Deque<'T> option + member TryTail: Deque<'T> option ///O(1) amortized, O(n), worst case. Returns init and the last element. - member Unconj : Deque<'T> * 'T + member Unconj: Deque<'T> * 'T ///O(1) amortized, O(n), worst case. Returns option init and the last element. - member TryUnconj : (Deque<'T> * 'T) option + member TryUnconj: (Deque<'T> * 'T) option ///O(1) amortized, O(n), worst case. Returns the first element and tail. - member Uncons : 'T * Deque<'T> + member Uncons: 'T * Deque<'T> ///O(1) amortized, O(n), worst case. Returns option first element and tail. - member TryUncons : ('T * Deque<'T>) option + member TryUncons: ('T * Deque<'T>) option [] module Deque = //pattern discriminators - val (|Cons|Nil|) : Deque<'T> -> Choice<('T * Deque<'T>),unit> + val (|Cons|Nil|): Deque<'T> -> Choice<('T * Deque<'T>), unit> - val (|Conj|Nil|) : Deque<'T> -> Choice<(Deque<'T> * 'T),unit> + val (|Conj|Nil|): Deque<'T> -> Choice<(Deque<'T> * 'T), unit> ///O(1). Returns a new deque with the element added to the end. - val inline conj : 'T -> Deque<'T> -> Deque<'T> + val inline conj: 'T -> Deque<'T> -> Deque<'T> ///O(1). Returns a new deque with the element added to the beginning. - val inline cons : 'T -> Deque<'T> -> Deque<'T> + val inline cons: 'T -> Deque<'T> -> Deque<'T> ///O(1). Returns deque of no elements. [] val empty<'T> : Deque<'T> ///O(n). Applies a function to each element of the deque, threading an accumulator argument through the computation, left to right - val fold : ('State -> 'T -> 'State) -> 'State -> Deque<'T> -> 'State + val fold: ('State -> 'T -> 'State) -> 'State -> Deque<'T> -> 'State ///O(n). Applies a function to each element of the deque, threading an accumulator argument through the computation, right to left - val foldBack : ('T -> 'State -> 'State) -> Deque<'T> -> 'State -> 'State + val foldBack: ('T -> 'State -> 'State) -> Deque<'T> -> 'State -> 'State ///O(1) amortized, O(n), worst case. Returns the first element. - val inline head : Deque<'T> -> 'T + val inline head: Deque<'T> -> 'T ///O(1) amortized, O(n), worst case. Returns option first element. - val inline tryHead : Deque<'T> -> 'T option + val inline tryHead: Deque<'T> -> 'T option ///O(1) amortized, O(n), worst case. Returns a new deque of the elements before the last element. - val inline initial : Deque<'T> -> Deque<'T> + val inline initial: Deque<'T> -> Deque<'T> ///O(1) amortized, O(n), worst case. Returns option deque of the elements before the last element. - val inline tryInitial : Deque<'T> -> Deque<'T> option + val inline tryInitial: Deque<'T> -> Deque<'T> option ///O(1). Returns true if the deque has no elements. - val inline isEmpty : Deque<'T> -> bool + val inline isEmpty: Deque<'T> -> bool ///O(1) amortized, O(n), worst case. Returns the last element. - val inline last : Deque<'T> -> 'T + val inline last: Deque<'T> -> 'T ///O(1) amortized, O(n), worst case. Returns option last element. - val inline tryLast : Deque<'T> -> 'T option + val inline tryLast: Deque<'T> -> 'T option ///O(1). Returns the count of elememts. - val inline length : Deque<'T> -> int + val inline length: Deque<'T> -> int ///O(n), worst case. Returns a deque of the two lists concatenated. - val ofCatLists : 'T list -> 'T list -> Deque<'T> + val ofCatLists: 'T list -> 'T list -> Deque<'T> ///O(n), worst case. Returns a deque of the list. - val ofList : 'T list -> Deque<'T> + val ofList: 'T list -> Deque<'T> ///O(n), worst case. Returns a deque of the seq. - val ofSeq : seq<'T> -> Deque<'T> + val ofSeq: seq<'T> -> Deque<'T> ///O(1). Returns deque reversed. - val inline rev : Deque<'T> -> Deque<'T> + val inline rev: Deque<'T> -> Deque<'T> ///O(1). Returns a deque of one element. - val singleton : 'T -> Deque<'T> + val singleton: 'T -> Deque<'T> ///O(1) amortized, O(n), worst case. Returns a new deque of the elements trailing the first element. - val inline tail : Deque<'T> -> Deque<'T> + val inline tail: Deque<'T> -> Deque<'T> ///O(1) amortized, O(n), worst case. Returns option deque of the elements trailing the first element. - val inline tryTail : Deque<'T> -> Deque<'T> option + val inline tryTail: Deque<'T> -> Deque<'T> option ///O(1) amortized, O(n), worst case. Returns init and the last element. - val inline unconj : Deque<'T> -> Deque<'T> * 'T + val inline unconj: Deque<'T> -> Deque<'T> * 'T ///O(1) amortized, O(n), worst case. Returns option init and the last element. - val inline tryUnconj : Deque<'T> -> (Deque<'T> * 'T) option + val inline tryUnconj: Deque<'T> -> (Deque<'T> * 'T) option ///O(1) amortized, O(n), worst case. Returns the first element and tail. - val inline uncons : Deque<'T> -> 'T * Deque<'T> + val inline uncons: Deque<'T> -> 'T * Deque<'T> ///O(n). Views the given deque as a sequence. - val inline toSeq : Deque<'T> -> seq<'T> + val inline toSeq: Deque<'T> -> seq<'T> + + ///O(n). Returns a list of the deque elements in FIFO order. + val toList: Deque<'T> -> 'T list + + ///O(n). Returns an array of the deque elements in FIFO order. + val toArray: Deque<'T> -> 'T[] + + ///O(n). Returns a new deque whose elements are the results of applying the given function to each element. + val map: ('T -> 'U) -> Deque<'T> -> Deque<'U> + + ///O(n). Returns a new deque containing only the elements for which the given predicate returns true. + val filter: ('T -> bool) -> Deque<'T> -> Deque<'T> + + ///O(n). Applies the given function to each element of the deque. + val iter: ('T -> unit) -> Deque<'T> -> unit + + ///O(n). Returns true if any element of the deque satisfies the given predicate. + val exists: ('T -> bool) -> Deque<'T> -> bool + + ///O(n). Returns true if all elements of the deque satisfy the given predicate. + val forall: ('T -> bool) -> Deque<'T> -> bool ///O(1) amortized, O(n), worst case. Returns option first element and tail. - val inline tryUncons : Deque<'T> -> ('T * Deque<'T>) option + val inline tryUncons: Deque<'T> -> ('T * Deque<'T>) option diff --git a/src/FSharpx.Collections/Queue.fs b/src/FSharpx.Collections/Queue.fs index c4a5b218..f1bba106 100644 --- a/src/FSharpx.Collections/Queue.fs +++ b/src/FSharpx.Collections/Queue.fs @@ -148,6 +148,55 @@ module Queue = let inline toSeq(q: Queue<'T>) = q :> seq<'T> + ///O(n). Returns a list of the queue elements in FIFO order. + let toList(q: Queue<'T>) : 'T list = + q.front @ List.rev q.rBack + + ///O(n). Returns an array of the queue elements in FIFO order. + let toArray(q: Queue<'T>) : 'T[] = + let arr = Array.zeroCreate q.Length + let mutable i = 0 + + List.iter + (fun x -> + arr.[i] <- x + i <- i + 1) + q.front + + List.iter + (fun x -> + arr.[i] <- x + i <- i + 1) + (List.rev q.rBack) + + arr + + ///O(n). Returns a new queue whose elements are the results of applying the given function to each element. + let map (f: 'T -> 'U) (q: Queue<'T>) : Queue<'U> = + Queue<'U>(List.map f q.front, List.map f q.rBack) + + ///O(n). Returns a new queue containing only the elements of the original for which the given predicate returns true. + let filter (predicate: 'T -> bool) (q: Queue<'T>) : Queue<'T> = + let f = List.filter predicate q.front + let r = List.filter predicate q.rBack + + match f with + | [] -> Queue<'T>(List.rev r, []) + | _ -> Queue<'T>(f, r) + + ///O(n). Applies the given function to each element of the queue. + let iter (f: 'T -> unit) (q: Queue<'T>) = + List.iter f q.front + List.iter f (List.rev q.rBack) + + ///O(n). Returns true if any element of the queue satisfies the given predicate. + let exists (predicate: 'T -> bool) (q: Queue<'T>) : bool = + List.exists predicate q.front || List.exists predicate q.rBack + + ///O(n). Returns true if all elements of the queue satisfy the given predicate. + let forall (predicate: 'T -> bool) (q: Queue<'T>) : bool = + List.forall predicate q.front && List.forall predicate q.rBack + let inline uncons(q: Queue<'T>) = q.Uncons let inline tryUncons(q: Queue<'T>) = diff --git a/src/FSharpx.Collections/Queue.fsi b/src/FSharpx.Collections/Queue.fsi index f5d05014..649e43d3 100644 --- a/src/FSharpx.Collections/Queue.fsi +++ b/src/FSharpx.Collections/Queue.fsi @@ -14,85 +14,106 @@ type Queue<'T> = interface System.Collections.Generic.IReadOnlyCollection<'T> ///O(1). Returns a new queue with the element added to the end. (Enqueue) - member Conj : 'T -> Queue<'T> + member Conj: 'T -> Queue<'T> ///O(1). Returns the first element. (Peek) - member Head : 'T + member Head: 'T ///O(1). Returns option first element - member TryHead : 'T option + member TryHead: 'T option ///O(1). Returns true if the queue has no elements. - member IsEmpty : bool + member IsEmpty: bool ///O(1). Returns the count of elememts. - member Length : int + member Length: int ///O(n). Returns queue reversed. - member Rev : unit -> Queue<'T> + member Rev: unit -> Queue<'T> ///O(1) amortized, O(n) worst-case. Returns a new queue of the elements trailing the first element. (Dequeue) - member Tail : Queue<'T> + member Tail: Queue<'T> ///O(1) amortized, O(n) worst-case. Returns option queue of the elements trailing the first element. - member TryTail : Queue<'T> option + member TryTail: Queue<'T> option ///O(1) amortized, O(n) worst-case. Returns the first element and tail. - member Uncons : 'T * Queue<'T> + member Uncons: 'T * Queue<'T> ///O(1) amortized, O(n) worst-case. Returns option first element and tail. - member TryUncons : ('T * Queue<'T>) option + member TryUncons: ('T * Queue<'T>) option [] module Queue = //pattern discriminators (active pattern) - val (|Cons|Nil|) : Queue<'T> -> Choice<('T * Queue<'T>),unit> + val (|Cons|Nil|): Queue<'T> -> Choice<('T * Queue<'T>), unit> ///O(1). Returns a new queue with the element added to the end. (enqueue) - val inline conj : 'T -> Queue<'T> -> Queue<'T> + val inline conj: 'T -> Queue<'T> -> Queue<'T> ///O(1). Returns queue of no elements. [] val empty<'T> : Queue<'T> ///O(n). Applies a function to each element of the queue, threading an accumulator argument through the computation, left to right. - val fold : ('State -> 'T -> 'State) -> 'State -> Queue<'T> -> 'State + val fold: ('State -> 'T -> 'State) -> 'State -> Queue<'T> -> 'State ///O(n). Applies a function to each element of the queue, threading an accumulator argument through the computation, right to left. - val foldBack : ('T -> 'State -> 'State) -> Queue<'T> -> 'State -> 'State + val foldBack: ('T -> 'State -> 'State) -> Queue<'T> -> 'State -> 'State ///O(1). Returns the first element. (peek) - val inline head : Queue<'T> -> 'T + val inline head: Queue<'T> -> 'T ///O(1). Returns option first element. - val inline tryHead : Queue<'T> -> 'T option + val inline tryHead: Queue<'T> -> 'T option ///O(1). Returns true if the queue has no elements. - val inline isEmpty : Queue<'T> -> bool + val inline isEmpty: Queue<'T> -> bool ///O(1). Returns the count of elememts. - val inline length : Queue<'T> -> int + val inline length: Queue<'T> -> int ///O(1). Returns a queue of the list - val ofList : list<'T> -> Queue<'T> + val ofList: list<'T> -> Queue<'T> ///O(n). Returns a queue of the seq. - val ofSeq : seq<'T> -> Queue<'T> + val ofSeq: seq<'T> -> Queue<'T> ///O(n). Returns queue reversed. - val inline rev : Queue<'T> -> Queue<'T> + val inline rev: Queue<'T> -> Queue<'T> ///O(1) amortized, O(n) worst-case. Returns a new queue of the elements trailing the first element. (dequeue) - val inline tail : Queue<'T> -> Queue<'T> + val inline tail: Queue<'T> -> Queue<'T> ///O(1) amortized, O(n) worst-case. Returns option queue of the elements trailing the first element - val inline tryTail : Queue<'T> -> Queue<'T> option + val inline tryTail: Queue<'T> -> Queue<'T> option ///O(n). Views the given queue as a sequence. - val inline toSeq : Queue<'T> -> seq<'T> + val inline toSeq: Queue<'T> -> seq<'T> + + ///O(n). Returns a list of the queue elements in FIFO order. + val toList: Queue<'T> -> 'T list + + ///O(n). Returns an array of the queue elements in FIFO order. + val toArray: Queue<'T> -> 'T[] + + ///O(n). Returns a new queue whose elements are the results of applying the given function to each element. + val map: ('T -> 'U) -> Queue<'T> -> Queue<'U> + + ///O(n). Returns a new queue containing only the elements for which the given predicate returns true. + val filter: ('T -> bool) -> Queue<'T> -> Queue<'T> + + ///O(n). Applies the given function to each element of the queue. + val iter: ('T -> unit) -> Queue<'T> -> unit + + ///O(n). Returns true if any element of the queue satisfies the given predicate. + val exists: ('T -> bool) -> Queue<'T> -> bool + + ///O(n). Returns true if all elements of the queue satisfy the given predicate. + val forall: ('T -> bool) -> Queue<'T> -> bool ///O(1) amortized, O(n) worst-case. Returns the first element and tail. - val inline uncons : Queue<'T> -> 'T * Queue<'T> + val inline uncons: Queue<'T> -> 'T * Queue<'T> ///O(1) amortized, O(n) worst-case. Returns option first element and tail. - val inline tryUncons : Queue<'T> -> ('T * Queue<'T>) option + val inline tryUncons: Queue<'T> -> ('T * Queue<'T>) option diff --git a/tests/FSharpx.Collections.Tests/DequeTest.fs b/tests/FSharpx.Collections.Tests/DequeTest.fs index 153366db..4ad06670 100644 --- a/tests/FSharpx.Collections.Tests/DequeTest.fs +++ b/tests/FSharpx.Collections.Tests/DequeTest.fs @@ -1221,4 +1221,64 @@ module DequeTests = config10k "get Deque.initial from deque safely 2" (Prop.forAll(Arb.fromGen intGensStart2.[2]) - <| fun (q, l) -> List.ofSeq q.TryInitial.Value = (List.rev l |> List.tail |> List.rev)) ] + <| fun (q, l) -> List.ofSeq q.TryInitial.Value = (List.rev l |> List.tail |> List.rev)) + + test "toList preserves FIFO order" { + let q = Deque.ofSeq [ 1; 2; 3; 4; 5 ] + Expect.equal "toList" [ 1; 2; 3; 4; 5 ] (Deque.toList q) + } + + test "toList empty deque" { Expect.equal "toList empty" [] (Deque.toList Deque.empty) } + + test "toArray preserves FIFO order" { + let q = Deque.ofSeq [ 1; 2; 3 ] + Expect.equal "toArray" [| 1; 2; 3 |] (Deque.toArray q) + } + + test "map transforms elements" { + let q = Deque.ofSeq [ 1; 2; 3 ] + Expect.equal "map" [ 2; 4; 6 ] (Deque.map ((*) 2) q |> Deque.toList) + } + + test "map preserves FIFO order" { + let q = Deque.ofSeq [ "a"; "b"; "c" ] |> Deque.conj "d" |> Deque.conj "e" + + Expect.equal "map order" [ "A"; "B"; "C"; "D"; "E" ] (Deque.map (fun (s: string) -> s.ToUpper()) q |> Deque.toList) + } + + test "filter keeps matching elements" { + let q = Deque.ofSeq [ 1; 2; 3; 4; 5; 6 ] + Expect.equal "filter even" [ 2; 4; 6 ] (Deque.filter (fun x -> x % 2 = 0) q |> Deque.toList) + } + + test "filter preserves order" { + let q = Deque.ofSeq [ 1; 2; 3; 4; 5 ] + Expect.equal "filter order" [ 1; 3; 5 ] (Deque.filter (fun x -> x % 2 <> 0) q |> Deque.toList) + } + + test "iter visits each element in FIFO order" { + let q = Deque.ofSeq [ 1; 2; 3 ] + let result = System.Collections.Generic.List() + Deque.iter result.Add q + Expect.equal "iter order" [ 1; 2; 3 ] (List.ofSeq result) + } + + test "exists returns true when element satisfies predicate" { + let q = Deque.ofSeq [ 1; 2; 3 ] + Expect.isTrue "exists" (Deque.exists ((=) 2) q) + } + + test "exists returns false when no element satisfies predicate" { + let q = Deque.ofSeq [ 1; 2; 3 ] + Expect.isFalse "exists false" (Deque.exists ((=) 99) q) + } + + test "forall returns true when all elements satisfy predicate" { + let q = Deque.ofSeq [ 2; 4; 6 ] + Expect.isTrue "forall" (Deque.forall (fun x -> x % 2 = 0) q) + } + + test "forall returns false when some elements do not" { + let q = Deque.ofSeq [ 2; 3; 6 ] + Expect.isFalse "forall false" (Deque.forall (fun x -> x % 2 = 0) q) + } ] diff --git a/tests/FSharpx.Collections.Tests/QueueTest.fs b/tests/FSharpx.Collections.Tests/QueueTest.fs index b942dfd3..f7f4acba 100644 --- a/tests/FSharpx.Collections.Tests/QueueTest.fs +++ b/tests/FSharpx.Collections.Tests/QueueTest.fs @@ -316,4 +316,69 @@ module QueueTests = config10k "ofList build and serialize" (Prop.forAll(Arb.fromGen queueOfListGen) - <| fun (q, l) -> q |> Seq.toList = l) ] + <| fun (q, l) -> q |> Seq.toList = l) + + test "toList preserves FIFO order" { + let q = Queue.ofSeq [ 1; 2; 3; 4; 5 ] + Expect.equal "toList" [ 1; 2; 3; 4; 5 ] (Queue.toList q) + } + + test "toList empty queue" { Expect.equal "toList empty" [] (Queue.toList Queue.empty) } + + test "toArray preserves FIFO order" { + let q = Queue.ofSeq [ 1; 2; 3 ] + Expect.equal "toArray" [| 1; 2; 3 |] (Queue.toArray q) + } + + test "map transforms elements" { + let q = Queue.ofSeq [ 1; 2; 3 ] + Expect.equal "map" [ 2; 4; 6 ] (Queue.map ((*) 2) q |> Queue.toList) + } + + test "map preserves FIFO order across front/rBack boundary" { + let q = Queue.ofSeq [ "a"; "b"; "c" ] |> Queue.conj "d" |> Queue.conj "e" + + Expect.equal "map order" [ "A"; "B"; "C"; "D"; "E" ] (Queue.map (fun (s: string) -> s.ToUpper()) q |> Queue.toList) + } + + test "filter keeps matching elements" { + let q = Queue.ofSeq [ 1; 2; 3; 4; 5; 6 ] + Expect.equal "filter even" [ 2; 4; 6 ] (Queue.filter (fun x -> x % 2 = 0) q |> Queue.toList) + } + + test "filter preserves order" { + let q = Queue.ofSeq [ 1; 2; 3; 4; 5 ] + Expect.equal "filter preserves order" [ 1; 3; 5 ] (Queue.filter (fun x -> x % 2 <> 0) q |> Queue.toList) + } + + test "filter all out gives empty" { + let q = Queue.ofSeq [ 1; 2; 3 ] + Expect.isTrue "filter all out" (Queue.filter (fun _ -> false) q |> Queue.isEmpty) + } + + test "iter visits each element in FIFO order" { + let q = Queue.ofSeq [ 1; 2; 3 ] + let result = System.Collections.Generic.List() + Queue.iter result.Add q + Expect.equal "iter order" [ 1; 2; 3 ] (List.ofSeq result) + } + + test "exists returns true when element satisfies predicate" { + let q = Queue.ofSeq [ 1; 2; 3 ] + Expect.isTrue "exists" (Queue.exists ((=) 2) q) + } + + test "exists returns false when no element satisfies predicate" { + let q = Queue.ofSeq [ 1; 2; 3 ] + Expect.isFalse "exists false" (Queue.exists ((=) 99) q) + } + + test "forall returns true when all elements satisfy predicate" { + let q = Queue.ofSeq [ 2; 4; 6 ] + Expect.isTrue "forall" (Queue.forall (fun x -> x % 2 = 0) q) + } + + test "forall returns false when some elements do not satisfy predicate" { + let q = Queue.ofSeq [ 2; 3; 6 ] + Expect.isFalse "forall false" (Queue.forall (fun x -> x % 2 = 0) q) + } ] From c4220d3d13cc7676f2800cdc60efe6a8c09032ee Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 10 Mar 2026 13:12:24 +0000 Subject: [PATCH 2/2] ci: trigger checks