在clojure中进行统一成本搜索

时间:2015-08-09 08:19:35

标签: clojure

这个程序中似乎有一个错误,如果有人可以提供帮助我很感激

(defn findmincostindex [frontier]
  (loop [i 0 n 0]
    (if (< i (count frontier))
      (if (< (:cost (get frontier i)) (:cost (get frontier n)))
        (recur (inc i) i)
        (recur (inc i) n))
      n)))


(defn uniformcostsearch [graph start end]
  ((fn [frontier explored]
    (if (empty? frontier)
      "Failure"
      (let [pathwithmincost (findmincostindex (into [] frontier))
      ;(let [pathwithmincost (findmincostindex frontier)
            path (:path (get frontier pathwithmincost))
            cost (:cost (get frontier pathwithmincost))
            node (peek path)
            childs (keys (graph node))]
        (if (= node end)
          path
          (recur 
            (concat (subvec frontier 0 pathwithmincost) (subvec frontier (inc pathwithmincost))
              (map (fn [c] {:path (conj path c) :cost (+ cost ((graph node) c))})
                (filter #(not (contains? explored %)) childs)))
            (conj explored node))))))
    [{:path [start] :cost 0}] #{}))



(def graph {
        "Oradea" {
            "Zerind" 71,
            "Sibiu" 151
        },
        "Zerind" {
            "Oradea" 71,
            "Arad" 75
        },
        "Arad" {
            "Zerind" 75,
            "Sibiu" 140,
            "Timisoara" 118
        },
        "Sibiu" {
            "Oradea" 151,
            "Arad" 140,
            "Fagaras" 99,
            "Rimnicu Vilcea" 80
        },
        "Fagaras" {
            "Sibiu" 99,
            "Bucharest" 211
        },
        "Rimnicu Vilcea" {
            "Sibiu" 80,
            "Pitesti" 97,
            "Craiova" 146
        },
        "Timisoara" {
            "Arad" 118,
            "Lugoj" 111
        },
        "Lugoj" {
            "Timisoara" 111,
            "Mehadia" 70
        },
        "Pitesti" {
            "Rimnicu Vilcea" 97,
            "Craiova" 138,
            "Bucharest" 101
        },
        "Mehadia" {
            "Lugoj" 70,
            "Drobeta" 75
        },
        "Drobeta" {
            "Mehadia" 75,
            "Craiova" 120
        },
        "Craiova" {
            "Drobeta" 120,
            "Rimnicu Vilcea" 146,
            "Pitesti" 138
        },
        "Bucharest" {
            "Pitesti" 101,
            "Fagaras" 211,
            "Giurgiu" 90,
            "Urziceni" 85
        },
        "Giurgiu" {
            "Bucharest" 90
        },
        "Urziceni" {
            "Bucharest" 85,
            "Vaslui" 142,
            "Hirsova" 98
        },
        "Hirsova" {
            "Urziceni" 98,
            "Eforie" 86
        },
        "Eforie" {
            "Hirsova" 86
        },
        "Vaslui" {
            "Iasi" 92,
            "Urziceni" 142
        },
        "Iasi" {
            "Neamt" 87,
            "Vaslui" 92
        },
        "Neamt" {
            "Iasi" 87
        }})

(println (uniformcostsearch graph "Neamt" "Iasi"))
(println (uniformcostsearch graph "Neamt" "Vaslui"))
(println (uniformcostsearch graph "Bucharest" "Arad"))

它应输出这些行

['Neamt', 'Iasi']
['Neamt', 'Iasi', 'Vaslui']
['Bucharest', 'Pitesti', 'Rimnicu Vilcea', 'Sibiu', 'Arad']

但相反它说:

  

clojure.lang.LazySeq无法强制转换为   clojure.lang.IPersistentVector

当我使用

(into [] frontier)

如果我单独使用边境,则说

  

显示java.lang.NullPointerException

1 个答案:

答案 0 :(得分:2)

例外情况发生在您subvec的第一个recur中。你重复使用concat的结果,这是一个懒惰的序列,你不能采用一个懒的序列的子向量。快速解决方法是将其包装在vec

(vec (concat (subvec frontier 0 pathwithmincost) (subvec frontier (inc pathwithmincost))
              (map (fn [c] {:path (conj path c) :cost (+ cost ((graph node) c))})
                (remove explored childs))))

其他一些提示:

  1. findmincostindex基本上是min-key的重新实现,不太通用,你可以使用它来使它更干净。
  2. 集合也是函数,如果它是集合的成员,则返回参数(truthy值)。您可以使用此功能在(filter #(not (contains? explored %)) childs)))上进行一些改进 - 例如(remove explored childs)
  3. 使用destructuring可以缩短您的let
  4. 这是我的尝试:

    (let [[idx {:keys [path cost]}] (apply min-key (comp :cost second) (map-indexed vector frontier))
          node (peek path)
          childs (keys (graph node))] 
    

    这里的过程是

    1. (map-indexed vector frontier)frontier转换为索引和节点对。
    2. min-key找到(:cost (second pair))值最小的对。
    3. let将名称idx绑定到该对的索引,将pathcost绑定到:path:cost键节点。