2009-10-20 19 views
8

NapisaĹ,em prosty przeszukiwanie w głąb w Scala z funkcji rekurencyjnej tak:przerwa lub zwarcie fałdę w Scala

search(labyrinth, path, goal) 

gdzie labiryntowe jest określenie problemów (wykres czy inne), ścieżka to lista, która zawiera dotychczasową ścieżkę, a celem jest określenie stanu celu. Funkcja zwraca ścieżkę do celu jako listę i zero, jeśli nie można znaleźć żadnej ścieżki.

Funkcja zostanie rozwinięta, np. znajduje wszystkie odpowiednie następne węzły (kandydatów), a następnie musi rekurencyjnie nazywać siebie.

zrobić to przez

candidates.foldLeft(Nil){ 
    (solution, next) => 
    if(solution == Nil) 
     search(labyrinth, next :: path, goal) 
    else 
     solution 
} 

Należy pamiętać, że pominięto kilka unescessary szczegóły. Wszystko do tej pory działa dobrze. Ale gdy rozwiązanie znajdzie się w wywołaniu foldLeft, to rozwiązanie zostanie po prostu skopiowane przez część else instrukcji if. Czy istnieje sposób na uniknięcie tego przez złamanie foldLeft, a może za pomocą innej funkcji zamiast foldLeft? Właściwie mógłbym prawdopodobnie napisać wersję foldLeft, która zrywa się, gdy sam "not Nil" zostanie zwrócony. Ale czy jest tam jeden interfejs API?

+0

Czego dokładnie chcesz uniknąć? Nigdzie nie ma kopiowania. – Apocalisp

+0

Czy nie wywoła co najmniej wywołania funkcji dla każdego pozostałego elementu na liście? –

+0

Z foldLeft, tak. Ale, znowu, foldLeft jest skłonny do zrobienia czegoś, na co nie ma zamiaru. –

Odpowiedz

4

Nie jestem pewien, czy rozumiem chęć zwarcia pętli. Czy to jest kosztowne, aby powtórzyć kandydatów? Czy lista kandydatów jest potencjalnie duża?

Może użyć „znaleźć” sposób:

candidates.find { c => 
    Nil != search(labyrinth, c :: path, goal) 
} match { 
    case Some(c) => c :: path 
    case None => Nil 
} 

Jeśli potencjał głębokość przestrzeni poszukiwań jest duża, można przelać swój stack (okucia, biorąc pod uwagę to imię miejscu). Ale jest to temat na inny post.

Dla chichotów, tutaj jest faktyczna działająca implementacja. Musiałem wprowadzić lokalną zmienną zmienną (fullPath) głównie z lenistwa, ale jestem pewien, że mógłbyś to zrobić.

object App extends Application { 

    // This impl searches for a specific factor in a large int 
    type SolutionNode = Int 
    case class SearchDomain(number: Int) { 
     def childNodes(l: List[Int]): List[Int] = { 
      val num = if (l.isEmpty) number else l.head 
      if (num > 2) { 
       (2 to (num - 1)) find { 
        n => (num % n)==0 
       } match { 
        case Some(i) => List(i, num/i) 
        case None => List() 
       } 
      } 
      else List() 
     } 
    } 
    type DesiredResult = Int 


    def search (labyrinth: SearchDomain, path: List[SolutionNode], goal: DesiredResult): List[SolutionNode] = { 

     if (!path.isEmpty && path.head == goal) return path 
     if (path.isEmpty) return search(labyrinth, List(labyrinth.number), goal) 

     val candidates: List[SolutionNode] = labyrinth.childNodes(path) 
     var fullPath: List[SolutionNode] = List() 
     candidates.find { c => 
      fullPath = search(labyrinth, c :: path, goal) 
      !fullPath.isEmpty 
     } match { 
      case Some(c) => fullPath 
      case None => Nil 
     } 
    } 

    // Is 5 a factor of 800000000? 
    val res = search(SearchDomain(800000000), Nil, 5) 
    println(res) 

} 
+0

Nie będzie więcej przepełnień stosu przy użyciu 'find' niż byłoby za pomocą' foldLeft'. Używanie 'find' idealnie pasuje do tego, czego chce: zdobądź pierwszego kandydata, dla którego można znaleźć rozwiązanie. –

+0

Dobrze. Ale, w zależności od domeny problemu, przepełnienie stosu może być problemem dla ziggystaru, ortogonalnie do pierwotnego pytania. Hej, po prostu miałem pomysł na pytanie! –

+0

To wygląda na dobre rozwiązanie. Dziękuję Ci bardzo. I: Problemy z wyszukiwaniem nie są duże. Pytanie rodzi się z ciekawości, jak to zrobić "dobrze". – ziggystar

0

Lubię Mitch Blevins rozwiązanie, gdyż jest to idealne dopasowanie do algorytmu. Możesz być zainteresowany my own solution na inny problem z labiryntem.