Skip to content

Commit 5451be1

Browse files
authored
Merge pull request #20 from greymatter-io/stack
Stack
2 parents 64fb5c4 + 8dd128c commit 5451be1

File tree

5 files changed

+439
-46
lines changed

5 files changed

+439
-46
lines changed

arrays/arrays.go

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package arrays
22

3-
//The efficiency of this algorithm is O(N) but it reverses the list. Use FoldLeft instead if you don't want this.
3+
// The efficiency of this algorithm is O(N) but it reverses the list. Use FoldLeft instead if you don't want this.
44
func FoldRight[T1, T2 any](as []T1, z T2, f func(T1, T2) T2) T2 {
55
if len(as) > 1 { //Slice has a head and a tail.
66
h, t := as[0], as[1:len(as)]
@@ -15,12 +15,12 @@ func FoldRight[T1, T2 any](as []T1, z T2, f func(T1, T2) T2) T2 {
1515
type fTypeFoldl[T1, T2 any] func([]T1, T2, func(T2, T1) T2) (T2, tTypeFoldl[T2])
1616
type tTypeFoldl[T2 any] func() (T2, tTypeFoldl[T2])
1717

18-
//This is a stack-safe, tail recursive, pure function that uses the trampoline technique to ensure that the runtime
19-
//does not face recursion that is too deep(i.e. The garbage collector will run before the recursion gets deep enough to blow the stack).
20-
//See https://trinetri.wordpress.com/2015/04/28/tail-call-thunks-and-trampoline-in-golang/
21-
//A tail call is a function call that is the last action performed in a function.
22-
//The efficiency of this algorithm is O(N) and it does not reverse the list like FoldRight does.
23-
//Most of the other array functions in this package use FoldLeft and thus all are stack-safe.
18+
// This is a stack-safe, tail recursive, pure function that uses the trampoline technique to ensure that the runtime
19+
// does not face recursion that is too deep(i.e. The garbage collector will run before the recursion gets deep enough to blow the stack).
20+
// See https://trinetri.wordpress.com/2015/04/28/tail-call-thunks-and-trampoline-in-golang/
21+
// A tail call is a function call that is the last action performed in a function.
22+
// The efficiency of this algorithm is O(N) and it does not reverse the list like FoldRight does.
23+
// Most of the other array functions in this package use FoldLeft and thus all are stack-safe.
2424
func FoldLeft[T1, T2 any](as []T1, z T2, f func(T2, T1) T2) T2 {
2525
accum, p := foldL(as, z, f)
2626
for {
@@ -48,7 +48,7 @@ func foldL[T1, T2 any](as []T1, z T2, f func(T2, T1) T2) (T2, tTypeFoldl[T2]) {
4848
h := as[0]
4949
zz := f(z, h)
5050
return zz, thunk(foldL[T1, T2], Zero[T1](), zz, f)
51-
} else { //Causes the stack-safe funtion FoldLeft that uses the trampoilne technique to leave the for loop with the final accum result
51+
} else { //Causes the stack-safe function FoldLeft that uses the trampoline technique to leave the recursion with the final accum result
5252
return z, nil
5353
}
5454
}
@@ -62,14 +62,14 @@ func Zero[T any]() []T {
6262
return []T{}
6363
}
6464

65-
//The efficiency of this algorithm is O(N)
65+
// The efficiency of this algorithm is O(N)
6666
func Reverse[T1 any](xs []T1) []T1 {
6767
f := Appender[T1]
6868
return FoldRight(xs, Zero[T1](), f)
6969
}
7070

7171
// A structure-preserving Functor on the given array of T.
72-
//The efficiency of this algorithm is O(N)
72+
// The efficiency of this algorithm is O(N)
7373
func Map[T1, T2 any](as []T1, f func(T1) T2) []T2 {
7474
g := func(as []T2, s T1) []T2 {
7575
gss := append(as, f(s))
@@ -79,8 +79,8 @@ func Map[T1, T2 any](as []T1, f func(T1) T2) []T2 {
7979
return xs
8080
}
8181

82-
//The efficiency of this algorithm is O(N)
83-
//Collapses the given array of arrays without changing the order.
82+
// The efficiency of this algorithm is O(N)
83+
// Collapses the given array of arrays without changing the order.
8484
func Concat[A any](l [][]A) []A {
8585
g := func(s []A, as []A) []A {
8686
gss := append(s, as...)
@@ -91,12 +91,12 @@ func Concat[A any](l [][]A) []A {
9191

9292
// Similar to Map in that it takes an array of T1 and applies a function to each element.
9393
// But FlatMap is more powerful than map. We can use flatMap to generate a collection that is either larger or smaller than the original input.
94-
//The efficiency of this algorithm is O(N squared)
94+
// The efficiency of this algorithm is O(N squared)
9595
func FlatMap[T1, T2 any](as []T1, f func(T1) []T2) []T2 {
9696
return Concat(Map(as, f))
9797
}
9898

99-
//The efficiency of this algorithm is O(N)
99+
// The efficiency of this algorithm is O(N)
100100
func Filter[T any](as []T, p func(T) bool) []T {
101101
var g = func(accum []T, h T) []T {
102102
if p(h) {
@@ -108,16 +108,16 @@ func Filter[T any](as []T, p func(T) bool) []T {
108108
return FoldLeft(as, []T{}, g)
109109
}
110110

111-
//The efficiency of this algorithm is O(N)
112-
//Appends as2 to the end of as1
111+
// The efficiency of this algorithm is O(N)
112+
// Appends as2 to the end of as1
113113
func Append[T any](as1, as2 []T) []T {
114114
var g = func(accum []T, h T) []T {
115115
return append(accum, h)
116116
}
117117
return FoldLeft(as2, as1, g)
118118
}
119119

120-
//The efficiency of this algorithm is O(N)
120+
// The efficiency of this algorithm is O(N)
121121
func Contains[T any](source []T, contains T, equality func(l, r T) bool) bool {
122122
p := func(s T) bool {
123123
if equality(s, contains) {
@@ -134,7 +134,7 @@ func Contains[T any](source []T, contains T, equality func(l, r T) bool) bool {
134134
}
135135
}
136136

137-
//The efficiency of this algorithm is O(N)
137+
// The efficiency of this algorithm is O(N)
138138
func ContainsAllOf[T any](source []T, contains []T, equality func(l, r T) bool) bool {
139139
for _, v := range contains {
140140
if !Contains(source, v, equality) {
@@ -144,7 +144,7 @@ func ContainsAllOf[T any](source []T, contains []T, equality func(l, r T) bool)
144144
return true
145145
}
146146

147-
//The efficiency of this algorithm is O(N)
147+
// The efficiency of this algorithm is O(N)
148148
func ArrayEquality[T any](aa []T, bb []T, equality func(l, r T) bool) bool {
149149
f := func(aa, bb []T) bool {
150150
for i, _ := range aa {

linked_list/linked_list.go

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,41 +3,47 @@ package linked_list
33
import "fmt"
44

55
type LinkedList[T any] struct {
6-
head T
7-
tail *LinkedList[T]
6+
Head T
7+
Tail *LinkedList[T]
88
}
99

1010
func (w *LinkedList[T]) String() string {
11-
return fmt.Sprintf("LinkedList {head: %v, tail: %v}", w.head, w.tail)
11+
return fmt.Sprintf("LinkedList {Head: %v, Tail: %v}", w.Head, w.Tail)
12+
}
13+
14+
func DeleteInBigOh1[T any](x *LinkedList[T]) *LinkedList[T] {
15+
x.Head = x.Tail.Head
16+
x.Tail = x.Tail.Tail
17+
return x
1218
}
1319

1420
func Push[T any](h T, l *LinkedList[T]) *LinkedList[T] {
1521
if l == nil {
1622
return &LinkedList[T]{
17-
head: h,
18-
tail: nil,
23+
Head: h,
24+
Tail: nil,
1925
}
2026
} else {
2127
return &LinkedList[T]{
22-
head: h,
23-
tail: l,
28+
Head: h,
29+
Tail: l,
2430
}
2531
}
2632
}
2733

2834
func AddLast[T any](h T, l *LinkedList[T]) *LinkedList[T] {
2935
newTail := &LinkedList[T]{
30-
head: h,
31-
tail: nil,
36+
Head: h,
37+
Tail: nil,
3238
}
3339
if l == nil {
3440
return newTail
3541
} else {
3642
current := l
37-
for current.tail != nil {
38-
current = current.tail
43+
for current.Tail != nil {
44+
current = current.Tail
3945
}
40-
current.tail = newTail
46+
current.Tail = newTail
4147
return l
4248
}
4349
}
@@ -46,13 +52,13 @@ func Tail[T any](l *LinkedList[T]) (*LinkedList[T], error) {
4652
if Len(l) == 0 {
4753
return nil, fmt.Errorf("Cannot Tail an empty list")
4854
} else {
49-
return l.tail, nil //r.Tail, nil
55+
return l.Tail, nil //r.Tail, nil
5056
}
5157
}
5258

5359
// TODO Head of Nil list will throw a NPE. Fix this
5460
func Head[T any](l *LinkedList[T]) T {
55-
return l.head
61+
return l.Head
5662
}
5763

5864
func Drop[T any](l *LinkedList[T], n int) *LinkedList[T] {
@@ -62,7 +68,7 @@ func Drop[T any](l *LinkedList[T], n int) *LinkedList[T] {
6268
if l == nil {
6369
return nil
6470
} else {
65-
return Drop(l.tail, n-1)
71+
return Drop(l.Tail, n-1)
6672
}
6773
}
6874
}
@@ -72,14 +78,14 @@ func Zero[T any]() *LinkedList[T] {
7278
}
7379

7480
func internalAddWhile[T any](l *LinkedList[T], r *LinkedList[T], p func(T) bool) *LinkedList[T] {
75-
if l == nil || !p(l.head) {
81+
if l == nil || !p(l.Head) {
7682
return r
7783
} else {
78-
return internalAddWhile(l.tail, AddLast(l.head, r), p)
84+
return internalAddWhile(l.Tail, AddLast(l.Head, r), p)
7985
}
8086
}
8187

82-
// Evaluates elements of given list, adding elements to head of a new list until predicate returns false, returning the new list and preserving ordering of original list.
88+
// Evaluates elements of given list, adding elements to Head of a new list until predicate returns false, returning the new list and preserving ordering of original list.
8389
// Note that this is different than filter. The algorithm stops appending to the resulting list when the predicate returns false.
8490
func AddWhile[T any](l *LinkedList[T], p func(T) bool) *LinkedList[T] {
8591
return internalAddWhile(l, Zero[T](), p)
@@ -110,7 +116,7 @@ func internalLen[T any](l *LinkedList[T], n int) int {
110116
if l == nil {
111117
return n
112118
} else {
113-
return internalLen(l.tail, n+1)
119+
return internalLen(l.Tail, n+1)
114120
}
115121
}
116122

@@ -122,15 +128,15 @@ func FoldRight[A, B any](l *LinkedList[A], z B, f func(A, B) B) B {
122128
if l == nil {
123129
return z
124130
} else {
125-
return f(l.head, FoldRight(l.tail, z, f))
131+
return f(l.Head, FoldRight(l.Tail, z, f))
126132
}
127133
}
128134

129135
func FoldLeft[A, B any](l *LinkedList[A], z B, f func(B, A) B) B {
130136
if l == nil {
131137
return z
132138
} else {
133-
return FoldLeft(l.tail, f(z, l.head), f)
139+
return FoldLeft(l.Tail, f(z, l.Head), f)
134140
}
135141
}
136142

linked_list/linked_list_test.go

Lines changed: 99 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,55 @@ import (
99
"time"
1010
)
1111

12+
func TestFilter(t *testing.T) {
13+
rng := propcheck.SimpleRNG{Seed: time.Now().Nanosecond()}
14+
ge := propcheck.ChooseArray(0, 20, propcheck.String(40))
15+
16+
prop := propcheck.ForAll(ge,
17+
"Validate Push for LinkedList \n",
18+
func(xs []string) []string {
19+
return xs
20+
},
21+
func(xss []string) (bool, error) {
22+
var errors error
23+
var l *LinkedList[string]
24+
var i int
25+
for {
26+
if len(xss) == 0 {
27+
break
28+
}
29+
l = Push(xss[i], l)
30+
if l.Head != xss[i] {
31+
errors = multierror.Append(errors, fmt.Errorf("Head %v should have been %v pushed to Head of LinkedList", l.Head, xss[i]))
32+
}
33+
p := func(s string) bool {
34+
if s == xss[i] {
35+
return true
36+
} else {
37+
return false
38+
}
39+
}
40+
r := Filter(l, p)
41+
if r.Head != xss[i] {
42+
errors = multierror.Append(errors, fmt.Errorf("Filter should have found %v in the LinkedList", xss[i]))
43+
}
44+
if i+1 == len(xss) {
45+
break
46+
} else {
47+
i++
48+
}
49+
}
50+
if errors != nil {
51+
return false, errors
52+
} else {
53+
return true, nil
54+
}
55+
},
56+
)
57+
result := prop.Run(propcheck.RunParms{100, rng})
58+
propcheck.ExpectSuccess[[]string](t, result)
59+
}
60+
1261
func TestPush(t *testing.T) {
1362
rng := propcheck.SimpleRNG{Seed: time.Now().Nanosecond()}
1463
ge := propcheck.ChooseArray(0, 20, propcheck.String(40))
@@ -27,8 +76,8 @@ func TestPush(t *testing.T) {
2776
break
2877
}
2978
l = Push(xss[i], l)
30-
if l.head != xss[i] {
31-
errors = multierror.Append(errors, fmt.Errorf("Head %v should have been %v pushed to head of LinkedList", l.head, xss[i]))
79+
if l.Head != xss[i] {
80+
errors = multierror.Append(errors, fmt.Errorf("Head %v should have been %v pushed to Head of LinkedList", l.Head, xss[i]))
3281
}
3382
if i+1 == len(xss) {
3483
break
@@ -65,11 +114,11 @@ func TestAddLast(t *testing.T) {
65114
break
66115
}
67116
l = AddLast(xss[i], l)
68-
if l.head == xss[i] && i > 0 {
69-
errors = multierror.Append(errors, fmt.Errorf("Head %v should have been %v pushed to last Cons of LinkedList, not the beginning", l.head, xss[i]))
117+
if l.Head == xss[i] && i > 0 {
118+
errors = multierror.Append(errors, fmt.Errorf("Head %v should have been %v pushed to last Cons of LinkedList, not the beginning", l.Head, xss[i]))
70119
}
71120
if Len(l) != i+1 {
72-
errors = multierror.Append(errors, fmt.Errorf("Element %v did not get added to LinkedList", l.head))
121+
errors = multierror.Append(errors, fmt.Errorf("Element %v did not get added to LinkedList", l.Head))
73122
}
74123
if i+1 == len(xss) {
75124
break
@@ -282,7 +331,7 @@ func TestHead(t *testing.T) {
282331
}
283332
l = Push(xss[i], l)
284333
if Head(l) != xss[i] {
285-
errors = multierror.Append(errors, fmt.Errorf("Head %v should have been %v pushed to head of LinkedList", Head(l), xss[i]))
334+
errors = multierror.Append(errors, fmt.Errorf("Head %v should have been %v pushed to Head of LinkedList", Head(l), xss[i]))
286335
}
287336
if i+1 == len(xss) {
288337
break
@@ -467,3 +516,47 @@ func TestOrderingOfFoldLeftAndFoldRight(t *testing.T) {
467516
result := prop.Run(propcheck.RunParms{100, rng})
468517
propcheck.ExpectSuccess[[]string](t, result)
469518
}
519+
520+
func TestDeleteInBigOh1(t *testing.T) {
521+
rng := propcheck.SimpleRNG{Seed: time.Now().Nanosecond()}
522+
ge := propcheck.ChooseArray(5, 8, propcheck.String(40))
523+
524+
prop := propcheck.ForAll(ge,
525+
"Validate Big Oh 1 delete on singly linked LinkedList \n",
526+
func(xs []string) []string {
527+
return xs
528+
},
529+
func(xss []string) (bool, error) {
530+
var errors error
531+
var l *LinkedList[string]
532+
var i int
533+
for {
534+
if len(xss) == 0 {
535+
break
536+
}
537+
l = Push(xss[i], l)
538+
if i+1 == len(xss) {
539+
break
540+
} else {
541+
i++
542+
}
543+
}
544+
m := DeleteInBigOh1(l.Tail.Tail)
545+
546+
if m.Head != l.Tail.Tail.Head {
547+
t.Errorf("Expected %v actual:%v", l.Tail.Tail.Head, m.Head)
548+
}
549+
if m.Tail != l.Tail.Tail.Tail {
550+
t.Errorf("Expected %v actual:%v", l.Tail.Tail.Tail, m.Tail)
551+
}
552+
if errors != nil {
553+
return false, errors
554+
} else {
555+
return true, nil
556+
}
557+
},
558+
)
559+
result := prop.Run(propcheck.RunParms{100, rng})
560+
propcheck.ExpectSuccess[[]string](t, result)
561+
562+
}

0 commit comments

Comments
 (0)