Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Language agnostic 在函数式语言中管理对嵌套不可变数据结构的更新_Language Agnostic_Design Patterns_Functional Programming_Clojure_Immutability - Fatal编程技术网

Language agnostic 在函数式语言中管理对嵌套不可变数据结构的更新

Language agnostic 在函数式语言中管理对嵌套不可变数据结构的更新,language-agnostic,design-patterns,functional-programming,clojure,immutability,Language Agnostic,Design Patterns,Functional Programming,Clojure,Immutability,在我寻求精益函数式编程的过程中,我注意到当使用嵌套的不可变数据结构时,参数列表开始变得过度。这是因为在更新对象状态时,还需要更新数据结构中的所有父节点。注意,这里我把“update”理解为“返回一个带有适当更改的新的不可变对象” e、 g.我发现自己编写的函数类型(Clojure示例)是: 更新一个简单属性的所有这些都非常难看,但是调用方还必须组装所有参数 在处理函数式语言中的不可变数据结构时,这一定是一个相当常见的要求,所以有没有一个好的模式或技巧来避免这种情况,我应该使用它来代替 试试看 (

在我寻求精益函数式编程的过程中,我注意到当使用嵌套的不可变数据结构时,参数列表开始变得过度。这是因为在更新对象状态时,还需要更新数据结构中的所有父节点。注意,这里我把“update”理解为“返回一个带有适当更改的新的不可变对象”

e、 g.我发现自己编写的函数类型(Clojure示例)是:

更新一个简单属性的所有这些都非常难看,但是调用方还必须组装所有参数

在处理函数式语言中的不可变数据结构时,这一定是一个相当常见的要求,所以有没有一个好的模式或技巧来避免这种情况,我应该使用它来代替

试试看

(update-in 
  world 
  [country city building] 
  (update-object-in-building object property value))

我知道有两种方法:

在某种便于传递的对象中收集多个参数。 例如:

这就引出了调用者需要关心的两个参数(位置和属性),如果这些参数不经常更改,这可能就足够公平了

另一种选择是with-X宏,它设置代码体使用的变量:

(defmacro with-location [location & body] ; run body in location context
  (concat
    (list 'let ['{:keys [world country city building] :as location} `~location])
    `(~@body)))

Example use:
(with-location location (println city))
然后,无论主体做什么,它都会对为其设置的世界/国家/城市/建筑执行操作,并且它可以使用“预组装”
location
参数将整个内容传递给另一个函数


更新:现在使用一个实际工作的with location宏

这个问题的经典通用解决方案是所谓的。有许多变体,但基本思想很简单:给定一个嵌套的数据结构,在遍历它时将其拆下,这样在每一步中都有一个“当前”元素和一个片段列表,表示如何在“当前”元素上方重建数据结构的其余部分。拉链可能被认为是一个“光标”,它可以在一个不变的数据结构中移动,在移动过程中替换各个部分,只重新创建需要的部分

在列表的简单情况下,片段只是列表的前几个元素,以相反的顺序存储,遍历只是将一个列表的第一个元素移动到另一个

在二叉树的非平凡但仍然简单的情况下,每个片段都由一个值和一个子树组成,分别标识为right或left。将拉链“向下向左”移动涉及到将当前元素的值和右子元素添加到片段列表中,使左子元素成为新的当前元素。移动“向下-向右”的工作原理类似,移动“向上”是通过将当前元素与片段列表上的第一个值和子树相结合来完成的

虽然zipper的基本思想非常笼统,但为特定数据结构构造zipper通常需要一些专门的位,如定制遍历或构造操作,以供通用zipper实现使用


(warning,PDF)给出了OCaml中的示例代码,该代码用于存储具有通过树的显式路径的片段的实现。不足为奇的是,大量的材料也可以在拉链上找到。作为构建显式路径和片段列表的替代方案,可以实现zippers。最后,似乎还有一个。

谢谢-这是一个非常有用的函数,我以前从未见过!然而,它仍然给我们留下了许多参数。。。。我想这是没有办法的,非常有用,谢谢!因此,即使您无法完全避免参数的激增,您至少可以使用宏或HOF使其看起来更漂亮……是的,以方便的形式包装它们几乎是您所能做的最好的事情。就像你在面向对象语言中使用“配置对象”所做的那样,配置对象的存在只是为了封装参数。你可以将数据扁平化:分别存储世界、国家、城市等。然后,如果必须更新一个,请在平面结构中更新它。通过键将数据链接在一起,以便以后需要时将其组合在一起。不过,我们现在正在重新发明关系数据库。
; world is a nested hash, the rest are keys
(defstruct location :world :country :city :building)
(defstruct attribute :object :property)

(defn do-update[location attribute value]
  (let [{:keys [world country city building]} location
        {:keys [object property]} attribute ]
    (update-in world [country city building object property] value)))
(defmacro with-location [location & body] ; run body in location context
  (concat
    (list 'let ['{:keys [world country city building] :as location} `~location])
    `(~@body)))

Example use:
(with-location location (println city))