0

使用 Clojure 的test.checklet生成器中的示例,生成一个非空字符串列表,将该列表提供给另一个生成器以从中选择一个字符串,然后创建一个包含字符串列表和所选字符串的映射。在 Clojure 中,它看起来如下:

(gen/let [list-of-strings (gen/not-empty (gen/list gen/string))
          a-string        (gen/element list-of-strings)]   ;; use the generated list-of-strings above
  {:all-strings list-of-strings
   :selected    a-string})

受到io.kotest.property.arbitrary.bind启发,我尝试按如下方式实现它,但它不起作用(Kotlin 编译器吐出“类型推断失败”):

fun <A, B, T: Any> let(genA: Gen<A>, genB: (A) -> Gen<B>, bindFn: (A, B) -> T): Arb<T> {
    return arb { rs ->
        val iterA = genA.generate(rs).iterator()

        generateSequence {
            val a = iterA.next()
            val iterB = genB(a.value).generate(rs).iterator()
            val b = iterB.next()
            bindFn(a.value, b.value)
        }
    }
}
4

2 回答 2

1

原来删除bindFn参数解决了这个问题,但解决方案看起来有点难看,因为它需要返回 a Pair

fun <A, B> let(genA: Gen<A>, genBFn: (A) -> Gen<B>): Arb<Pair<A, B>> {
    return arb { rs ->
        val iterA = genA.generate(rs).iterator()

        generateSequence {
            val a = iterA.next().value

            // could combine the following to one line, but split for clarity
            val genB = genBFn(a)
            val iterB = genB.generate(rs).iterator()
            Pair(a, iterB.next().value)
        }
    }
}

然后有了上面的,使用它看起来如下:

class StringTest : StringSpec({
    "element is in list" {
        val letGen = let(
            Arb.list(Arb.string(), range=1..100),    // genA
            { xs -> Arb.element(xs) }                // genBFn
        )

        forAll(letGen) { (xs, x) ->
            x in xs
        }
    }
})
于 2020-06-16T00:18:41.117 回答
0

从上述解决方案中汲取灵感并写了一个较短的解决方案

    fun <A, B> Gen<A>.then(genFn: (A) -> Gen<B>): Arb<Pair<A, B>> =
        arbitrary { rs ->
            val first = this.generate(rs).first().value
            val second = genFn(first).generate(rs).first().value
            Pair(first, second)
        }
class StringTest : StringSpec({
    "element is in list" {
        val dependArb = 
            Arb.list(Arb.string(), range=1..100).then { Arb.element(it) } // genBFn

        forAll(dependArb) { (xs, x) ->
            x in xs
        }
    }
})
于 2021-09-18T08:26:45.000 回答