├── .travis.yml ├── README.md ├── functional.go └── functional_test.go /.travis.yml: -------------------------------------------------------------------------------- 1 | # Copied from github.com/peterbourgon/diskv 2 | 3 | language: erlang # wither art thou, Go? 4 | 5 | install: 6 | - hg clone -r go1 https://code.google.com/p/go $HOME/go 7 | - cd $HOME/go/src && ./make.bash 8 | - mkdir -p $HOME/src || true 9 | - mkdir -p $HOME/bin || true 10 | - mkdir -p $HOME/pkg || true 11 | - export GOPATH=$HOME 12 | - export PATH=$PATH:$HOME/go/bin 13 | - ln -s $HOME/builds/tcard/functional $HOME/src/functional 14 | - go get -v github.com/tcard/functional 15 | 16 | script: 17 | - cd $HOME/src/functional && go build -v . 18 | - cd $HOME/src/functional && go test -v . 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # functional 2 | 3 | A functional programming library including 4 | a lazy list implementation and some of the most usual functions. 5 | 6 | import FP "github.com/tcard/functional" 7 | 8 | ## Installing 9 | 10 | go get github.com/tcard/functional 11 | 12 | Tested on Go 1. 13 | 14 | [![Build Status](http://goci.me/project/image/github.com/tcard/functional)](http://goci.me/project/github.com/tcard/functional) 15 | 16 | ## Examples 17 | 18 | The Fibonacci sequence, infinite stream: 19 | 20 | var fibo *FP.Thunk 21 | fibo = FP.Link(1, FP.DelayedLink(1, func() *FP.Thunk { 22 | return FP.MapN(func(xs ...FP.I) FP.I { 23 | ret := 0 24 | for _, v := range xs { 25 | ret += v.(int) 26 | } 27 | return ret 28 | }, fibo, fibo.Tail()) 29 | })) 30 | 31 | Which is an uglier, slower version of the famous Lisp-like: 32 | 33 | (define fibo 34 | (cons 1 35 | (cons 1 36 | (map + fibo (cdr fibo))))) 37 | 38 | For showing some "real" utility, this would retrieve the third page of a blog: 39 | 40 | var posts *FP.Thunk = ... 41 | page := 3 42 | postsPerPage := 10 43 | _ = posts.Drop((page - 1) * postsPerPage).Take(postsPerPage).Map(func (post FP.I) FP.I { 44 | blog.showPost(post.(Post)) 45 | return post 46 | }) 47 | 48 | See documentation and tests for detailed usage. 49 | 50 | ## To Do 51 | 52 | * Better memoization. 53 | * Ability to skip lists and work directly with slices, although lack of generics would make it clumsy anyway. 54 | * Some optimizations. 55 | -------------------------------------------------------------------------------- /functional.go: -------------------------------------------------------------------------------- 1 | // Package functional implements a functional programming library including 2 | // a lazy list implementation and some of the most usual functions. 3 | package functional 4 | 5 | import ( 6 | "fmt" 7 | "reflect" 8 | ) 9 | 10 | // Type I is the type of the element of a Pair. It is defined as interface{}, 11 | // so you can throw anything inside of a Pair. When you take elements back to 12 | // non-functional code you will probably need to type-assert it. 13 | type I interface{} 14 | 15 | // A generic function type returned by Curry. 16 | type F func(...I) I 17 | 18 | // The Pair type is the basic element of Lists. Composed of an element (the head) 19 | // and a pointer to the Thunk which returns the next Pair of the List (the tail). 20 | type Pair struct { 21 | Head I 22 | Tail *Thunk 23 | } 24 | 25 | // A Thunk is a delayed Pair. It is a function that, when called, 26 | // returns or generates the underlying pair. In practice, Thunk is like a Pair 27 | // which is like a List; you won't usually need to worry about the differences. 28 | // Use MakeThunk to provide your own generator function. 29 | type Thunk func() *Pair 30 | 31 | func MakeThunk(f func() *Pair) *Thunk { 32 | var ret Thunk = f 33 | return &ret 34 | } 35 | 36 | // Empty is the empty Thunk, that is, a Thunk that returns nil. Lists end 37 | // with it. 38 | var Empty *Thunk 39 | 40 | var memo bool 41 | 42 | // Starts memoizing thunk evaluations. By default memoization is on. 43 | func StartMemo() { 44 | memo = true 45 | } 46 | 47 | // Stops memoizing thunk evaluations. By default memoization is on. 48 | func StopMemo() { 49 | memo = false 50 | } 51 | 52 | func force(thunk *Thunk) *Pair { 53 | if thunk == nil { 54 | return nil 55 | } 56 | pair := (*thunk)() 57 | if memo { 58 | *thunk = *MakeThunk(func() *Pair { 59 | return pair 60 | }) 61 | } 62 | return pair 63 | } 64 | 65 | func (thunk *Thunk) Head() I { 66 | return force(thunk).Head 67 | } 68 | 69 | func (thunk *Thunk) Tail() *Thunk { 70 | return force(thunk).Tail 71 | } 72 | 73 | // Takes a head element and a tail Thunk and makes a Thunk with them. 74 | // Similar to Lisp's `cons` or Haskell's `(:)`. 75 | // list123 := Link(1, Link(2, Link(3, Empty))) 76 | func Link(head I, tail *Thunk) *Thunk { 77 | return MakeThunk(func() *Pair { return &Pair{head, tail} }) 78 | } 79 | 80 | // Performs just like Link, but the tail is doubly delayed. Rarely used, 81 | // useful when the tail is generated by some recursive function. 82 | // 83 | // var factN func(int) int 84 | // factN = func(n int) int { 85 | // if n == 0 { 86 | // return 1 87 | // } 88 | // return n * factN(n-1) 89 | // } 90 | // var makeFact func(int) *Thunk 91 | // makeFact = func(n int) *Thunk { 92 | // // A direct Link to makeFact(n + 1) would lead to an infinite loop. 93 | // return DelayedLink(factN(n), func() *Thunk { return makeFact(n + 1) }) 94 | // } 95 | // fact := makeFact(0) 96 | func DelayedLink(head I, tail func() *Thunk) *Thunk { 97 | return MakeThunk(func() *Pair { return &Pair{head, tail()} }) 98 | } 99 | 100 | // Helper function that Links all its arguments. You can easily make a list 101 | // from a slice with it: List(slice...) 102 | func List(items ...I) *Thunk { 103 | if len(items) >= 1 { 104 | return Link(items[0], List(items[1:]...)) 105 | } 106 | return Empty 107 | } 108 | 109 | // Shortcut for List. 110 | func L(items ...I) *Thunk { 111 | return List(items...) 112 | } 113 | 114 | func SliceToList(items I) (ret *Thunk) { 115 | t := reflect.TypeOf(items) 116 | if items == nil || t.Kind() != reflect.Slice { 117 | return 118 | } 119 | v := reflect.ValueOf(items) 120 | ret = Empty 121 | for i := v.Len() - 1; i >= 0; i-- { 122 | ret = Link(v.Index(i).Interface(), ret) 123 | } 124 | return 125 | } 126 | 127 | // Makes a slice from a List. 128 | func (thunk *Thunk) ToSlice() [](I) { 129 | ret := make([]I, thunk.Length()) 130 | pair := force(thunk) 131 | for i := 0; pair != nil; i++ { 132 | ret[i] = pair.Head 133 | pair = force(pair.Tail) 134 | } 135 | return ret 136 | } 137 | 138 | // Makes a single List by appending one to another. 139 | func (thunk *Thunk) Append(other *Thunk) *Thunk { 140 | return MakeThunk(func() *Pair { 141 | pair := force(thunk) 142 | if pair != nil { 143 | return &Pair{pair.Head, pair.Tail.Append(other)} 144 | } else if pair := force(other); pair != nil { 145 | return &Pair{pair.Head, pair.Tail.Append(Empty)} 146 | } 147 | return nil 148 | }) 149 | } 150 | 151 | // A handy way of iterating through a List is by calling Iter() 152 | // in a for-range loop. 153 | func (thunk *Thunk) Iter() chan I { 154 | ch := make(chan I) 155 | go func() { 156 | for { 157 | pair := force(thunk) 158 | if pair == nil { 159 | break 160 | } 161 | ch <- pair.Head 162 | thunk = pair.Tail 163 | } 164 | close(ch) 165 | }() 166 | 167 | return ch 168 | } 169 | 170 | func (thunk *Thunk) String() (ret string) { 171 | ret = "[" 172 | first := true 173 | for { 174 | pair := force(thunk) 175 | if pair == nil { 176 | ret += "]" 177 | break 178 | } 179 | if !first { 180 | ret += " " 181 | } else { 182 | first = false 183 | } 184 | ret += fmt.Sprintf("%v", pair.Head) 185 | thunk = pair.Tail 186 | } 187 | return 188 | } 189 | 190 | // Tests for equality between two lists. 191 | func (thunk *Thunk) Equals(other *Thunk) bool { 192 | for { 193 | pair := force(thunk) 194 | otherPair := force(other) 195 | if pair == nil { 196 | if otherPair != nil { 197 | return false 198 | } else { 199 | break 200 | } 201 | } 202 | switch head := pair.Head.(type) { 203 | case *Thunk: 204 | switch otherHead := otherPair.Head.(type) { 205 | case *Thunk: 206 | if !head.Equals(otherHead) { 207 | return false 208 | } 209 | default: 210 | return false 211 | } 212 | default: 213 | if pair.Head != otherPair.Head { 214 | return false 215 | } 216 | } 217 | thunk, other = pair.Tail, otherPair.Tail 218 | } 219 | return true 220 | } 221 | 222 | func (thunk *Thunk) Length() (ret int) { 223 | pair := force(thunk) 224 | for pair != nil { 225 | thunk = pair.Tail 226 | pair = force(thunk) 227 | ret++ 228 | } 229 | return 230 | } 231 | 232 | // Retrieves the element at the n-th position on the list. If there is 233 | // no such element, err is set. 234 | func (thunk *Thunk) At(n uint) (ret I) { 235 | var pair *Pair 236 | for i := uint(0); i <= n; i++ { 237 | pair = force(thunk) 238 | if pair == nil { 239 | panic("Index out of list.") 240 | return 241 | } 242 | thunk = pair.Tail 243 | } 244 | ret = pair.Head 245 | return 246 | } 247 | 248 | // Takes the first n elements of a list. Mostly needed for infinite lists. 249 | func (thunk *Thunk) Take(n uint) *Thunk { 250 | return MakeThunk(func() *Pair { 251 | if n > 0 { 252 | pair := force(thunk) 253 | if pair != nil { 254 | return &Pair{pair.Head, pair.Tail.Take(n - 1)} 255 | } 256 | } 257 | return nil 258 | }) 259 | } 260 | 261 | // Drops the first n elements of a list and returns the rest. 262 | func (thunk *Thunk) Drop(n uint) *Thunk { 263 | var f func() *Pair 264 | f = func() *Pair { 265 | pair := force(thunk) 266 | if pair != nil { 267 | if n > 0 { 268 | n -= 1 269 | thunk = pair.Tail 270 | return f() 271 | } 272 | return pair 273 | } 274 | return nil 275 | } 276 | return MakeThunk(f) 277 | } 278 | 279 | // Applies a function to each element of some lists. The function must 280 | // handle any number of elements. It ends when any of the lists ends. 281 | func MapN(f func(...I) I, thunks ...*Thunk) *Thunk { 282 | return MakeThunk(func() *Pair { 283 | l := len(thunks) 284 | heads := make([](I), l) 285 | tails := make([]*Thunk, l) 286 | for k := 0; k < l; k++ { 287 | pair := force(thunks[k]) 288 | if pair == nil { 289 | return nil 290 | } 291 | heads[k] = pair.Head 292 | tails[k] = pair.Tail 293 | } 294 | return &Pair{f(heads...), MapN(f, tails...)} 295 | }) 296 | } 297 | 298 | // Applies a function to each element of a list. 299 | func (thunk *Thunk) Map(f func(I) I) *Thunk { 300 | return MapN(func(xs ...I) I { 301 | return f(xs[0]) 302 | }, thunk) 303 | } 304 | 305 | // Applies a function to each element of some lists, returning the 306 | // accumulated value. The function must take the so far accumulated 307 | // value as its first argument and handle any number of elements as 308 | // the second, third and so on. You pass that initial value as ReduceN's second 309 | // argument. It stops reducing when any of the lists ends. 310 | // 311 | // func sumInts(xs I...) I { 312 | // return ReduceN(func(x I, xs ...I) { 313 | // ret := 0 314 | // for _, x := range xs { 315 | // ret += x.(int) 316 | // } 317 | // return ret 318 | // }, 0, xs...) 319 | // } 320 | // 321 | // sumInts(L(1, 2), L(3, 4)) // == 10 322 | func ReduceN(f func(I, ...I) I, acc I, thunks ...*Thunk) I { 323 | l := len(thunks) 324 | heads := make([](I), l) 325 | tails := make([]*Thunk, l) 326 | for k := 0; k < l; k++ { 327 | pair := force(thunks[k]) 328 | if pair == nil { 329 | return acc 330 | } 331 | heads[k] = pair.Head 332 | tails[k] = pair.Tail 333 | } 334 | return ReduceN(f, f(acc, heads...), tails...) 335 | } 336 | 337 | // Applies a function to each element of a list, returning the 338 | // accumulated value. The function must take the so far accumulated value 339 | // as its first argument and the next element of the list as its second 340 | // one. You pass that initial value as Reduce's second argument. 341 | // It stops reducing when any of the lists ends. 342 | // 343 | // func (xs *Thunk) sumInts() I { 344 | // return xs.Reduce(func(acc, x I) { 345 | // acc = acc.(int) + x.(int) 346 | // } 347 | // return acc 348 | // }, 0) 349 | // } 350 | // 351 | // L(1,2,3,4).sumInts() // == 10 352 | func (thunk *Thunk) Reduce(f func(I, I) I, initial I) I { 353 | return ReduceN(func(acc I, xs ...I) I { 354 | return f(acc, xs[0]) 355 | }, initial, thunk) 356 | } 357 | 358 | // Returns the list of lists of the elements which pass a testing function. 359 | // The testing function must take an element from each list to which 360 | // it is applied. 361 | func FilterN(f func(...I) bool, thunks ...*Thunk) *Thunk { 362 | return MakeThunk(func() *Pair { 363 | l := len(thunks) 364 | heads := make([](I), l) 365 | tails := make([]*Thunk, l) 366 | for k := 0; k < l; k++ { 367 | pair := force(thunks[k]) 368 | if pair == nil { 369 | return nil 370 | } 371 | heads[k] = pair.Head 372 | tails[k] = pair.Tail 373 | } 374 | tail := FilterN(f, tails...) 375 | if f(heads...) { 376 | return &Pair{L(heads...), tail} 377 | } 378 | return force(tail) 379 | }) 380 | } 381 | 382 | // Returns the lists of the elements of the list that pass a testing 383 | // function. 384 | func (thunk *Thunk) Filter(f func(I) bool) *Thunk { 385 | /* 386 | // Slow, but left here for fanciness. 387 | return FilterN(func(xs ...I) bool { 388 | return f(xs[0]) 389 | }, thunk).Map(func(arg I) I { 390 | return arg.(*Thunk).ToSlice()[0] 391 | }) 392 | */ 393 | 394 | return MakeThunk(func() *Pair { 395 | pair := force(thunk) 396 | if pair == nil { 397 | return nil 398 | } 399 | tail := pair.Tail.Filter(f) 400 | if f(pair.Head) { 401 | return &Pair{pair.Head, tail} 402 | } 403 | return force(tail) 404 | }) 405 | } 406 | 407 | // Tests if any of the elements of the list passes a testing 408 | // function. 409 | func (thunk *Thunk) Any(f func(I) bool) bool { 410 | pair := force(thunk) 411 | if pair != nil { 412 | if f(pair.Head) { 413 | return true 414 | } else { 415 | return pair.Tail.Any(f) 416 | } 417 | } 418 | return false 419 | } 420 | 421 | // Tests if all of the elements of the list passes a testing 422 | // function. 423 | 424 | func (thunk *Thunk) All(f func(I) bool) bool { 425 | pair := force(thunk) 426 | if pair != nil { 427 | if f(pair.Head) { 428 | return pair.Tail.All(f) 429 | } else { 430 | return false 431 | } 432 | } 433 | return true 434 | } 435 | 436 | // Tests if a list contains an element. 437 | func (thunk *Thunk) Has(x I) bool { 438 | return thunk.Any(func(y I) bool { 439 | return reflect.DeepEqual(x, y) 440 | }) 441 | } 442 | 443 | // Retrieves the maximum element of a list. Obviously, the list must be 444 | // composed of ordered elements (ints, floats or strings). 445 | func (thunk *Thunk) Max() I { 446 | return thunk.Reduce(func(acc, x I) I { 447 | accV := reflect.ValueOf(acc) 448 | xV := reflect.ValueOf(x) 449 | if accV.Kind() != xV.Kind() { 450 | return nil 451 | } 452 | switch accV.Kind() { 453 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 454 | if accV.Int() >= xV.Int() { 455 | return acc 456 | } else { 457 | return x 458 | } 459 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32: 460 | if accV.Uint() >= xV.Uint() { 461 | return acc 462 | } else { 463 | return x 464 | } 465 | case reflect.Float32, reflect.Float64: 466 | acc := accV.Float() 467 | if accV.Float() >= xV.Float() { 468 | return acc 469 | } else { 470 | return x 471 | } 472 | case reflect.String: 473 | if accV.Float() >= xV.Float() { 474 | return acc 475 | } else { 476 | return x 477 | } 478 | } 479 | return nil 480 | }, thunk.Head()) 481 | } 482 | 483 | // Retrieves the minimum element of a list. Obviously, the list must be 484 | // composed of ordered elements (ints, floats or strings). 485 | func (thunk *Thunk) Min() I { 486 | return thunk.Reduce(func(acc, x I) I { 487 | accV := reflect.ValueOf(acc) 488 | xV := reflect.ValueOf(x) 489 | if accV.Kind() != xV.Kind() { 490 | return nil 491 | } 492 | switch accV.Kind() { 493 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 494 | if accV.Int() <= xV.Int() { 495 | return acc 496 | } else { 497 | return x 498 | } 499 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32: 500 | if accV.Uint() <= xV.Uint() { 501 | return acc 502 | } else { 503 | return x 504 | } 505 | case reflect.Float32, reflect.Float64: 506 | acc := accV.Float() 507 | if accV.Float() <= xV.Float() { 508 | return acc 509 | } else { 510 | return x 511 | } 512 | case reflect.String: 513 | if accV.Float() <= xV.Float() { 514 | return acc 515 | } else { 516 | return x 517 | } 518 | } 519 | return nil 520 | }, thunk.Head()) 521 | } 522 | 523 | // Lists the first elements of the list that pass a filtering function. 524 | func (thunk *Thunk) TakeWhile(f func(I) bool) *Thunk { 525 | return MakeThunk(func() *Pair { 526 | pair := force(thunk) 527 | if pair != nil && f(pair.Head) { 528 | return &Pair{pair.Head, pair.Tail.TakeWhile(f)} 529 | } 530 | return nil 531 | }) 532 | } 533 | 534 | // Lists the elements of the list after the first one that doesn't pass a 535 | // filtering function. 536 | func (thunk *Thunk) DropWhile(f func(I) bool) *Thunk { 537 | var g func() *Pair 538 | g = func() *Pair { 539 | pair := force(thunk) 540 | if pair != nil && f(pair.Head) { 541 | thunk = pair.Tail 542 | return g() 543 | } 544 | return pair 545 | } 546 | return MakeThunk(g) 547 | } 548 | 549 | // Takes some lists and returns a list with slices of one element of each list. 550 | // ZipN(L(1, 2, 3), L(4, 5, 6)) // L([1 4], [2 5], [3 6]) 551 | func ZipN(thunks ...*Thunk) *Thunk { 552 | return MapN(func(xs ...I) I { 553 | return SliceToList(xs) 554 | }, thunks...) 555 | } 556 | 557 | // Returns a list with slices of one element of each list. 558 | // L(1, 2, 3).Zip(L(4, 5, 6)) // L([1 4], [2 5], [3 6]) 559 | func (thunk *Thunk) Zip(other *Thunk) *Thunk { 560 | return ZipN(thunk, other) 561 | } 562 | 563 | // Converts a list of lists and makes a single list. 564 | // L(L(1, 2), L(3, 4)).Flatten() // L(1, 2, 3, 4) 565 | func (thunk *Thunk) Flatten() *Thunk { 566 | // Can do better. 567 | return MakeThunk(func() *Pair { 568 | pair := force(thunk) 569 | if pair != nil { 570 | pair2 := force(pair.Head.(*Thunk)) 571 | return &Pair{pair2.Head, 572 | pair2.Tail.Append(pair.Tail.Flatten())} 573 | } 574 | return nil 575 | }) 576 | /*return thunk.Reduce(func(acc, x I) I { 577 | return acc.(*Thunk).Append(x.(*Thunk)) 578 | }, L()).(*Thunk)*/ 579 | } 580 | 581 | func (thunk *Thunk) Reverse() *Thunk { 582 | return MakeThunk(func() *Pair { 583 | return force(thunk.Reduce(func(acc, x I) I { 584 | return Link(x, acc.(*Thunk)) 585 | }, L()).(*Thunk)) 586 | }) 587 | } 588 | 589 | func (thunk *Thunk) Last() I { 590 | pair := force(thunk.Reverse().Take(1)) 591 | return pair.Head 592 | } 593 | 594 | // Makes an autoupdating infinite list. Each element will be 595 | // generated by a function that takes the previous element as 596 | // argument. You must provide an initial element. 597 | // naturals := Updating(0, func(x I) I { 598 | // return x.(int) + 1 599 | // }) 600 | func Updating(initial I, f func(I) I) *Thunk { 601 | return MakeThunk(func() *Pair { 602 | return &Pair{initial, Updating(f(initial), f)} 603 | }) 604 | } 605 | 606 | // Currying is a way of thinking about multiparameter functions 607 | // not as taking a tuple of values, but as taking a sole parameter 608 | // and returning a function that takes another parameter and so on. 609 | // So f(x, y) = [something] becomes f(x) = λ(y) = [something] and 610 | // is called like f(x)(y). Note that you should call curried functions 611 | // by type-asserting the return values to F. 612 | // Curry(math.Max)(2.0).(F)(3.0) == math.Max(2, 3) 613 | // 614 | // add := func(a, b int) int { 615 | // return a + b 616 | // } 617 | // addTwo := Curry(add)(2).(F) 618 | // Also note that this won't work with variadic functions; Curry(func(...T) T) 619 | // is the same as func(...T) but with a I return type. 620 | func Curry(f I) F { 621 | vf := reflect.ValueOf(f) 622 | 623 | var ret F 624 | ret = func(xs ...I) I { 625 | values := make([]reflect.Value, len(xs)) 626 | for k, v := range xs { 627 | values[k] = reflect.ValueOf(v) 628 | } 629 | 630 | ch := make(chan I) 631 | go func() { 632 | defer func() { 633 | if r := recover(); r == "reflect: Call with too few input arguments" { 634 | var g F = func(ys ...I) I { 635 | return ret(append(xs, ys...)...) 636 | } 637 | ch <- g 638 | } else if r != nil { 639 | panic(r) 640 | } 641 | }() 642 | ch <- vf.Call(values)[0].Interface() 643 | }() 644 | 645 | return <-ch 646 | } 647 | 648 | return ret 649 | } 650 | 651 | func init() { 652 | Empty = MakeThunk(func() *Pair { return nil }) 653 | memo = true 654 | } 655 | -------------------------------------------------------------------------------- /functional_test.go: -------------------------------------------------------------------------------- 1 | package functional 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestEquals(t *testing.T) { 9 | l1 := Link(1, Link(2, Link(3, Link(4, Link(5, Empty))))) 10 | l2 := Link(1, Link(2, Link(3, Link(4, Link(5, Empty))))) 11 | if !l1.Equals(l2) { 12 | t.Errorf("%v.Equals(%v)", l1, l2) 13 | } 14 | l2 = Link(1, Link(2, Link(5, Empty))) 15 | if l1.Equals(l2) { 16 | t.Errorf("%v.Equals(%v)", l1, l2) 17 | } 18 | } 19 | 20 | func TestList(t *testing.T) { 21 | l := Link(1, Link(2, Link(3, Link(4, Link("a", Empty))))) 22 | if !l.Equals(List(1, 2, 3, 4, "a")) || l.Equals(List(1, "a", 9)) { 23 | t.Errorf("List(%v)", l) 24 | } 25 | } 26 | 27 | func TestSliceToList(t *testing.T) { 28 | slice := []int{1, 2, 3, 4, 5} 29 | l := SliceToList(slice) 30 | if !l.Equals(List(1, 2, 3, 4, 5)) || l.Equals(List(1, "a", 9)) { 31 | t.Errorf("SliceToList(%v) -> %v", slice, l) 32 | } 33 | } 34 | 35 | func TestToSlice(t *testing.T) { 36 | l := List(1, 2, "a", 4, 5) 37 | if !reflect.DeepEqual(l.ToSlice(), []I{1, 2, "a", 4, 5}) || 38 | reflect.DeepEqual(l.ToSlice(), []I{1, "b", 0}) { 39 | t.Errorf("ToSlice(%v)", l) 40 | } 41 | } 42 | 43 | func TestAppend(t *testing.T) { 44 | l1 := List(1, 2, 3) 45 | l2 := List(4, 5, 6) 46 | l3 := List(1, 2, 3, 4, 5, 6) 47 | if !l1.Append(l2).Equals(l3) { 48 | t.Errorf("Append(%v, %v) -> %v", l1, l2, l3) 49 | } 50 | } 51 | 52 | func TestIter(t *testing.T) { 53 | l := List(1, 2, "a", 4, 5) 54 | s := []I{1, 2, "a", 4, 5} 55 | i := 0 56 | for v := range l.Iter() { 57 | if v != s[i] { 58 | t.Errorf("Iter(%v, %v) -> %v != s[%v]", l, s, v, i) 59 | } 60 | i++ 61 | } 62 | } 63 | 64 | func TestLength(t *testing.T) { 65 | l1 := List(1, 2, 3) 66 | l2 := List() 67 | if l1.Length() != 3 { 68 | t.Errorf("Length(%v) != 3", l1) 69 | } 70 | if l2.Length() != 0 { 71 | t.Errorf("Length(%v) != 0", l2) 72 | } 73 | } 74 | 75 | func TestAt(t *testing.T) { 76 | l1 := List(1, 2, 3) 77 | s := l1.ToSlice() 78 | for k, v := range s { 79 | if w := l1.At(uint(k)); v != w { 80 | t.Errorf("At(%v, %v) != s[%v]", l1, k, k) 81 | } 82 | } 83 | } 84 | 85 | func TestTake(t *testing.T) { 86 | l1 := List(1, 2, 3) 87 | if !l1.Take(0).Equals(List()) || !l1.Take(2).Equals(List(1, 2)) || 88 | !l1.Take(4).Equals(l1) { 89 | t.Errorf("Take(%v)", l1) 90 | } 91 | } 92 | 93 | func TestDrop(t *testing.T) { 94 | l1 := List(1, 2, 3) 95 | if !l1.Drop(0).Equals(l1) || !l1.Drop(1).Equals(List(2, 3)) || 96 | !l1.Drop(4).Equals(List()) { 97 | t.Errorf("Drop(%v)", l1) 98 | } 99 | } 100 | 101 | var prog *Thunk 102 | var fact *Thunk 103 | 104 | func TestProg(t *testing.T) { 105 | var integersFromN func(int) *Thunk 106 | integersFromN = func(n int) *Thunk { 107 | return DelayedLink(n, func() *Thunk { return integersFromN(n + 1) }) 108 | } 109 | prog = integersFromN(1) 110 | if p := prog.Take(10); !p.Equals(List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)) { 111 | t.Errorf("prog.Take(10) = (%v)", p) 112 | } 113 | } 114 | 115 | func TestFact(t *testing.T) { 116 | var factN func(int) int 117 | factN = func(n int) int { 118 | if n == 0 { 119 | return 1 120 | } 121 | return n * factN(n-1) 122 | } 123 | var makeFact func(int) *Thunk 124 | makeFact = func(n int) *Thunk { 125 | return DelayedLink(factN(n), func() *Thunk { return makeFact(n + 1) }) 126 | } 127 | fact = makeFact(0) 128 | if p := fact.Take(5); !p.Equals(List(1, 1, 2, 6, 24)) { 129 | t.Errorf("fact.Take(10) = (%v) %v", p, L(1, 1, 2, 6, 24)) 130 | } 131 | } 132 | 133 | func TestMapN(t *testing.T) { 134 | m := MapN(func(xs ...I) I { 135 | switch xs[0].(type) { 136 | case int: 137 | r := 0 138 | for _, v := range xs { 139 | r += v.(int) 140 | } 141 | return r 142 | } 143 | return xs 144 | }, List(1, 2, 2), List(3, 9, 3, 5), prog) 145 | if l := List(5, 13, 8); !m.Equals(l) { 146 | t.Errorf("MapN = %v", m) 147 | } 148 | } 149 | 150 | func TestMap(t *testing.T) { 151 | m := prog.Map(func(x I) I { 152 | return x.(int) * 2 153 | }) 154 | if l := List(2, 4, 6, 8, 10); !m.Take(5).Equals(l) { 155 | t.Errorf("MapN = %v", m) 156 | } 157 | } 158 | 159 | func TestReduceN(t *testing.T) { 160 | totalSum := func(acc I, xs ...I) I { 161 | for _, x := range xs { 162 | acc = acc.(int) + x.(int) 163 | } 164 | return acc 165 | } 166 | r := ReduceN(totalSum, 0, L(7, 8, 9), L(4, 5, 6), prog) 167 | if i := 45; r != i { 168 | t.Errorf("ReduceN = %v != %v", r, i) 169 | } 170 | } 171 | 172 | func TestReduce(t *testing.T) { 173 | sum := func(acc I, x I) I { 174 | return acc.(int) + x.(int) 175 | } 176 | r := prog.Take(10).Reduce(sum, 0) 177 | if i := 10 * (10 + 1) / 2; r != i { 178 | t.Errorf("Reduce = %v != %v", r, i) 179 | } 180 | } 181 | 182 | func TestFilterN(t *testing.T) { 183 | growing := func(xs ...I) bool { 184 | prev := xs[0].(int) 185 | for _, i := range xs[1:] { 186 | if i.(int) < prev { 187 | return false 188 | } 189 | } 190 | return true 191 | } 192 | r := FilterN(growing, L(1, 2, 3), L(2, 1, 3), L(3, 0, 3)) 193 | if l := L(L(1, 2, 3), L(3, 3, 3)); !r.Equals(l) { 194 | t.Errorf("FilterN = %v", r) 195 | } 196 | } 197 | 198 | var evens *Thunk 199 | 200 | func TestFilter(t *testing.T) { 201 | evens = prog.Filter(func(x I) bool { 202 | return x.(int)%2 == 0 203 | }) 204 | if l := L(2, 4, 6, 8, 10); !l.Equals(evens.Take(5)) { 205 | t.Errorf("Filter = %v", evens.Take(5)) 206 | } 207 | } 208 | 209 | func TestAny(t *testing.T) { 210 | if evens.Take(20).Any(func(x I) bool { 211 | return x.(int)%2 == 1 212 | }) { 213 | t.Error() 214 | } 215 | if !L(1, 7, 26, 54, 20).Any(func(x I) bool { 216 | return x.(int)%13 == 0 217 | }) { 218 | t.Error() 219 | } 220 | } 221 | 222 | func TestAll(t *testing.T) { 223 | f := func(x I) bool { 224 | return x.(int)%2 == 0 225 | } 226 | if e := evens.Take(20); !e.All(f) || L(2, 4, 10, 3, 6).All(f) { 227 | t.Error() 228 | } 229 | } 230 | 231 | func TestHas(t *testing.T) { 232 | if l := L(1, 2, 3, 4, 5); !l.Has(3) || l.Has(6) { 233 | t.Error() 234 | } 235 | } 236 | 237 | func TestMax(t *testing.T) { 238 | if l := L(1, 3, 5, 4, 2); l.Max().(int) != 5 { 239 | t.Errorf("%v", l.Max()) 240 | } 241 | if l := L(1, 3, 5.0, 4, 2); l.Max() != nil { 242 | t.Errorf("%v", l.Max()) 243 | } 244 | if l := L([]int{1, 2, 3}, 3, 5, 4, 2); l.Max() != nil { 245 | t.Errorf("%v", l.Max()) 246 | } 247 | } 248 | 249 | func TestMin(t *testing.T) { 250 | if l := L(3, 2, 5, 1, 2); l.Min().(int) != 1 { 251 | t.Errorf("%v", l.Min()) 252 | } 253 | if l := L(1, 3, 5.0, 4, 2); l.Min() != nil { 254 | t.Errorf("%v", l.Min()) 255 | } 256 | if l := L([]int{1, 2, 3}, 3, 5, 4, 2); l.Min() != nil { 257 | t.Errorf("%v", l.Min()) 258 | } 259 | } 260 | 261 | func TestTakeWhile(t *testing.T) { 262 | lt6 := func(x I) bool { 263 | return x.(int) < 6 264 | } 265 | if l := L(1, 2, 3, 4, 5); !l.Equals(prog.TakeWhile(lt6)) { 266 | t.Error() 267 | } 268 | } 269 | 270 | func TestDropWhile(t *testing.T) { 271 | lt3 := func(x I) bool { 272 | return x.(int) < 3 273 | } 274 | if l := L(3, 4, 5); !l.Equals(prog.DropWhile(lt3).Take(3)) { 275 | t.Errorf("%v", prog.DropWhile(lt3).Take(3)) 276 | } 277 | } 278 | 279 | func TestZipN(t *testing.T) { 280 | if l := L(L(1, 2), L(2, 4), L(3, 6)); !l.Equals(ZipN(prog, evens).Take(3)) { 281 | t.Error() 282 | } 283 | } 284 | 285 | func TestZip(t *testing.T) { 286 | if l := L(L(1, 2), L(2, 4), L(3, 6)); !l.Equals(prog.Zip(evens).Take(3)) { 287 | t.Error() 288 | } 289 | } 290 | 291 | func TestFlatten(t *testing.T) { 292 | if l := L(1, 2, 3, 4); !l.Equals(L(prog.Take(2), L(3, 4)).Flatten()) { 293 | t.Error() 294 | } 295 | } 296 | 297 | func TestReverse(t *testing.T) { 298 | if l := L(5, 4, 3, 2, 1); !l.Equals(prog.Take(5).Reverse()) { 299 | t.Error() 300 | } 301 | } 302 | 303 | func TestLast(t *testing.T) { 304 | if L(1, 2, 3, 4, 5).Last() != 5 { 305 | t.Error() 306 | } 307 | } 308 | 309 | func TestUpdating(t *testing.T) { 310 | prog = Updating(1, func(x I) I { 311 | return x.(int) + 1 312 | }) 313 | if l := L(1, 2, 3, 4, 5); !l.Equals(prog.Take(5)) { 314 | t.Errorf("%v", prog.Take(5)) 315 | } 316 | } 317 | 318 | func TestCurry(t *testing.T) { 319 | add := func(a, b int) int { 320 | return a + b 321 | } 322 | addTwo := Curry(add)(2).(F) 323 | if add(3, 2) != addTwo(3) { 324 | t.Errorf("%v", addTwo(3)) 325 | } 326 | } 327 | 328 | var fibo *Thunk 329 | 330 | func TestFibo(t *testing.T) { 331 | fibo = Link(1, DelayedLink(1, func() *Thunk { 332 | return MapN(func(xs ...I) I { 333 | ret := 0 334 | for _, v := range xs { 335 | ret += v.(int) 336 | } 337 | return ret 338 | }, fibo, fibo.Tail()) 339 | })) 340 | if l := L(1, 1, 2, 3, 5); !l.Equals(fibo.Take(5)) { 341 | t.Error() 342 | } 343 | } 344 | 345 | func usualFibo(n int) int { 346 | if n <= 1 { 347 | return 1 348 | } 349 | return usualFibo(n-1) + usualFibo(n-2) 350 | } 351 | 352 | func BenchmarkIterSlice(b *testing.B) { 353 | b.StopTimer() 354 | s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 355 | 11, 12, 13, 14, 15, 16, 17, 18, 19, 20} 356 | b.StartTimer() 357 | for i := 0; i < b.N; i++ { 358 | for _ = range s { 359 | } 360 | } 361 | } 362 | 363 | func BenchmarkIterList(b *testing.B) { 364 | b.StopTimer() 365 | l := L(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 366 | 11, 12, 13, 14, 15, 16, 17, 18, 19, 20) 367 | b.StartTimer() 368 | for i := 0; i < b.N; i++ { 369 | for _ = range l.Iter() { 370 | } 371 | } 372 | } 373 | 374 | func BenchmarkIterListNoMemo(b *testing.B) { 375 | b.StopTimer() 376 | StopMemo() 377 | l := L(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 378 | 11, 12, 13, 14, 15, 16, 17, 18, 19, 20) 379 | b.StartTimer() 380 | for i := 0; i < b.N; i++ { 381 | for _ = range l.Iter() { 382 | } 383 | } 384 | StartMemo() 385 | } 386 | 387 | func BenchmarkMapLoop(b *testing.B) { 388 | b.StopTimer() 389 | s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 390 | 11, 12, 13, 14, 15, 16, 17, 18, 19, 20} 391 | double := func(n int) int { 392 | return n * 2 393 | } 394 | b.StartTimer() 395 | for i := 0; i < b.N; i++ { 396 | for k, v := range s { 397 | s[k] = double(v) 398 | } 399 | _ = s[0] 400 | } 401 | } 402 | 403 | func BenchmarkMapList(b *testing.B) { 404 | b.StopTimer() 405 | l := L(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 406 | 11, 12, 13, 14, 15, 16, 17, 18, 19, 20) 407 | double := func(n I) I { 408 | return n.(int) * 2 409 | } 410 | b.StartTimer() 411 | for i := 0; i < b.N; i++ { 412 | _ = l.Map(double).At(0) 413 | } 414 | } 415 | 416 | func BenchmarkMapListNoMemo(b *testing.B) { 417 | b.StopTimer() 418 | StopMemo() 419 | l := L(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 420 | 11, 12, 13, 14, 15, 16, 17, 18, 19, 20) 421 | double := func(n I) I { 422 | return n.(int) * 2 423 | } 424 | b.StartTimer() 425 | for i := 0; i < b.N; i++ { 426 | _ = l.Map(double).At(0) 427 | } 428 | StartMemo() 429 | } 430 | 431 | func BenchmarkAppendSlices(b *testing.B) { 432 | b.StopTimer() 433 | s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 434 | 11, 12, 13, 14, 15, 16, 17, 18, 19, 20} 435 | b.StartTimer() 436 | for i := 0; i < b.N; i++ { 437 | _ = append(s, s...) 438 | } 439 | } 440 | 441 | func BenchmarkAppendLists(b *testing.B) { 442 | l := L(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 443 | 11, 12, 13, 14, 15, 16, 17, 18, 19, 20) 444 | b.StartTimer() 445 | for i := 0; i < b.N; i++ { 446 | _ = l.Append(l) 447 | } 448 | } 449 | 450 | func BenchmarkAppendLsNoMemo(b *testing.B) { 451 | b.StopTimer() 452 | StopMemo() 453 | l := L(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 454 | 11, 12, 13, 14, 15, 16, 17, 18, 19, 20) 455 | b.StartTimer() 456 | for i := 0; i < b.N; i++ { 457 | _ = l.Append(l) 458 | } 459 | StartMemo() 460 | } 461 | 462 | func BenchmarkUsualFibo(b *testing.B) { 463 | for i := 0; i < b.N; i++ { 464 | usualFibo(30) 465 | } 466 | } 467 | 468 | func BenchmarkFiboStream(b *testing.B) { 469 | for i := 0; i < b.N; i++ { 470 | _ = fibo.At(30) 471 | } 472 | } 473 | 474 | func BenchmarkFiboStrNoMemo(b *testing.B) { 475 | b.StopTimer() 476 | StopMemo() 477 | fibo = Link(1, DelayedLink(1, func() *Thunk { 478 | return MapN(func(xs ...I) I { 479 | ret := 0 480 | for _, v := range xs { 481 | ret += v.(int) 482 | } 483 | return ret 484 | }, fibo, fibo.Tail()) 485 | })) 486 | b.StartTimer() 487 | for i := 0; i < b.N; i++ { 488 | _ = fibo.At(30) 489 | } 490 | StartMemo() 491 | } 492 | --------------------------------------------------------------------------------