Как прекратить возвращаться в Скала? - PullRequest
7 голосов
/ 19 января 2012

Предположим, я решаю проблему (например, N-Queen) с возвратом . Что делать, если я хочу найти единственное (1-е) решение, а не все из них.

Полагаю, я могу сделать это обязательно (например, с изменяемым логическим флагом). Интересно, как я могу это сделать функционально .

Ответы [ 3 ]

14 голосов
/ 20 января 2012

Хотя Scala Option будет работать здесь, как указывали два других ответа, более идиоматическим функциональным подходом было бы использование «ленивого списка» - или Stream в Scala - представлять множество решений.

Я обнаружил, что пишу такой код, например:

trait Node[A] {
  def children: Stream[A with Node[A]]

  def dfs(f: A => Boolean): Stream[A] = this.children.flatMap {
    child => if (f(child)) Stream(child) else child.dfs(f)
  }
}

Теперь предположим, что у меня есть класс Board, который расширяет Node[Board] и который имеет реализацию метода children, который возвращает все допустимые платы с одним дополнительным компонентом. Предположим, у него также есть некоторые другие полезные вещи, такие как size метод, объект-компаньон с empty и т. Д.

Затем я могу написать следующее, чтобы получить Stream решений:

val solutions = Board.empty.dfs(_.size == 8)

A Stream ленив и оценивает только его голову, поэтому сейчас мы только обыскали дерево достаточно далеко, чтобы найти первое решение. Мы можем получить это решение, используя head:

scala> solutions.head
res1: Board = 
o . . . . . . .
. . . . o . . .
. . . . . . . o
. . . . . o . .
. . o . . . . .
. . . . . . o .
. o . . . . . .
. . . o . . . .

Или что угодно. Но я могу также получить другие результаты, если я хочу их:

scala> solutions(10)
res2: Board = 
. o . . . . . .
. . . . . . o .
. . . . o . . .
. . . . . . . o
o . . . . . . .
. . . o . . . .
. . . . . o . .
. . o . . . . .

При этом поиск по дереву будет достаточным, чтобы найти десятое решение, а затем останавливается.

Большим преимуществом Stream перед подходом Option является то, что я могу получить дополнительные результаты, если они мне нужны, без дополнительной оплаты за первый.

5 голосов
/ 20 января 2012

Вот простой случай с поиском в глубину, который останавливается, когда он находит то, что ищет.Он использует Option, как упоминалось в ответе Криса К. *

case class Tree[A](v: A, subtrees: Tree[A]*) {
  def dfs(s: A): Option[A] = {
    println("visiting " + v)
    subtrees.foldLeft(if(v == s) Some(v) else None)((r, t) => 
      if(r.isDefined) 
        r 
      else 
        t.dfs(s)
    )
  }
  override def toString() = "Tree(%s%s%s)".format(v, if(subtrees.nonEmpty) ", " else "", subtrees.mkString(", "))
}

Использование:

scala> val t = Tree(1, Tree(2, Tree(3), Tree(4)), Tree(5, Tree(6), Tree(7)))
t: Tree[Int] = Tree(1, Tree(2, Tree(3), Tree(4)), Tree(5, Tree(6), Tree(7)))

Дерево t выглядит как

     1
   /   \
  2     5
 / \   / \
3   4 6   7    

Таким образом, мы можем искать элемент и отслеживать узлы, которые он посещает:

scala> t.dfs(6)
visiting 1
visiting 2
visiting 3
visiting 4
visiting 5
visiting 6
res42: Option[Int] = Some(6)

Обратите внимание, что мы больше не посещаем узлы после того, как находим то, что ищем.

2 голосов
/ 19 января 2012

Предполагая, что вы используете рекурсивную функцию поиска, ваша функция должна либо возвращать результат (т. Е. Расположение ферзей), либо указание на то, что для этой ветви не может быть найдено никакого результата.Scala, вероятно, имеет опцию / возможно тип, вы можете использовать это.Этот совет в равной степени применим к любому функциональному языку.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...