一旦你建立了身份,就可以完成。
但首先你必须建立身份。
在许多语言中,值可以彼此不同但相等。以 Python 为例:
>>> a = [1]
>>> b = [1]
>>> a == b
True
>>> a is b
False
你想更新树的一个分支中的 E,并更新该元素 E 的所有其他元素。但是 Haskell 是引用透明的:它没有事物相同的概念目的;只有相等,甚至这并不适用于每个对象。
你可以做到这一点的一种方法是平等。说这是你的树:
__A__
/ \
B C
/ \ / \
1 2 2 3
然后我们可以遍历树并将所有 2 更新为 4。但在某些情况下,这并不是您想要的。
在 Haskell 中,如果你想在多个地方更新一个东西,你必须明确是和不是相同的东西。解决此问题的另一种方法是使用唯一整数标记每个不同的值,并使用该整数来确定身份:
____________A___________
/ \
B C
/ \ / \
(id=1)"foo" (id=2)"bar" (id=2)"bar" (id=3)"baz"
然后我们可以更新标识为 2 的所有值。意外碰撞不会成为问题,因为除了故意的碰撞之外不会有任何碰撞。
这基本上是STRef 和IORef 所做的,除了它们将实际值提升到monad 的状态并隐藏身份。使用这些的唯一缺点是你需要让你的大部分代码都是单子的,但无论你做什么,你都可能不会轻易摆脱这一点。 (修改值而不是替换它们本身就是一种有效的做法。)
您提供的结构没有详细说明,因此无法根据您的用例定制示例,但这里有一个使用the ST monad 和Tree 的简单示例:
import Control.Monad
import Control.Monad.ST
import Data.Tree
import Data.Traversable (traverse)
import Data.STRef
createInitialTree :: ST s (Tree (STRef s String))
createInitialTree = do
[a, b, c, d, e, f] <- mapM newSTRef ["A", "B", "C", "D", "E", "F"]
return $ Node a [ Node b [Node d [], Node e []]
, Node c [Node e [], Node f []]
]
dereferenceTree :: Tree (STRef s a) -> ST s (Tree a)
dereferenceTree = traverse readSTRef
test :: ST s (Tree String, Tree String)
test = do
tree <- createInitialTree
before <- dereferenceTree tree
let leftE = subForest (subForest tree !! 0) !! 1
writeSTRef (rootLabel leftE) "new" -- look ma, single update!
after <- dereferenceTree tree
return (before, after)
main = do
let (before, after) = runST test
putStrLn $ drawTree before
putStrLn $ drawTree after
请注意,虽然我们只显式修改了左侧 E 值的值,但它在右侧也根据需要发生了变化。
我应该注意,这些不是唯一的方法。对于同一问题,可能还有许多其他解决方案,但它们都要求您明智地定义身份。只有完成后才能开始下一步。