使用Spock-Genesis生成基于有限项目的随机列表

qvsjd97n  于 2022-10-07  发布在  其他
关注(0)|答案(3)|浏览(230)

我使用spock-genesis,并希望使用值列表将无限列表生成器参数化。生成的项应该是最多包含随机顺序的参数化值列表的列表。

一些不变量包括:

def 'list generator'() {
    expect:
    xs.size() <= 3
    xs.findAll({x -> x == 1}).size() <= 1
    xs.findAll({x -> x == 2}).size() <= 1
    xs.findAll({x -> x == 3}).size() <= 1

    where:
    xs << listGen([1, 2, 3])
}

我即将编写自己的Generator实现,但有可能我想得太多了,有可能用已有的Spock-Gensis单元组成这样的生成器。

z4iuyo4d

z4iuyo4d1#

尝尝这个

List listGen(List list) {
    list.subsequences()
            .collect { it.permutations() }
            .inject([[]]) { result, subseq -> result + subseq }
}

listGen([1, 2, 3])的结果将是:

[[], [1], [1, 2, 3], [3, 2, 1], [2, 1, 3], [3, 1, 2], [1, 3, 2], [2, 3, 1], [2], [3, 2], [2, 3], [2, 1], [1, 2], [3], [1, 3], [3, 1]]

您的测试通过了此实现。

**更新:**根据下面评论中的OP说明,他们预计排列是随机的,因此以下代码行将使用Spock-gensis Any实现这一点:

where:
xs << Gen.any(listGen([1, 2, 3])).take(42).collect() // I assume 42 here should be random as well then
ndasle7k

ndasle7k2#

我不太确定你想要什么,只是列表[1,2,3]的每一个可能的排列的列表?如果是这样的话,这应该就足够了。

[1, 2, 3].permutations()
5anewei6

5anewei63#

在@Dmitry Khamitov的帮助下,我想出了Spock-Genesis生成器

package spock.genesis.generators.composites

import groovy.transform.CompileStatic
import spock.genesis.generators.Generator
import spock.genesis.generators.UnmodifiableIterator
import spock.genesis.generators.values.RandomElementGenerator

/**A lazy infinite {@code Generator} that returns a random subset of elements from a source Collection
 * @warning O(n!) time complexity. Starts being too expensive with lists 10+ elements
 * @param < E >     the generated type
 */
@CompileStatic
class ListSubsetGenerator<E> extends Generator<List<E>> {
    final RandomElementGenerator<List<E>> valueSource

    ListSubsetGenerator(Collection<E> source) {
        this.valueSource = new RandomElementGenerator<>(getListsSource(source))
    }

    private List<List<E>> getListsSource(Collection<E> source) {
        source.toList().subsequences()
            .collect { it.permutations() }
            .inject([[]]) { result, subseq ->
                result.addAll(subseq)
                result
            } as List<List<E>>
    }

    @Override
    UnmodifiableIterator<List<E>> iterator() {
        new UnmodifiableIterator<List<E>>() {
            private final Iterator<List<E>> source = valueSource.iterator()

            @Override
            boolean hasNext() {
                source.hasNext()
            }

            @Override
            List<E> next() {
                source.next()
            }
        }
    }

    @Override
    Generator<List<E>> seed(Long seed) {
        super.seed(seed)
        valueSource.seed(seed)
        this
    }
}

以下是一些测试:

class ListSubsetGeneratorTest extends Specification {
    @Iterations(100)
    def 'test invariants'() {
        expect:
        xs.size() <= 3
        xs.findAll({x -> x == 1}).size() <= 1
        xs.findAll({x -> x == 2}).size() <= 1
        xs.findAll({x -> x == 3}).size() <= 1
        xs.every { [1, 2, 3].contains(it) }

        where:
        xs << new ListSubsetGenerator([1, 2, 3])
    }

    def 'setting seed produces the same sequences for different generators'() {
        given:
        def elements = ['a', 'b', 'c', 'd']
        def xs = new ListSubsetGenerator(elements).seed(seed).take(100).realized
        def ys = new ListSubsetGenerator(elements).seed(seed).take(100).realized

        expect:
        xs == ys

        where:
        seed << [Long.MIN_VALUE, 100, Long.MAX_VALUE]
    }
}

相关问题