Défis multivers et croisés

Une fois que j'ai lu sur Habré un article "Transporter un loup, une chèvre et un chou à travers une rivière avec effets à Haskell" , que j'ai tellement aimé que j'ai décidé d'écrire un cadre pour toute la classe de problèmes de traversée en utilisant la conception multi-paradigme . Finalement, nous avons réussi à trouver le temps, et maintenant, après presque un an, le cadre est prêt. Désormais, les personnages, leurs interactions et la description du résultat souhaité sont définis via un langage spécifique au domaine , ce qui vous permet de résoudre toutes les énigmes de ce type avec une conclusion étape par étape. Vous trouverez ci-dessous une ventilation étape par étape de la mise en œuvre DSL. L'article convient à ceux qui étudient la langue Kotlin ou qui sont simplement intéressés par des exemples de son utilisation. Certains détails mineurs (comme les importations et la production) ont été omis par souci de concision.





Le caractère peut être facilement décrit comme une classe ouverte pour l'héritage :





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)
}
      
      



Voici ce qui s'est passé, je l'insère avec une capture d'écran, car Habr ne digère pas les émoticônes :





Passez une bonne journée et plus de temps pour écrire votre propre DSL :)





Le code source est ici : demidko / river-crossing-puzzle Les

critiques et suggestions sur la façon de faire mieux sont les bienvenues.








All Articles