-
Notifications
You must be signed in to change notification settings - Fork 77
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add AA Tree and tests #203
base: master
Are you sure you want to change the base?
Conversation
Checking. |
/// A balanced binary tree similar to a red-black tree which may have less predictable performance. | ||
type AaTree<'T when 'T: comparison> = | ||
| E | ||
| T of int * AaTree<'T> * 'T * AaTree<'T> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider using names for the T case data as in the Shape example here - https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/discriminated-unions .
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea. Would be helpful when editing the tree itself to specify the different data types mean. (Added.)
testList "AaTree" [ | ||
|
||
(* Existence tests. *) | ||
test "test isEmpty" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we divide the tests to have test per assertion/aspect and explain in the test name which aspect of isEmpty
we are testing? Here I would go with something along the lines of "test isEmpty returns true for an empty aatree"
and "test isEmpty returns false for a single element aatree"
. This is additional work for you to do now, but it will make future maintenance easier.
|
||
(* Existence tests. *) | ||
test "test isEmpty" { | ||
Expect.isTrue <| AaTree.isEmpty AaTree.empty <| "" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's not use empty assertion messages
|
||
test "test tryFind" { | ||
let tree = AaTree.ofList ["hello"; "bye"] | ||
Expect.equal (Some("hello")) <| AaTree.tryFind "hello" tree <| "" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider using AAA(arrange, act, assert) structure for the test.
Maybe:
let tree = AaTree.ofList ["hello"; "bye"]
let result = AaTree.tryFind "hello" tree
Expect.equal (Some "hello" ) result "tryFind should find hello in aatree created from [hello; bye]"
let array = [|1; 2; 3; 4; 5|] | ||
let tree = AaTree.ofArray array | ||
for i in array do | ||
Expect.isTrue <| AaTree.exists i tree <| "" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Expect.containsAll
?
let inputList = [0;1;2;3] | ||
let tree = AaTree.ofList inputList | ||
let outputList = AaTree.toList tree | ||
Expect.equal outputList inputList "" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it guaranteed that AaTree.toList
output will be sorted?
open FSharpx.Collections | ||
open System.Collections.Generic | ||
|
||
(* Implementation guided by following paper: https://arxiv.org/pdf/1412.4882.pdf *) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will be able to familiarize myself with it on Tuesday.
I think all of these are good suggestions and appreciate your rigorous standards. I'll have them implemented in the next few days hopefully. |
|
||
interface IEnumerable<'T> with | ||
member x.GetEnumerator() = | ||
(x.ToList() :> _ seq).GetEnumerator() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AATree.toSeq
?
let toList (tree: AaTree<'T>) = | ||
foldBack (fun a e -> e::a) [] tree | ||
|
||
let toSeq (tree: AaTree<'T>) = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we provide a lazy implementation? IMO it is unexpected and surprising for the user that just getting the seq, without even using it is O(n). Maybe something along the lines of:
let rec toSeq (tree: AaTree<'T>) =
seq{
match tree with
| E -> ()
| T(_, l, v, r) ->
yield! toSeq l
yield v
yield! toSeq r
}
?
open FSharpx.Collections | ||
open FSharpx.Collections.Experimental | ||
open Expecto | ||
open Expecto.Flip |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe that you are not using the flip style and that the code will compile again if you remove it.
Hi @gdziadkiewicz and thanks again for the feedback. (I personally think it's easier to reply "here" rather than you jumping up/down the page navigating to different comments, but let me know if you prefer the other way of individual threads.)
|
I reviewed my code and compared it with the paper (and also with the Isabelle HOL proof linked below which corrects the paper in a couple of places) and made some changes. Summary of my mistakes (now fixed):
Summary of changes from the proof (corrects errors in the paper, and corrections now implemented in code):
Isabelle HOL proof for reference: https://isabelle.in.tum.de/library/HOL/HOL-Data_Structures/AA_Set.html . |
@hummy123 the CI build is failing due to problems with code formatting. Could you resolve that? |
@@ -75,6 +72,15 @@ module AaTree = | |||
then split <| (skew <| T(h, l, v, insert item r)) | |||
else node | |||
|
|||
(* nlvl function fixed according to Isabelle HOL prrof below: *) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
prrof -> proof
@gdziadkiewicz Got it working thanks to your help (haven't used FAKE/GitHub actions much before). Here's the action running successfully on my own fork: https://github.com/hummy123/FSharpx.Collections/actions/runs/3841007372/jobs/6540731078 . (Also updated FAKE as it was giving me errors.) |
@hummy123 I'm sorry that I'm not able to work on it quicker. Would you mind if I add some property tests to your code and change the |
@gdziadkiewicz No worries - appreciate your time. Anything that makes the code better and tests more resilient sounds good to me, so go ahead. |
I wasn't able to run the test in this repository as I don't have .NET 6.0.401 SDK installed (only a version matching .NET 7), but I don't expect anything to break since it was just mostly a copy-paste from my other repository.
Appreciate the time taken in reviewing this.