@@ -54,28 +54,17 @@ function compareList<T>(self: List<T>, other: List<T>) {
5454 if ( other == null ) {
5555 return - 1 ;
5656 }
57- const idxDiff = self . idx - other . idx ;
58- for ( let i = self . idx ; i >= 0 ; i -- ) {
59- const 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 ) ;
57+ const selfLen = self . length ;
58+ const otherLen = other . length ;
59+ const minLen = Math . min ( selfLen , otherLen ) ;
60+ for ( let i = 0 ; i < minLen ; i ++ ) {
61+ const res = compare ( self . item ( i ) , other . item ( i ) ) ;
6462 if ( res !== 0 ) { return res ; }
6563 }
66- return self . length === other . length ? 0 : - 1 ;
64+ return selfLen > otherLen ? 1 : ( selfLen < otherLen ? - 1 : 0 ) ;
6765 }
6866}
6967
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- return li ;
77- }
78-
7968/**
8069 * F# list is represented in runtime by an optimized type that uses a stack (a reverted JS array)
8170 * to store the values, so we can a have a big list represented by a single object (plus the stack).
@@ -91,20 +80,50 @@ export class List<T> implements IEquatable<List<T>>, IComparable<List<T>>, Itera
9180 this . idx = idx ?? this . vals . length - 1 ;
9281 }
9382
94- public get isEmpty ( ) {
95- return this . idx < 0 ;
83+ add ( item : T ) : List < T > {
84+ // If this points to the last index of the stack, push the new value into it.
85+ // Otherwise, this becomes an "actual" tail.
86+ if ( this . vals . length === this . idx + 1 ) {
87+ this . vals . push ( item ) ;
88+ return new List ( this . vals ) ;
89+ } else {
90+ const li = new List ( [ item ] ) ;
91+ li . _tail = this ;
92+ return li ;
93+ }
94+ }
95+
96+ /** Unsafe, check length before calling it */
97+ public item ( i : number ) : T | undefined {
98+ let rev_i = this . idx - i ;
99+ if ( rev_i >= 0 ) {
100+ return this . vals [ rev_i ] ;
101+ } else if ( this . _tail ) {
102+ return this . _tail . item ( rev_i * - 1 - 1 ) ;
103+ }
104+ return undefined ;
96105 }
97106
107+ /** Unsafe, check isEmpty before calling it */
98108 public get head ( ) : T | undefined {
99109 return this . vals [ this . idx ] ;
100110 }
101111
102112 public get tail ( ) : List < T > | undefined {
103- return this . idx >= 0 ? new List ( this . vals , this . idx - 1 ) : undefined ;
113+ if ( this . idx === 0 && this . _tail ) {
114+ return this . _tail ;
115+ } else if ( this . idx >= 0 ) {
116+ return new List ( this . vals , this . idx - 1 ) ;
117+ }
118+ return undefined ;
119+ }
120+
121+ public get isEmpty ( ) {
122+ return this . idx < 0 ;
104123 }
105124
106- public get length ( ) {
107- return this . idx + 1 ;
125+ public get length ( ) : number {
126+ return this . idx + 1 + ( this . _tail ?. length ?? 0 ) ;
108127 }
109128
110129 public toString ( ) {
@@ -117,11 +136,19 @@ export class List<T> implements IEquatable<List<T>>, IComparable<List<T>>, Itera
117136
118137 public [ Symbol . iterator ] ( ) : Iterator < T > {
119138 let curIdx = this . idx ;
139+ let li : List < T > = this ;
120140 return {
121- next : ( ) => ( {
122- done : curIdx < 0 ,
123- value : this . vals [ curIdx -- ] ,
124- } ) ,
141+ next : ( ) : IteratorResult < T > => {
142+ if ( curIdx < 0 ) {
143+ if ( li . _tail ) {
144+ li = li . _tail ;
145+ curIdx = li . idx ;
146+ } else {
147+ return { done : true , value : undefined } ;
148+ }
149+ }
150+ return { done : false , value : li . vals [ curIdx -- ] } ;
151+ }
125152 } ;
126153 }
127154
0 commit comments