Desafíos multiverso y cruzados

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.








All Articles