今日もちょっとAlloyで遊んでみます。
ある目的から、自然数の区間 {0, 1, 2, ..., n} の性質の一部を抽象した構造が欲しくなりました。その構造とは次のものです。
- サクセッサ(後者関数)に基づく全順序(線形順序)
- 2点間の距離
2点間の距離とは、x, y∈{0, 1, 2, ..., n} として、次のように定義されます。
- δ(x, y) = |x - y|
ここでは便宜上引き算と絶対値で表現してますが、事前に引き算や絶対値を要求しているわけではありません。
とりあえず、サクセッサ(名前はnextにします)と順序<(名前はlt)、それと定数ゼロ(zero)、最後の数(last)を定義してみます。
-- 自然数の区間 {0, 1, 2, ..., n}
module interval
sig Interv {
-- 後者
next: lone Interv,
-- 順序 <
lt: set Interv
}
-- ゼロと最後の数
one sig zero, last extends Interv {}
-- nextに関する公理
fact next {
-- lastのnextは存在しない
no last.next
and
-- last以外ではnextが定義されている
all x: (Interv - last) |
one x.next
and
-- ゼロからの可達性
zero.*next = Interv
}
-- ltに関する公理
fact lt {
lt = ^next
}不等号「<」に相当する関係ltを上では定義してますが、nextの推移的閉包に過ぎないし、^next と書いても同じなんで、下に出てくるコードでは特にltを定義はしてません。
さて、δ(x, y) = |x - y| に相当する関係が x.delta[y] = z となるように定義します。基本的に、δ(x, x) = 0 からはじまる再帰的な定義です。x ≦ y のときだけを考えた補助関数δ0を先に定義して、それの対称化としてδを定義しました。もっとうまいやり方/Alloyらしい書き方があるかも知れませんが、とりあえず最初に思いついた方法です。
-- 自然数の区間 {0, 1, 2, ..., n}
module interval
sig Interv {
-- 後者
next: lone Interv,
-- 距離
private delta0: Interv ->one Interv,
delta: Interv ->one Interv
}
-- ゼロと最後の数
one sig zero, last extends Interv {}
-- nextに関する公理
fact next {
-- lastのnextは存在しない
no last.next
and
-- last以外ではnextが一意に定義されている
all x: (Interv - last) |
one x.next
and
-- ゼロからの可達性
zero.*next = Interv
}
-- delta0に関する公理
fact delta0 {
all x, y: Interv |
{
x.delta0[x] = zero
-- x ≦ y ならば、定義されている範囲内で
-- δ0(x, y.next) = δ0(x, y).next
y in x.*next implies
(x.delta0[y.next] in (x.delta[y]).next)
}
}
-- deltaに関する公理
fact delta {
all x, y: Interv |
-- x ≦ y ならば、δ = δ0
y in x.*next implies (delta = delta0)
-- それ以外では、δ(x, y) = δ(y, x)
else x.delta[y] = y.delta0[x]
}このくらいの定義(Alloyコード)でも、未定義部分を考慮してなかったり、「<」と「≦」を取り違えたりと、何度も何度もAlloyアナライザーに事例(モデルインスタンス)を作らせないと正しく書けません。これ(↑)で正しい自信もありませんが、基数が2, 3の台集合上の構造としては意図どおりになっているみたいです。