作为过程的函数的动态(树)结构(和 Clojure 中的实现)
Dynamic (tree) structure of functions as process (and implementation in Clojure)
这听起来可能很奇怪,因为我找不到一个确切的术语来描述我正在尝试做的事情。
我正在开发一个应用程序,给定一组规则(很容易转化为函数)和 input/output 对(不太容易转化为代码),将允许构建一棵树应用于给定输入以达到给定输出的规则数。
它类似于专家系统,除了目标不是确定 "better"(通过某种质量)rule/function 树(实际上将输入映射到输出本身是次要的)而是能够根据一些限制或业务规则构建这些树。
我正尝试在 Clojure 中执行此操作,但我也会感谢更多一般性建议,因为我什至无法弄清楚如何简短地调用这种东西。
说到细节,据说我有一张大的细节平面图。
我有一长串函数,我将它们转换成几乎相同的功能:每个函数都采用这个大的平面细节图,并将规则应用于该规则涉及的任何值。函数有副作用(记录它做了什么),并且据推测,所述(将是)树结构使用单个布尔输出来确定进入哪个分支(如果树在此函数上分裂)。
我的想法是我可以采用一个函数并将其声明为树的根。然后采用另一个函数并将其声明为下一个要执行的函数,或者采用两个函数并将它们声明为根的两个下一个分支,具体取决于根函数输出。等等等等。
而且我需要能够根据需要多次执行此操作,从而生成符合某些要求的树。
我将完成所有的逻辑,但我需要一个结构,允许我将函数树(我什至可以自己构造,只要我只需要将它指定为像列表一样简单的东西)到给定输入映射,而不必为我将尝试做的每棵树手动编码整个过程树。
现实生活中的例子是一个大型的树状数据结构(我们可以很容易地简化输入),每个客户都希望根据自己的一套规则来描述(函数的副作用),当它是已处理(达到输出)。
这个 "procedure" 有比这个长描述更常见的名字吗?
Java/Clojure 是否有任何功能可以用于它,还是我应该自己尝试做?
对于那些了解 Clojure 的人,我基本上需要一个 (->) 家族的变体,它可以采用函数树,比如
(tree->
input-buffer side-effect-buffer output-buffer (f1 (f2 f4 (f5 f7)) (f3 f6)))
编辑下方:添加示例:
这只是我正在寻找的更通用解决方案的一部分:
一个基于炼金术的(迷你)游戏(更一般地说,是真正的化学和炼金术的混合)。
在这种情况下,输入被分组 measurable/observable 一个混合物的特征,例如:
(def concoction
{:observable {:color {:r 50 :g 50 :b 50} :opacity 50}
:measurable {:acidity 50 :density 50 :fluidity 50 :composition "TO DO"}
:testable {:duration 10 :known-effects [list of maps] :safety 10 :after "TO DO"}})
输出是地图的向量,每个地图都类似于:
{:ingredient "ingredient-a" :amount 20 :application {:type "mix" :order 0}}
(单机)功能一般由三部分组成:
- 获得混合物的一种(或多种)特性。
- 对所选特征应用一些限制性逻辑(来自 table 的成分对最终混合物的个别影响的条目很少,table 应用类型或巨大的 table 组合效应两种或多种成分)。
- 将处理后的特征记录到共享 log/info 输出中。
- 将逻辑应用的结果附加到输出。
- Return 布尔值(目前,稍后将是 int)表示此步骤在生成输出方面的成功程度。
我稍微改变了逻辑,所以现在我只有一个函数将给定的逻辑应用于输入(而不是拥有几乎无限数量的类似函数),类似于:
(defn apply-logic [input logic-to-apply]
(let [chalist (apply (:input logic-to-apply) input)
out (apply (:logic logic-to-apply) chalist)]
(info-out (apply (:format logic-to-apply) out))
(return-grade out chalist))))
; info-out uses info-output and output variables set with let somewhere outside
然后我将有一个逻辑树来应用而不是函数:
(def tree-1112 '(logic1
(logic2
(logic3
(logic4 logic5)))
(logic6
(logic7)
(logic8
(logic9)))))
还有某种应用树逻辑:
(defn apply-tree-logic [some-tree input]
(if (apply-logic input (take-root some-tree))
(apply-tree-logic (take-branch first some-tree) input)
(apply-tree-logic (take-branch last some-tree) input))
实际上,如果我能完全按照我在这些示例中引入的内容进行操作,那么我就非常接近于自己全部实现了。
但是我需要很长时间才能优化所有这些。
听起来您尝试做的事情在某些方面与 Plumbing 相似。
A Graph is just a map from keywords to keyword functions. In this case, stats-graph represents the steps in taking a sequence of numbers (xs) and producing univariate statistics on those numbers (i.e., the mean m and the variance v). The names of arguments to each fnk can refer to other steps that must happen before the step executes. For instance, in the above, to execute :v, you must first execute the :m and :m2 steps (mean and mean-square respectively).
据我了解,你想找到图中从输入节点到输出节点的所有路径,其中图的每个节点都是某个值,每个连接都是函数应用程序,并制作一棵树他们。
这里有一些粗略的(和部分的)解决方案:
假设我们想要获得一个算术运算列表,以从一个数字中生成另一个数字。我们有函数描述:predicate 到 applicable functions 对的集合。谓词检查,如果相应的功能适用于某些输入:
(def fns
[[zero? {:add3 #(+ 3 %)}]
[#(== % 1) {:add2 #(+ 2 %) :sub10 #(- 10 %)}]
[even? {:mul3 #(* 3 %) :add2 #(+ 2 %) :add1 inc}]
[#(> % 50) {:sub49 #(- % 49)}]
[(constantly true) {:add1 inc}]])
(defn applicable-fns [fns input]
(some (fn [[pred f-map]]
(when (pred input)
f-map))
fns))
回复:
(applicable-fns fns 1)
;; {:add2 #function[reactive-clj.core/fn--21334],
:sub10 #function[reactive-clj.core/fn--21336]}
因为我们无法查看所有数字,所以我们将域限制为 -100 到 100 之间的数字:
(defn in-domain? [i] (<= -100 i 100))
现在介绍函数:clojure 有一个很好的机制来遍历树状嵌套结构:zippers
这是一个函数示例,它将计算从输入到输出的函数链:
(defn get-path [input output next-fns domain-pred]
(loop [visited #{}
curr (z/zipper identity
#(map (fn [[k v]] [k (v (second %))])
(next-fns (second %)))
(constantly nil)
[nil input])]
(let [curr-out (-> curr z/node second)]
(cond (z/end? curr) nil
(or (visited curr-out) (not (domain-pred curr-out)))
(recur (conj visited curr) (-> curr z/remove z/next))
(= output curr-out) (conj (z/path curr) (z/node curr))
:else (recur (conj visited curr-out)
(z/next curr))))))
它很简单(更容易理解,当你看到输入和输出时):
(get-path 1 21 (partial applicable-fns fns) in-domain?)
;; => [[nil 1] [:add2 3] [:add1 4] [:mul3 12] [:add2 14]
[:add2 16] [:add2 18] [:add2 20] [:add1 21]]
(get-path 51 29 (partial applicable-fns fns) in-domain?)
;; => [[nil 51] [:sub49 2] [:mul3 6] [:mul3 18] [:add2 20]
[:add2 22] [:add2 24] [:add2 26] [:add2 28] [:add1 29]]
所以,这些对是深度优先搜索路径的结果。它不是最短的,而是第一个,这是有效的。你可以读作 (-> 1 add2 add1 mul3 .... add1) => 21
如果你真的需要从输入到输出的所有路径,你应该更好地阅读一些关于循环图的奇特算法(这真的不是一项简单的任务)。但对我来说最有趣的问题是,为什么 你需要所有路径,你 真的 需要它们吗?你的最终目标是什么?你将如何使用这个功能树?
这听起来可能很奇怪,因为我找不到一个确切的术语来描述我正在尝试做的事情。
我正在开发一个应用程序,给定一组规则(很容易转化为函数)和 input/output 对(不太容易转化为代码),将允许构建一棵树应用于给定输入以达到给定输出的规则数。
它类似于专家系统,除了目标不是确定 "better"(通过某种质量)rule/function 树(实际上将输入映射到输出本身是次要的)而是能够根据一些限制或业务规则构建这些树。
我正尝试在 Clojure 中执行此操作,但我也会感谢更多一般性建议,因为我什至无法弄清楚如何简短地调用这种东西。
说到细节,据说我有一张大的细节平面图。 我有一长串函数,我将它们转换成几乎相同的功能:每个函数都采用这个大的平面细节图,并将规则应用于该规则涉及的任何值。函数有副作用(记录它做了什么),并且据推测,所述(将是)树结构使用单个布尔输出来确定进入哪个分支(如果树在此函数上分裂)。
我的想法是我可以采用一个函数并将其声明为树的根。然后采用另一个函数并将其声明为下一个要执行的函数,或者采用两个函数并将它们声明为根的两个下一个分支,具体取决于根函数输出。等等等等。 而且我需要能够根据需要多次执行此操作,从而生成符合某些要求的树。 我将完成所有的逻辑,但我需要一个结构,允许我将函数树(我什至可以自己构造,只要我只需要将它指定为像列表一样简单的东西)到给定输入映射,而不必为我将尝试做的每棵树手动编码整个过程树。
现实生活中的例子是一个大型的树状数据结构(我们可以很容易地简化输入),每个客户都希望根据自己的一套规则来描述(函数的副作用),当它是已处理(达到输出)。
这个 "procedure" 有比这个长描述更常见的名字吗? Java/Clojure 是否有任何功能可以用于它,还是我应该自己尝试做? 对于那些了解 Clojure 的人,我基本上需要一个 (->) 家族的变体,它可以采用函数树,比如
(tree->
input-buffer side-effect-buffer output-buffer (f1 (f2 f4 (f5 f7)) (f3 f6)))
编辑下方:添加示例: 这只是我正在寻找的更通用解决方案的一部分:
一个基于炼金术的(迷你)游戏(更一般地说,是真正的化学和炼金术的混合)。 在这种情况下,输入被分组 measurable/observable 一个混合物的特征,例如:
(def concoction
{:observable {:color {:r 50 :g 50 :b 50} :opacity 50}
:measurable {:acidity 50 :density 50 :fluidity 50 :composition "TO DO"}
:testable {:duration 10 :known-effects [list of maps] :safety 10 :after "TO DO"}})
输出是地图的向量,每个地图都类似于:
{:ingredient "ingredient-a" :amount 20 :application {:type "mix" :order 0}}
(单机)功能一般由三部分组成:
- 获得混合物的一种(或多种)特性。
- 对所选特征应用一些限制性逻辑(来自 table 的成分对最终混合物的个别影响的条目很少,table 应用类型或巨大的 table 组合效应两种或多种成分)。
- 将处理后的特征记录到共享 log/info 输出中。
- 将逻辑应用的结果附加到输出。
- Return 布尔值(目前,稍后将是 int)表示此步骤在生成输出方面的成功程度。
我稍微改变了逻辑,所以现在我只有一个函数将给定的逻辑应用于输入(而不是拥有几乎无限数量的类似函数),类似于:
(defn apply-logic [input logic-to-apply]
(let [chalist (apply (:input logic-to-apply) input)
out (apply (:logic logic-to-apply) chalist)]
(info-out (apply (:format logic-to-apply) out))
(return-grade out chalist))))
; info-out uses info-output and output variables set with let somewhere outside
然后我将有一个逻辑树来应用而不是函数:
(def tree-1112 '(logic1
(logic2
(logic3
(logic4 logic5)))
(logic6
(logic7)
(logic8
(logic9)))))
还有某种应用树逻辑:
(defn apply-tree-logic [some-tree input]
(if (apply-logic input (take-root some-tree))
(apply-tree-logic (take-branch first some-tree) input)
(apply-tree-logic (take-branch last some-tree) input))
实际上,如果我能完全按照我在这些示例中引入的内容进行操作,那么我就非常接近于自己全部实现了。 但是我需要很长时间才能优化所有这些。
听起来您尝试做的事情在某些方面与 Plumbing 相似。
A Graph is just a map from keywords to keyword functions. In this case, stats-graph represents the steps in taking a sequence of numbers (xs) and producing univariate statistics on those numbers (i.e., the mean m and the variance v). The names of arguments to each fnk can refer to other steps that must happen before the step executes. For instance, in the above, to execute :v, you must first execute the :m and :m2 steps (mean and mean-square respectively).
据我了解,你想找到图中从输入节点到输出节点的所有路径,其中图的每个节点都是某个值,每个连接都是函数应用程序,并制作一棵树他们。
这里有一些粗略的(和部分的)解决方案:
假设我们想要获得一个算术运算列表,以从一个数字中生成另一个数字。我们有函数描述:predicate 到 applicable functions 对的集合。谓词检查,如果相应的功能适用于某些输入:
(def fns
[[zero? {:add3 #(+ 3 %)}]
[#(== % 1) {:add2 #(+ 2 %) :sub10 #(- 10 %)}]
[even? {:mul3 #(* 3 %) :add2 #(+ 2 %) :add1 inc}]
[#(> % 50) {:sub49 #(- % 49)}]
[(constantly true) {:add1 inc}]])
(defn applicable-fns [fns input]
(some (fn [[pred f-map]]
(when (pred input)
f-map))
fns))
回复:
(applicable-fns fns 1)
;; {:add2 #function[reactive-clj.core/fn--21334],
:sub10 #function[reactive-clj.core/fn--21336]}
因为我们无法查看所有数字,所以我们将域限制为 -100 到 100 之间的数字:
(defn in-domain? [i] (<= -100 i 100))
现在介绍函数:clojure 有一个很好的机制来遍历树状嵌套结构:zippers
这是一个函数示例,它将计算从输入到输出的函数链:
(defn get-path [input output next-fns domain-pred]
(loop [visited #{}
curr (z/zipper identity
#(map (fn [[k v]] [k (v (second %))])
(next-fns (second %)))
(constantly nil)
[nil input])]
(let [curr-out (-> curr z/node second)]
(cond (z/end? curr) nil
(or (visited curr-out) (not (domain-pred curr-out)))
(recur (conj visited curr) (-> curr z/remove z/next))
(= output curr-out) (conj (z/path curr) (z/node curr))
:else (recur (conj visited curr-out)
(z/next curr))))))
它很简单(更容易理解,当你看到输入和输出时):
(get-path 1 21 (partial applicable-fns fns) in-domain?)
;; => [[nil 1] [:add2 3] [:add1 4] [:mul3 12] [:add2 14]
[:add2 16] [:add2 18] [:add2 20] [:add1 21]]
(get-path 51 29 (partial applicable-fns fns) in-domain?)
;; => [[nil 51] [:sub49 2] [:mul3 6] [:mul3 18] [:add2 20]
[:add2 22] [:add2 24] [:add2 26] [:add2 28] [:add1 29]]
所以,这些对是深度优先搜索路径的结果。它不是最短的,而是第一个,这是有效的。你可以读作 (-> 1 add2 add1 mul3 .... add1) => 21
如果你真的需要从输入到输出的所有路径,你应该更好地阅读一些关于循环图的奇特算法(这真的不是一项简单的任务)。但对我来说最有趣的问题是,为什么 你需要所有路径,你 真的 需要它们吗?你的最终目标是什么?你将如何使用这个功能树?