|
1 | 1 | package fj.test; |
2 | 2 |
|
3 | | -import static fj.Bottom.error; |
4 | | -import fj.Effect; |
5 | 3 | import fj.F; |
6 | 4 | import fj.Function; |
7 | | -import static fj.Function.flip; |
8 | | -import static fj.Function.curry; |
9 | 5 | import fj.P2; |
10 | | -import static fj.P2.__1; |
11 | 6 | import fj.Unit; |
12 | | -import fj.F2; |
13 | | -import static fj.data.Array.array; |
| 7 | +import fj.control.Trampoline; |
| 8 | +import fj.data.Array; |
14 | 9 | import fj.data.List; |
15 | | -import static fj.data.List.nil; |
16 | | -import static fj.data.List.replicate; |
17 | 10 | import fj.data.Option; |
18 | 11 | import fj.function.Effect1; |
19 | 12 |
|
| 13 | +import static fj.Bottom.error; |
| 14 | +import static fj.Function.curry; |
| 15 | +import static fj.Function.flip; |
20 | 16 | import static fj.Monoid.intAdditionMonoid; |
21 | 17 | import static fj.Ord.intOrd; |
22 | | - |
| 18 | +import static fj.P.lazy; |
| 19 | +import static fj.P2.__1; |
| 20 | +import static fj.control.Trampoline.pure; |
| 21 | +import static fj.control.Trampoline.suspend; |
| 22 | +import static fj.data.Array.array; |
| 23 | +import static fj.data.List.cons; |
| 24 | +import static fj.data.List.nil; |
| 25 | +import static fj.data.List.replicate; |
23 | 26 | import static java.lang.Math.max; |
24 | 27 | import static java.lang.Math.min; |
25 | 28 |
|
@@ -560,47 +563,231 @@ public static <A> Gen<List<A>> listOf1(final Gen<A> g) { |
560 | 563 | return listOf(g, 1); |
561 | 564 | } |
562 | 565 |
|
| 566 | + /** |
| 567 | + * Returns a generator that picks one element from the given list. If the given list is empty, then the |
| 568 | + * returned generator will never produce a value. |
| 569 | + * |
| 570 | + * @param as The list from which to pick an element. |
| 571 | + * @return A generator that picks an element from the given list. |
| 572 | + */ |
| 573 | + public static <A> Gen<A> pickOne(List<A> as) { |
| 574 | + // This is the fastest of the four; functionally, any of them would do |
| 575 | + return wordOf(1, as).map(List::head); |
| 576 | + } |
| 577 | + |
563 | 578 | /** |
564 | 579 | * Returns a generator of lists that picks the given number of elements from the given list. If |
565 | 580 | * the given number is less than zero or greater than the length of the given list, then the |
566 | 581 | * returned generator will never produce a value. |
| 582 | + * <p> |
| 583 | + * Note: pick is synonymous with combinationOf |
567 | 584 | * |
568 | 585 | * @param n The number of elements to pick from the given list. |
569 | 586 | * @param as The list from which to pick elements. |
570 | 587 | * @return A generator of lists that picks the given number of elements from the given list. |
571 | 588 | */ |
572 | | - public static <A> Gen<List<A>> pick(final int n, final List<A> as) { |
573 | | - return n < 0 || n > as.length() ? Gen.<List<A>>fail() : sequenceN(n, choose(0, as.length() - 1)).map(new F<List<Integer>, List<A>>() { |
574 | | - public List<A> f(final List<Integer> is) { |
575 | | - List<A> r = nil(); |
| 589 | + @Deprecated |
| 590 | + public static <A> Gen<List<A>> pick(int n, List<A> as) { |
| 591 | + return combinationOf(n, as); |
| 592 | + } |
576 | 593 |
|
577 | | - List<Integer> iis = is.sort(intOrd); |
578 | | - List<P2<A, Integer>> aas = as.zipIndex(); |
| 594 | + /** |
| 595 | + * Returns a generator of lists that picks the given number of elements from the given list. The selection is |
| 596 | + * a combination without replacement of elements from the given list, i.e. |
| 597 | + * <ul> |
| 598 | + * <li>For any given selection, a generated list will always contain its elements in the same order</li> |
| 599 | + * <li>An element will never be picked more than once</li> |
| 600 | + * </ul> |
| 601 | + * <p> |
| 602 | + * If the given number is less than zero or greater than the length of the given list, then the |
| 603 | + * returned generator will never produce a value. |
| 604 | + * |
| 605 | + * @param n The number of elements to pick from the given list. |
| 606 | + * @param as The list from which to pick elements. |
| 607 | + * @return A generator of lists that picks the given number of elements from the given list. |
| 608 | + */ |
| 609 | + public static <A> Gen<List<A>> combinationOf(int n, List<A> as) { |
| 610 | + int aLength = as.length(); |
| 611 | + return ((n >= 0) && (n <= aLength)) ? |
| 612 | + parameterised(s -> r -> { |
| 613 | + final class Tramp { |
| 614 | + |
| 615 | + // Picks elements in constant stack space |
| 616 | + private Trampoline<List<A>> tramp(List<A> remainAs, int remainN, int remainALength) { |
| 617 | + return suspend(lazy(() -> |
| 618 | + (remainN == 0) ? |
| 619 | + // We have picked N elements; stop |
| 620 | + pure(nil()) : |
| 621 | + // For M remaining elements of which N will be picked, pick remainAs.head() with probability N/M |
| 622 | + (r.choose(0, remainALength - 1) < remainN) ? |
| 623 | + tramp(remainAs.tail(), remainN - 1, remainALength - 1) |
| 624 | + .map(pickedTail -> cons(remainAs.head(), pickedTail)) : |
| 625 | + tramp(remainAs.tail(), remainN, remainALength - 1))); |
| 626 | + } |
579 | 627 |
|
580 | | - //noinspection ForLoopWithMissingComponent |
581 | | - for(; iis.isNotEmpty() && aas.isNotEmpty(); aas = aas.tail()) |
582 | | - if(iis.head().equals(aas.head()._2())) |
583 | | - iis = iis.tail(); |
584 | | - else |
585 | | - r = r.snoc(aas.head()._1()); |
| 628 | + } |
| 629 | + return value(new Tramp().tramp(as, n, aLength).run()); |
| 630 | + }) : |
| 631 | + fail(); |
| 632 | + } |
586 | 633 |
|
587 | | - return r; |
588 | | - } |
589 | | - }); |
| 634 | + /** |
| 635 | + * Returns a generator of lists that picks the given number of elements from the given list. The selection is |
| 636 | + * a combination with replacement of elements from the given list, i.e. |
| 637 | + * <ul> |
| 638 | + * <li>For any given selection, a generated list will always contain its elements in the same order</li> |
| 639 | + * <li>Each element may be picked more than once</li> |
| 640 | + * </ul> |
| 641 | + * <p> |
| 642 | + * If the given number is less than zero, then the returned generator will never produce a value. Note that, |
| 643 | + * with replacement, the given number may be larger than the length of the given list. |
| 644 | + * |
| 645 | + * @param n The number of elements to pick from the given list. |
| 646 | + * @param as The list from which to pick elements. |
| 647 | + * @return A generator of lists that picks the given number of elements from the given list. |
| 648 | + */ |
| 649 | + public static <A> Gen<List<A>> selectionOf(int n, List<A> as) { |
| 650 | + Array<A> aArr = as.toArray(); |
| 651 | + return (n >= 0) ? |
| 652 | + pick(indexWord(n, aArr.length()).map(indexes -> indexes.sort(intOrd)), aArr) : |
| 653 | + fail(); |
| 654 | + } |
| 655 | + |
| 656 | + /** |
| 657 | + * Returns a generator of lists that picks the given number of elements from the given list. The selection is |
| 658 | + * a permutation without replacement of elements from the given list, i.e. |
| 659 | + * <ul> |
| 660 | + * <li>For any given selection, a generated list may contain its elements in any order</li> |
| 661 | + * <li>An element will never be picked more than once</li> |
| 662 | + * </ul> |
| 663 | + * <p> |
| 664 | + * If the given number is less than zero or greater than the length of the given list, then the |
| 665 | + * returned generator will never produce a value. |
| 666 | + * |
| 667 | + * @param n The number of elements to pick from the given list. |
| 668 | + * @param as The list from which to pick elements. |
| 669 | + * @return A generator of lists that picks the given number of elements from the given list. |
| 670 | + */ |
| 671 | + public static <A> Gen<List<A>> permutationOf(int n, List<A> as) { |
| 672 | + return parameterised(s -> r -> |
| 673 | + combinationOf(n, as).map(combination -> { |
| 674 | + // Shuffle combination using the Fisher-Yates algorithm |
| 675 | + Array<A> aArr = combination.toArray(); |
| 676 | + int length = aArr.length(); |
| 677 | + for (int i = length - 1; i > 0; --i) { |
| 678 | + int j = r.choose(0, i); |
| 679 | + A tmp = aArr.get(i); |
| 680 | + aArr.set(i, aArr.get(j)); |
| 681 | + aArr.set(j, tmp); |
| 682 | + } |
| 683 | + return aArr.toList(); |
| 684 | + })); |
| 685 | + } |
| 686 | + |
| 687 | + /** |
| 688 | + * Returns a generator of lists that picks the given number of elements from the given list. The selection is |
| 689 | + * a permutation with replacement of elements from the given list, i.e. |
| 690 | + * <ul> |
| 691 | + * <li>For any given selection, a generated list may contain its elements in any order</li> |
| 692 | + * <li>Each element may be picked more than once</li> |
| 693 | + * </ul> |
| 694 | + * <p> |
| 695 | + * If the given number is less than zero, then the returned generator will never produce a value. Note that, |
| 696 | + * with replacement, the given number may be larger than the length of the given list. |
| 697 | + * |
| 698 | + * @param n The number of elements to pick from the given list. |
| 699 | + * @param as The list from which to pick elements. |
| 700 | + * @return A generator of lists that picks the given number of elements from the given list. |
| 701 | + */ |
| 702 | + public static <A> Gen<List<A>> wordOf(int n, List<A> as) { |
| 703 | + Array<A> aArr = as.toArray(); |
| 704 | + return (n >= 0) ? |
| 705 | + pick(indexWord(n, aArr.length()), aArr) : |
| 706 | + fail(); |
| 707 | + } |
| 708 | + |
| 709 | + private static Gen<List<Integer>> indexWord(int n, int m) { |
| 710 | + return sequenceN(n, choose(0, m - 1)); |
| 711 | + } |
| 712 | + |
| 713 | + private static <A> Gen<List<A>> pick(Gen<List<Integer>> indexesGen, Array<A> as) { |
| 714 | + return indexesGen.map(indexes -> |
| 715 | + indexes.foldLeft((acc, index) -> cons(as.get(index), acc), List.<A>nil()).reverse()); |
590 | 716 | } |
591 | 717 |
|
592 | 718 | /** |
593 | 719 | * Returns a generator of lists that produces some of the values of the given list. |
| 720 | + * <p> |
| 721 | + * Note: someOf is synonymous with someCombinationOf |
594 | 722 | * |
595 | 723 | * @param as The list from which to pick values. |
596 | 724 | * @return A generator of lists that produces some of the values of the given list. |
597 | 725 | */ |
598 | | - public static <A> Gen<List<A>> someOf(final List<A> as) { |
599 | | - return choose(0, as.length()).bind(new F<Integer, Gen<List<A>>>() { |
600 | | - public Gen<List<A>> f(final Integer i) { |
601 | | - return pick(i, as); |
602 | | - } |
603 | | - }); |
| 726 | + @Deprecated |
| 727 | + public static <A> Gen<List<A>> someOf(List<A> as) { |
| 728 | + return someCombinationOf(as); |
| 729 | + } |
| 730 | + |
| 731 | + /** |
| 732 | + * Returns a generator of lists that produces some of the values of the given list. The selection is |
| 733 | + * a combination without replacement of elements from the given list, i.e. |
| 734 | + * <ul> |
| 735 | + * <li>For any given selection, a generated list will always contain its elements in the same order</li> |
| 736 | + * <li>An element will never be picked more than once</li> |
| 737 | + * </ul> |
| 738 | + * |
| 739 | + * @param as The list from which to pick values. |
| 740 | + * @return A generator of lists that produces some of the values of the given list. |
| 741 | + */ |
| 742 | + public static <A> Gen<List<A>> someCombinationOf(List<A> as) { |
| 743 | + return choose(0, as.length()).bind(n -> combinationOf(n, as)); |
| 744 | + } |
| 745 | + |
| 746 | + /** |
| 747 | + * Returns a generator of lists that produces some of the values of the given list. The selection is |
| 748 | + * a combination with replacement of elements from the given list, i.e. |
| 749 | + * <ul> |
| 750 | + * <li>For any given selection, a generated list will always contain its elements in the same order</li> |
| 751 | + * <li>Each element may be picked more than once</li> |
| 752 | + * </ul> |
| 753 | + * |
| 754 | + * @param maxLength The maximum length of a generated list |
| 755 | + * @param as The list from which to pick values. |
| 756 | + * @return A generator of lists that produces some of the values of the given list. |
| 757 | + */ |
| 758 | + public static <A> Gen<List<A>> someSelectionOf(int maxLength, List<A> as) { |
| 759 | + return choose(0, maxLength).bind(n -> selectionOf(n, as)); |
| 760 | + } |
| 761 | + |
| 762 | + /** |
| 763 | + * Returns a generator of lists that produces some of the values of the given list. The selection is |
| 764 | + * a permutation without replacement of elements from the given list, i.e. |
| 765 | + * <ul> |
| 766 | + * <li>For any given selection, a generated list may contain its elements in any order</li> |
| 767 | + * <li>An element will never be picked more than once</li> |
| 768 | + * </ul> |
| 769 | + * |
| 770 | + * @param as The list from which to pick values. |
| 771 | + * @return A generator of lists that produces some of the values of the given list. |
| 772 | + */ |
| 773 | + public static <A> Gen<List<A>> somePermutationOf(List<A> as) { |
| 774 | + return choose(0, as.length()).bind(n -> permutationOf(n, as)); |
| 775 | + } |
| 776 | + |
| 777 | + /** |
| 778 | + * Returns a generator of lists that produces some of the values of the given list. The selection is |
| 779 | + * a permutation with replacement of elements from the given list, i.e. |
| 780 | + * <ul> |
| 781 | + * <li>For any given selection, a generated list may contain its elements in any order</li> |
| 782 | + * <li>Each element may be picked more than once</li> |
| 783 | + * </ul> |
| 784 | + * |
| 785 | + * @param maxLength The maximum length of a generated list |
| 786 | + * @param as The list from which to pick values. |
| 787 | + * @return A generator of lists that produces some of the values of the given list. |
| 788 | + */ |
| 789 | + public static <A> Gen<List<A>> someWordOf(int maxLength, List<A> as) { |
| 790 | + return choose(0, maxLength).bind(n -> wordOf(n, as)); |
604 | 791 | } |
605 | 792 |
|
606 | 793 | /** |
|
0 commit comments