简体   繁体   English

函数的动态(树)结构作为过程(和Clojure中的实现)

[英]Dynamic (tree) structure of functions as process (and implementation in Clojure)

This will all sound probably very strange as I was not able to find an exact term for what I am trying to do. 这听起来可能都很奇怪,因为我无法找到我想要做的确切术语。

I am developing an application that, given a set of rules (which are easily translated into functions) and input/output pairs (that are not so easily translated into the code), would allow to construct a tree of the rules to be applied to the given input to reach given output. 我正在开发一个应用程序,给定一组规则(很容易翻译成函数)和输入/输出对(不那么容易翻译成代码),将允许构建一个应用于规则的树的树达到给定输出的给定输入。

It is similar to expert system, except the goal is not to determine "better" (by some quality) rule/function tree (actually mapping input to output is secondary by itself) but to be able to build those trees according to some restrictions or business rules. 它类似于专家系统,除了目标不是确定“更好”(通过某种质量)规则/功能树(实际上映射输入到输出本身是次要的)但是能够根据某些限制或者构建这些树或商业规则。

I am trying to do this in Clojure, but I will appreciate more general advice as well since I cannot even figure out how to call this sort of thing in short. 我试图在Clojure中做到这一点,但我会欣赏更一般的建议,因为我甚至无法弄清楚如何简单地称这种事情。

Going into details, supposedly I have a large flat map of details. 进入细节,据说我有一个大的平面细节图。 I have a long list of functions that I transformed to do almost the same: each function takes this large flat map of details, and applies the rule to whatever value(s) this rule concerns. 我有一长串功能,我转换为几乎相同:每个函数都采用这个大的平面详细信息图,并将规则应用于此规则所关注的任何值。 Function has side effects (logs what it does) and, supposedly, single boolean output that is used by the said (to be) tree structure to determine what branch to go into (if tree splits on this function). 函数有副作用(记录它的作用),并且据说是单个布尔输出,由所述(将)树结构用来确定要进入的分支(如果树在此函数上分裂)。

The idea is that I can take one function and declare it as a root of the tree. 我的想法是我可以使用一个函数并将其声明为树的根。 Then take either one another function and declare it to be next function to do, or take two functions and declare them as two next branches from the root depending on the root function output. 然后取另一个函数并声明它是下一个要执行的函数,或者取两个函数并根据根函数输出将它们声明为根中的两个下一个分支。 And so on and so forth. 等等等等。 And I need to be able to do it as many times as I want, producing a tree that fits some requirements. 而且我需要能够按照自己的意愿多次进行,生成符合某些要求的树。 I will be doing all of the logic, but I need a structure that would allow me to apply the tree of functions (that I can even construct myself as long as I only need to specify it as something as simple as a list) to the given input map, without having to manually code the whole process tree for every tree I will be trying to do. 我将完成所有的逻辑,但我需要一个结构,允许我应用函数树(我甚至可以构建自己,只要我只需要将它指定为像列表一样简单)到给定输入映射,而不必为我将尝试执行的每个树手动编写整个过程树。

Real life example would be a large tree-like data structure (input that we can flat down easily) that every client can want to be described (side effect of functions) according to his own set of rules when it is processed (reaches output). 现实生活中的例子是一个大型的树状数据结构(我们可以轻松平放的输入)每个客户端都可以根据他自己的规则集(处理输出时)来描述(函数的副作用) 。

Does this "procedure" have a more common name than this long description? 这个“程序”的名称是否比这个长描述更常见? Are there any functionalities withing Java/Clojure that can be used for it or should I try doing it myself? 是否有任何可用于Java / Clojure的功能,或者我应该自己尝试一下? For those who know Clojure, I basically need a variation of one of the (->) family that can take a tree of functions, like 对于那些了解Clojure的人,我基本上需要一个( - >)系列的变体,它可以采用一个函数树,比如

(tree-> 
input-buffer side-effect-buffer output-buffer (f1 (f2 f4 (f5 f7)) (f3 f6)))

Edit below: adding examples: This is just one part of a more general solution I am looking for: 编辑如下:添加示例:这只是我正在寻找的更通用解决方案的一部分:

A (mini)game that is based around alchemy (more generally, a mix of real chemistry and alchemy). 基于炼金术的(迷你)游戏(更常见的是,真正的化学和炼金术的混合)。 In this case, input is grouped measurable/observable characteristics of a concoction, for example: 在这种情况下,输入被分组可测量/可观察的混合特征,例如:

