Una vez leí en Habré un artículo "Transportando un lobo, una cabra y un repollo a través de un río con efectos en Haskell" , que me gustó tanto que decidí escribir un marco para toda la clase de problemas de cruces usando diseño multi-paradigma . Finalmente logramos encontrar el tiempo, y ahora, después de casi un año, el marco está listo. Ahora los personajes, sus interacciones y la descripción del resultado deseado se establecen a través de un lenguaje específico de dominio , que le permite resolver cualquier rompecabezas de este tipo con una conclusión paso a paso. A continuación se muestra un desglose paso a paso de la implementación de DSL. El artículo es adecuado para aquellos que están estudiando el idioma Kotlin o simplemente están interesados en ejemplos de su uso. Algunos detalles menores (como las importaciones y la producción) se han omitido por brevedad.
El personaje se puede describir fácilmente como una clase abierta para la herencia:
open class Person(private val name: String)
, :
typealias Place = Set<Person>
. , , :
abstract class QuantumBoat(val left: Place, val right: Place) {
abstract fun invert(): List<QuantumBoat>
fun where(condition: Place.() -> Boolean, select: QuantumBoat.() -> Boolean) =
Multiverse(this, condition).search(selector)
}
where, N . (condition) , (selector) . , , , :)
, :
class LeftBoat(left: Place, right: Place) : QuantumBoat(left, right) {
override fun invert() =
left.map {
RightBoat(left - it - Farmer, right + it + Farmer)
} + RightBoat(left - Farmer, right + Farmer)
}
. . , , . . , Kotlin .
, , , , . , :
typealias History = LinkedList<QuantumBoat>
fun Sequence<History>.fork() = sequence {
for (history in this@fork) {
for (forked in history.last.invert()) {
yield((history.clone() as History).apply {
add(forked)
})
}
}
}
( ) (). , yield.
( ):
/**
*
* @param boat
* @param condition
*/
class Multiverse(boat: QuantumBoat, val condition: Place.() -> Boolean) {
/**
*
*/
private var multiverse = sequenceOf(historyOf(boat))
/**
*
* @param selector
* @return
*/
tailrec fun search(selector: QuantumBoat.() -> Boolean): List<History> {
multiverse = multiverse.fork().distinct().filter {
it.last.left.condition()
&& it.last.right.condition()
}
val results = multiverse.filter { it.last.selector() }.toList()
return when {
results.isNotEmpty() -> results
else -> search(selector)
}
}
}
, kotlinc . : . ( ), . !
, DSL , :
object Wolf : Person
object Goat : Person
object Cabbage : Person
fun Place.isCompatible() =
contains(Farmer) ||
(!contains(Wolf) || !contains(Goat)) &&
(!contains(Goat) || !contains(Cabbage))
fun main() {
val property = setOf(Wolf, Goat, Cabbage)
//
LeftBoat(property)
//
.where(Place::isCompatible)
// ,
//
{ right.containsAll(property) }
//
.forEach(History::prettyPrint)
}
Esto es lo que sucedió, lo inserto con una captura de pantalla, porque Habr no digiere emoticones:
Que tenga un buen día y más tiempo para escribir su propio DSL :)
El código fuente está aquí: demidko / river-crossing-puzzle Las
críticas y sugerencias sobre cómo mejorar son bienvenidas.