@@ -54,27 +54,29 @@ 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
7068export function newList < T > ( head : T , tail : List < T > ) : List < T > {
7169 // 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- }
70+ // Otherwise, the tail becomes an "actual" tail.
71+ if ( tail . vals . length === tail . idx + 1 ) {
72+ tail . vals . push ( head ) ;
73+ return new List ( tail . vals ) ;
74+ } else {
75+ const li = new List ( [ head ] ) ;
76+ li . _tail = tail ;
77+ return li ;
78+ }
79+ }
7880
7981/**
8082 * F# list is represented in runtime by an optimized type that uses a stack (a reverted JS array)
@@ -91,20 +93,50 @@ export class List<T> implements IEquatable<List<T>>, IComparable<List<T>>, Itera
9193 this . idx = idx ?? this . vals . length - 1 ;
9294 }
9395
94- public get isEmpty ( ) {
95- return this . idx < 0 ;
96+ add ( item : T ) : List < T > {
97+ // If this points to the last index of the stack, push the new value into it.
98+ // Otherwise, this becomes an "actual" tail.
99+ if ( this . vals . length === this . idx + 1 ) {
100+ this . vals . push ( item ) ;
101+ return new List ( this . vals ) ;
102+ } else {
103+ const li = new List ( [ item ] ) ;
104+ li . _tail = this ;
105+ return li ;
106+ }
107+ }
108+
109+ /** Unsafe, check length before calling it */
110+ public item ( i : number ) : T | undefined {
111+ let rev_i = this . idx - i ;
112+ if ( rev_i >= 0 ) {
113+ return this . vals [ rev_i ] ;
114+ } else if ( this . _tail ) {
115+ return this . _tail . item ( rev_i * - 1 - 1 ) ;
116+ }
117+ return undefined ;
96118 }
97119
120+ /** Unsafe, check isEmpty before calling it */
98121 public get head ( ) : T | undefined {
99122 return this . vals [ this . idx ] ;
100123 }
101124
102125 public get tail ( ) : List < T > | undefined {
103- return this . idx >= 0 ? new List ( this . vals , this . idx - 1 ) : undefined ;
126+ if ( this . idx === 0 && this . _tail ) {
127+ return this . _tail ;
128+ } else if ( this . idx >= 0 ) {
129+ return new List ( this . vals , this . idx - 1 ) ;
130+ }
131+ return undefined ;
104132 }
105133
106- public get length ( ) {
107- return this . idx + 1 ;
134+ public get isEmpty ( ) {
135+ return this . idx < 0 ;
136+ }
137+
138+ public get length ( ) : number {
139+ return this . idx + 1 + ( this . _tail ?. length ?? 0 ) ;
108140 }
109141
110142 public toString ( ) {
@@ -117,11 +149,19 @@ export class List<T> implements IEquatable<List<T>>, IComparable<List<T>>, Itera
117149
118150 public [ Symbol . iterator ] ( ) : Iterator < T > {
119151 let curIdx = this . idx ;
152+ let li : List < T > = this ;
120153 return {
121- next : ( ) => ( {
122- done : curIdx < 0 ,
123- value : this . vals [ curIdx -- ] ,
124- } ) ,
154+ next : ( ) : IteratorResult < T > => {
155+ if ( curIdx < 0 ) {
156+ if ( li . _tail ) {
157+ li = li . _tail ;
158+ curIdx = li . idx ;
159+ } else {
160+ return { done : true , value : undefined } ;
161+ }
162+ }
163+ return { done : false , value : li . vals [ curIdx -- ] } ;
164+ }
125165 } ;
126166 }
127167
0 commit comments