英語のドキュメント を読んだけどあまり理解できてなかったので 日本語 で再挑戦する会。
進捗
- 前回
- 「7章 アルゴリズム」を読んだり試したり
- 「8章 データ構造」を読み始めた
- 今回 2022.5 実践TLA+もくもく会 - connpass
- 「8.1 リンクリスト」の157ページ
ノードのサブセットをすべて生成しまで
- 「8.1 リンクリスト」の157ページ
- 次回 2022.6 実践TLA+もくもく会 - connpass
- 「8.1 リンクリスト」の157ページ
リンクリストと名の付くものには開始点があるはずだから
- 「8.1 リンクリスト」の157ページ
8.1 リンクリスト
まず、ノード間で考えられるマッピングをすべて定義する
次に、「最後のノード」の概念が必要である
これだけで全ての要素の全ての組み合わせを定義したことになる。便利だ。
CONSTANT NULL
PointerMaps(Nodes) == [Nodes -> Nodes \union {NULL}]
実は先に「8.2検証」まで読んでから戻ってきたのだけど、ようやく説明してることがわかってきた。
全ての要素の全ての組み合わせを定義してる から それらのマッピングが全てリンクリストであるとは限らない のか。
そこで次のようなアルゴリズムを導入しようとしている。
リンクリストのノードを「たどっていく」とシーケンスになることに着目するのである。
そうすると、与えられたPointerMapが単一のリンクリストである場合は、それ以降のすべての要素が
その手前の要素の次のノードであるというノードのシーケンスが得られる。たとえば、
リンクリストが[a |-> b, b |-> NULL, c |-> a] であるとすれば、シーケンスは<<c, a, b>>になる。
こういう解釈であってるのかな。
- 「それ以降の全ての要素」とは
a |-> bb |-> NULLc |-> aのこと
- 「その手前の要素の次のノードであるというノード」とは
a |-> bについて- 「次のノード」が
aになるのはc |-> aだからc
- 「次のノード」が
b |-> NULLについて- 「次のノード」が
bになるのはa |-> bだからa
- 「次のノード」が
c |-> aについて- 「次のノード」が
cになるノードはない、つまりNULLだからb
- 「次のノード」が
- したがって、シーケンスは
<<c, a, b>>になる
(1周目)以降の説明や実装がよくわからなかったので、「8.2検証」を読んだら戻ってくることにする。
---------------------------- MODULE LinkedLists ----------------------------
CONSTANT NULL
LOCAL INSTANCE FiniteSets \* Cardinality
LOCAL INSTANCE Sequences \* Len
LOCAL INSTANCE TLC \* Assert
\* 考えられるメモリマッピングの集合
\* まだこの演算子の役割がわからない
LOCAL PointerMaps(Nodes) == [Nodes -> Nodes \union {NULL}]
\* シーケンスをセットに変換する
LOCAL Range(f) == {f[x]: x \in DOMAIN f}
\* PointerMapはPointerMapsの要素
\* 与えられたPointerMapが単一のリンクリストになっていることをチェックする
\* PointerMapが[a |-> b, b |-> NULL, c |-> a]なら <<c, a, b>> になっていることをチェックする
LOCAL isLinkedList(PointerMap) ==
LET
nodes == DOMAIN PointerMap
\* 1..Cardinality(nodes) は 1..len(nodes) の範囲になる
\* つまりnodesの全ての要素について、重複を許す全ての組み合わせのシーケンスのセットになる
\* [1..3 -> {4,5,6}] なら {<<4,4,4>>,...,<<6,6,6>>} になる
all_seqs == [1..Cardinality(nodes) -> nodes]
\* PointerMapに含まれる全てのノードの全ての並び順を網羅したシーケンスから、
\* リンクリストの構成要件を満たすシーケンスが見つかれば、それはリンクリストだと判断できる
IN \E ordering \in all_seqs:
\* ordering[i]=a |-> bならPointerMap[ordering[i]]=c |-> aになる
\* だからordering[i+1]=c |-> aになっていて欲しい
/\ \A i \in 1..Len(ordering)-1:
PointerMap[ordering[i]] = ordering[i+1]
\* /\ \A n \in nodes:
\* \E i \in 1..Len(ordering):
\* ordering[i] = n
\* マッピングのすべてのノードは ordering に含まれる
\* Rangeはシーケンスのorderingをセットに変換する
\* orderingはnodesの要素から生成した全ての組み合わせのシーケンスなので、
\* 一部の要素だけ選択した場合nodesが部分集合にならない場合がある
/\ nodes \subseteq Range(ordering)
\* リンクリスト演算子
LinkedLists(Nodes) ==
IF NULL \in Nodes THEN
Assert(FALSE, "NULL cannot be in Nodes")
ELSE
{pm \in PointerMaps(Nodes): isLinkedList(pm)}
この時点で main モジュールから LinkedList モジュールを読み込んで、実行できるようになる。
そうすると Evaluate Constant Expression でどういう結果になるのか観測できるようになる。
1度実行しないと Model Checking Results は出てこない。
-------------------------- MODULE main -------------------------- CONSTANTS NULL INSTANCE LinkedLists WITH NULL <- NULL
- Model Overview
- What is the behavior spec?:
No behavior spec - What is the model?
NULL <- [model value]
- What is the behavior spec?:
Model Checking Results でこういう結果を観測できる。
Expression:
LinkedLists({"a", "b"})
Value:
{ [a |-> NULL, b |-> "a"],
[a |-> "a", b |-> "a"],
[a |-> "b", b |-> NULL],
[a |-> "b", b |-> "a"],
[a |-> "b", b |-> "b"] }
これが
しかし、LinkedLists(Nodes) を呼び出してみると、長さが Cardinality(Nodes) のリンクリストが得られるだけである
ということなのかな。
NULL で終端できてないことや、循環してることは問題ないんだろうか。
それらは後で記述されているから、ここではリストの長さが等しくなってしまう、つまり機能不足であることを問題視しているんだな。
考えられるノードのサブセットをすべて生成し、サブセットごとにポインタマップをすべて定義し、生成されたポインタマップのすべてで isLinkedList を呼び出せばよい
すると次のように変化した。なるほどバリエーションが出てきてよさそう。
Expression:
LinkedLists({"a", "b"})
Value:
{ [a |-> NULL],
[a |-> "a"],
[b |-> NULL],
[b |-> "b"],
[a |-> NULL, b |-> "a"],
[a |-> "a", b |-> "a"],
[a |-> "b", b |-> NULL],
[a |-> "b", b |-> "a"],
[a |-> "b", b |-> "b"] }
開始点や循環リストの話は次回にする。
参考リンク
- Practical TLA+ | SpringerLink
- 原著
- 実践TLA+ 電子書籍|翔泳社の本
- 翻訳
- The TLA+ Home Page
- TLA+ の総本山的なWebページ