@@ -54,24 +54,60 @@ function compareList<T>(self: List<T>, other: List<T>) {
5454 if ( other == null ) {
5555 return - 1 ;
5656 }
57- while ( self . tail != null ) {
58- if ( other . tail == null ) { return 1 ; }
59- const res = compare ( self . head , other . head ) ;
57+ const idxDiff = self . idx - other . idx ;
58+ for ( let i = self . idx ; i >= 0 ; i -- ) {
59+ let otherIdx = i - idxDiff ;
60+ if ( otherIdx < 0 ) { return 1 ; }
61+ const selfItem = self . vals [ i ] ;
62+ const otherItem = other . vals [ otherIdx ] ;
63+ const res = compare ( selfItem , otherItem ) ;
6064 if ( res !== 0 ) { return res ; }
61- self = self . tail ;
62- other = other . tail ;
6365 }
64- return other . tail == null ? 0 : - 1 ;
66+ return self . length === other . length ? 0 : - 1 ;
6567 }
6668}
6769
70+ export function newList < T > ( head : T , tail : List < T > ) : List < T > {
71+ // If the tail points to the last index of the stack, push the new value into it.
72+ // Otherwise, create a new stack
73+ const vals = tail . vals . length === tail . idx + 1 ? tail . vals : tail . vals . slice ( 0 , tail . idx + 1 ) ;
74+ vals . push ( head ) ;
75+ const li = new List ( vals ) ;
76+ li . _tail = tail ;
77+ return li ;
78+ }
79+
80+ /**
81+ * F# list is represented in runtime by an optimized type that uses a stack (a reverted JS array)
82+ * to store the values, so we can a have a big list represented by a single object (plus the stack).
83+ * It also allows for optimizations in the List module.
84+ */
6885export class List < T > implements IEquatable < List < T > > , IComparable < List < T > > , Iterable < T > {
69- public head : T ;
70- public tail ?: List < T > ;
86+ public vals : T [ ] ;
87+ public idx : number ;
88+ public _tail : List < T > | undefined ;
89+
90+ constructor ( vals ?: T [ ] , idx ?: number ) {
91+ this . vals = vals ?? [ ] ;
92+ this . idx = idx ?? this . vals . length - 1 ;
93+ }
94+
95+ public get empty ( ) {
96+ return this . idx < 0 ;
97+ }
98+
99+ public get head ( ) : T | undefined {
100+ return this . vals [ this . idx ] ;
101+ }
71102
72- constructor ( head ?: T , tail ?: List < T > ) {
73- this . head = head as T ;
74- this . tail = tail ;
103+ public get tail ( ) : List < T > | undefined {
104+ return ! this . empty
105+ ? this . _tail ?? ( this . _tail = new List ( this . vals , this . idx - 1 ) )
106+ : undefined ;
107+ }
108+
109+ public get length ( ) {
110+ return this . idx + 1 ;
75111 }
76112
77113 public toString ( ) {
@@ -83,20 +119,25 @@ export class List<T> implements IEquatable<List<T>>, IComparable<List<T>>, Itera
83119 }
84120
85121 public [ Symbol . iterator ] ( ) : Iterator < T > {
86- let cur : List < T > | undefined = this ;
122+ let curIdx = this . idx ;
87123 return {
88- next : ( ) : IteratorResult < T > => {
89- const value = cur ?. head as T ;
90- const done = cur ?. tail == null ;
91- cur = cur ?. tail ;
92- return { done, value } ;
93- } ,
124+ next : ( ) => ( {
125+ done : curIdx < 0 ,
126+ value : this . vals [ curIdx -- ] ,
127+ } ) ,
94128 } ;
95129 }
96130
97131 public GetHashCode ( ) {
98- const hashes = Array . from ( this ) . map ( structuralHash ) ;
99- return combineHashCodes ( hashes ) ;
132+ if ( this . idx < 0 ) {
133+ return 0 ;
134+ } else {
135+ const hashes : number [ ] = new Array ( this . idx + 1 ) ;
136+ for ( let i = this . idx ; i >= 0 ; i -- ) {
137+ hashes [ i ] = structuralHash ( this . vals [ i ] ) ;
138+ }
139+ return combineHashCodes ( hashes ) ;
140+ }
100141 }
101142
102143 public Equals ( other : List < T > ) : boolean {
0 commit comments