我有一个看起来像这样的interface
:
interface FontRegionTransformer<R> {
fun transform(region: R, textCharacter: TextCharacter): R
}
我不是范畴论方面的专家,但正如我之前所知道的,这个结构是一个幺半群(是吗?(,我可以组合任意数量的函数,这些函数一起R
和返回R
。
这就是我现在拥有的:
var image = source.getSubimage(meta.x * width, meta.y * height, width, height)
regionTransformers.forEach {
image = it.transform(image, textCharacter)
}
这有效,但我有一个问题:如何将FontRegionTransformer
s 的List
组合到单个函数中?我可以在不向界面添加compose
函数的情况下做到这一点吗?我用reduce
试了一下,但没有点击。
澄清:我想实现的是将存储在regionTransformers
中的函数组合成一个函数,而不是这里的循环:
var image = source.getSubimage(meta.x * width, meta.y * height, width, height)
regionTransformers.forEach {
image = it.transform(image, textCharacter)
}
我想要这样的东西:
var image = source.getSubimage(meta.x * width, meta.y * height, width, height)
return combinedTransformers.invoke(image)
对于组合定义,不太清楚,当调用组合转换器时,第二个FontRegionTransformer<R>
应该得到什么textCharacter
。在这里,我假设它是传递到调用中的相同textCharacter
,并且自然地传递给第一个转换器。
您可以将自定义合成操作实现为FontRegionTransformer<R>
的扩展:
fun <R> FontRegionTransformer<R>.compose(other: FontRegionTransformer<R>) =
object : FontRegionTransformer<R> {
override fun transform(region: R, textCharacter: TextCharacter): R {
val firstResult = this@compose.transform(region, textCharacter)
return other.transform(firstResult, textCharacter)
}
}
你可以将infix
修饰符添加到compose
以使用中缀表示法a compose b
,或者让它重载运算符+
或*
,如果你想把它称为a * b
。或者使用非分机顶级函数进行compose(a, b)
调用。
然后,您可以编写两个FontRegionTransformer
:
val composed = first.compose(second)
要将变压器列表组合成一个,请使用reduce
:
val transformers: List<FontRegionTransformer<SomeType>> = TODO()
val composition = transformers.reduce { a, b -> a.compose(b) }
对于幺半群的FontRegionTransformer<R>
,组合操作应该是结合的(a ∘ (b ∘ c)
应该等价于所有a
、b
和c
的(a ∘ b) ∘ c
(,上面的实现似乎满足了这个要求。但是,严格来说,它也应该有一个中立的元素,例如n
a ∘ n = n ∘ a = a
任何a
.这两个要求不能用 Kotlin 类型系统来表达,而应该是合同的一部分。
单语句解决方案是将compose
内联到reduce
调用中:
val composition = transformers.reduce { a, b ->
object : FontRegionTransformer<SomeType> {
override fun transform(region: SomeType, textCharacter: TextCharacter) =
a.transform(region, textCharacter).let { b.transform(it, textCharacter) }
}
}