(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"}})

Output is a vector of maps each of which is similar to: 输出是地图的矢量,每个地图类似于:

{:ingredient "ingredient-a" :amount 20 :application {:type "mix" :order 0}}

The (stand-alone) function in general consist of 3 parts: (独立)功能一般由3部分组成:

  1. Get one (or more) characteristics of the concoction. 获得混合物的一个(或多个)特征。
  2. Apply some restricted logic to the chosen characteristics (few entries from table of individual effects of ingredient on the resulting concoction, table of application types or huge table of combined effects of two or more ingredients). 对所选择的特征应用一些受限制的逻辑(来自成分的各个效果的表格中的几个条目,应用类型表或两个或更多成分的组合效果的巨大表格)。
  3. Log processed characteristics into shared log/info output. 将处理后的特征记录到共享日志/信息输出中
  4. Append result of application of the logic to the output. 将逻辑的应用结果附加到输出。
  5. Return boolean (for now, it will be int later) that signals what level of success this step had in terms of producing output. 返回布尔值(现在,它将是后来的int),表示此步骤在产生输出方面的成功程度。

I changed logic around a bit so now I have only one function that applies a given piece of logic to the input (instead of having almost infinite amount of similar functions) similar to: 我改变了一点逻辑,所以现在我只有一个函数将一个给定的逻辑应用于输入(而不是几乎无限量的类似函数),类似于:

(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

Then I would have a tree of logic to apply instead of functions: 然后我会有一个逻辑树来应用而不是函数:

(def tree-1112 '(logic1 
                 (logic2 
                  (logic3 
                   (logic4 logic5))) 
                 (logic6 
                   (logic7) 
                   (logic8 
                    (logic9)))))

And a some sort of apply-tree-logic: 还有一种应用树逻辑:

(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))

Practically if I could do exactly what I brought in these examples it would be pretty close to implementing it all myself. 实际上,如果我可以完全按照我在这些示例中提到的那样做,那将非常接近我自己实现它。 But then it would take me ages to optimize all of this. 但是,我需要花费很多时间来优化所有这些。

It sounds like what you are trying to do is similar in some respects to Plumbing . 听起来你要做的事情在某些方面类似于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 (ie, the mean m and the variance v). 在这种情况下,stats-graph表示采用一系列数字(xs)并生成关于这些数字的单变量统计数据的步骤(即,均值m和方差v)。 The names of arguments to each fnk can refer to other steps that must happen before the step executes. 每个fnk的参数名称可以指在步骤执行之前必须执行的其他步骤。 For instance, in the above, to execute :v, you must first execute the :m and :m2 steps (mean and mean-square respectively). 例如,在上面,要执行:v,您必须首先执行:m和:m2步骤(分别为均值和均值)。

as far as i understood, you want to find all the paths in a graph from input node to output node, where every node of graph is some value, and every connection is the function application, and make a tree of them. 据我所知,你想找到从输入节点到输出节点的图形中的所有路径,其中图形的每个节点都是某个值,每个连接都是函数应用程序,并制作它们的树。

Here is some sketchy (and partial) solution for that: 这是一些粗略(和部分)的解决方案:

let's say we want to get a list of arithmetic ops to make one number from another. 假设我们想得到一个算术运算列表,以便从另一个数字中得到一个数字。 We have functions description: a collection of pairs predicate to applicable functions . 我们有函数描述:对适用函数的 谓词集合。 Predicate checks, if corresponding functions are applicable to some input: 谓词检查,如果相应的函数适用于某些输入:

(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))

in repl: 在repl中:

(applicable-fns fns 1)

;; {:add2 #function[reactive-clj.core/fn--21334], 
    :sub10 #function[reactive-clj.core/fn--21336]}

as we can't look through all the numbers, let's just limit our domain to numbers from -100 to 100: 因为我们无法查看所有数字,所以我们只需将域名限制在-100到100之间:

(defn in-domain? [i] (<= -100 i 100))

now to the function: clojure has a nice mechanism to traverse tree like nested structures: zippers 现在到函数:clojure有一个很好的机制来遍历树,就像嵌套结构: 拉链

here is an example of a function, that would compute the functions' chain from input to output: 这是一个函数的例子,它将计算从输入到输出的函数链:

(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))))))

it's qoite a simple one (easier to understand, when you'll see input and output): 它是一个简单的qoite(当你看到输入和输出时更容易理解):

(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]]

so, these pairs is the result of depth-first search for the path. 所以,这些对是深度优先搜索路径的结果。 it's not the shortest one, but the first on, that was valid. 它不是最短的,但是第一个,这是有效的。 You can read it as (-> 1 add2 add1 mul3 .... add1) => 21 您可以将其读作(-> 1 add2 add1 mul3 .... add1) => 21

if you really need all the paths from input to output, you should better read about some fancy algorithms for graphs with cycles (which can be really non a trivial task). 如果你真的需要从输入到输出的所有路径,你应该更好地阅读一些带有周期的图形的奇特算法(这可能真的不是一项简单的任务)。 But the most interesting question for me, is why do you need all the paths, and do you really need them? 但对我来说最有趣的问题是, 为什么你需要所有的路径,你真的需要它们吗? What is your final goal? 你的最终目标是什么? how will you use this functions tree? 你将如何使用这个功能树?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM