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