首页 > 解决方案 > 嵌套测试的功能版本是什么?

问题描述

我正在将一些 C++ 代码转换为 Clojure,并且我想返回一个g添加了一堆边的图。我传入了顶点数、图形和测试谓词(例如,一个可能依赖于 i、j、随机性...的函数),如下所示:

(defn addSomeEdges [v g test-p]
  (doseq [i (range v)]
    (doseq [j (range (dec i))] 
      (if test-p 
          (add-edges g [i j] )
          )))
  g)

当然,问题是(add-edges)返回一个新的g. 请问如何使用 Clojure 最佳实践来捕获这个更新的图表?在 C++ 中,它看起来如此简单和自然。

标签: clojurefunctional-programming

解决方案


如果将信息拆分为两部分,则迭代地累积信息看起来像一个缩减函数:

  • 生成一堆边来考虑包括在内。
  • 测试每个边缘,如果它通过,包括它。否则将结果不变地传递

可以使用reduce

user> (defn add-edge [g i j]
        (assoc g i j))
#'user/add-edge

user> (add-edge {1 2} 2 1)
{1 2, 2 1}

user> (defn addSomeEdges [v g test-p]
        (reduce (fn [graph [i j]]        ;; this takes the current graph, the points,
                  (if (test-p graph i j) ;; decides if the edge should be created.
                    (add-edge graph i j) ;; and returns the next graph
                    graph))              ;; or returns the graph unchanged.
                g  ;; This is the initial graph
                (for [i (range v)    
                      j (range (dec i))]
                  [i j])))  ;; this generates the candidate edges to check.
#'user/addSomeEdges

让我们运行它!

user> (addSomeEdges 4 {1 2} (fn [g i j] (rand-nth [true false])))
{1 2, 2 0}
user> (addSomeEdges 4 {1 2} (fn [g i j] (rand-nth [true false])))
{1 2, 3 0}
user> (addSomeEdges 4 {1 2} (fn [g i j] (rand-nth [true false])))
{1 2, 2 0, 3 1} 

当您想到其他测试时,您可以将这些调用串联在一起:

user> (as-> {1 2} g
        (addSomeEdges 4 g (fn [g i j] (rand-nth [true false])))
        (addSomeEdges 7 g (fn [g i j] (< i j)))
        (addSomeEdges 9 g (fn [g i j] (contains? (set (keys g)) j))))
{1 2, 3 1, 4 1, 5 3, 6 4, 7 5, 8 6}

推荐阅读