DOM でも children と parentElement だったり nextElementSibling と previousElementSibling だったりがあります
ミュータブルなものなら簡単に実現できますが イミュータブルなデータだと相互に参照を入れられない気がします
普通に JavaScript でやると
const child1 = {
parent: null,
children: [],
prev: null,
next: null,
}
const child2 = {
parent: null,
children: [],
prev: null,
next: null,
}
const parent = {
parent: null,
children: [],
prev: null,
next: null,
}
child1.next = child2
child1.parent = parent
child2.prev = child1
child2.parent = parent
parent.children.push(child1, child2)
これがイミュータブルだとすると 参照をいれるところがこうなります
const child1_new = { ...child1, next: child2, parent }
const child2_new = { ...child2, prev: child1_new, parent }
こうしたときに child2_new.prev に child1_new が入っていますが child1_new.next は古い child2 です
イミュータブルしかできない言語だとこういう構造は扱わないのでしょうか
直接参照をもたせず 間接的に持たせて自分で参照するならできそうですが 手間が多いです
const parent_id = Math.random()
const child1_id = Math.random()
const child2_id = Math.random()
const parent = {
parent: null,
children: [child1_id, child2_id],
prev: null,
next: null,
}
const child1 = {
parent: parent_id,
children: [],
prev: null,
next: child2_id,
}
const child2 = {
parent: parent_id,
children: [],
prev: child1_id,
next: null,
}
const refs = {
[parent_id]: parent,
[child1_id]: child1,
[child2_id]: child2,
}
console.log(child1 === refs[refs[child1.next].prev])
// true
更新することも考えるとあまりやりたい方法ではないです
イミュータブルな言語でやり方をググってみると 関数にして遅延評価することで実現してる例がありました
const parent = {
parent: null,
children: [() => child1, () => child2],
prev: null,
next: null,
}
const child1 = {
parent: () => parent,
children: [],
prev: null,
next: () => child2,
}
const child2 = {
parent: () => parent,
children: [],
prev: () => child1,
next: null,
}
console.log(child1 === child1.next().prev())
// true
たしかに実行時に参照先が評価される言語なら関数を作る時点で参照できなくても大丈夫そうです
ただこれでも変更は簡単ではなく どこかを書き換えると参照先も変更が必要になり全体を作り直すことになります
なのでそういうデータ構造は持たず単一方向にするのが普通で なぜ循環する参照が必要になるのか聞かれてるような感じでした