和集合と積集合

VDM++ の set 型には、2つの集合の和集合/積集合(共通集合)を定義できます。数学の世界では、2つの集合 A および B の和集合を A∪B で、積集合を A∩B で表現します。VDM++ では、次のように表現します。

    {1, 2} union {2, 3}        ==> {1, 2, 3}

2つの集合の「どちらかに」含まれる要素を列挙した、和集合は {1, 2, 3} になります。

    {1, 2} inter {2, 3}        ==> {2}

2つの集合の「どちらにも」含まれる要素を列挙した、積集合は {2} になります。

演算 union および inter を実現する

VDM++ での演算 union/inter に準拠するように、メソッド VDM_Set.union/VDM_Set.inter を実現します。

class VDM_Set:
def union(s1,s2):
"""
s1 union s2
; set of A * set of A -> set of A
;
; Union
; yields the union of the sets s1 and s2, i.e. the set
; containing all the elements of both s1 and s2.
"""
return VDM_Set(s1._Union(s2))
def _Union(s1,s2):
s = [e for e in s1.list if not e in s2.list]
s += s2.list[:]
s.sort()
return s

メソッド union では、集合 s1 および s2 の和集合を生成して、これをリターン値とします。
メソッド _Union は、union の補助関数です。まず、リスト s1.list に含まれる要素 e の中から、リスト s2.list に含まれないものを列挙した、リスト s を生成します。次に、この s の末尾に、s2.list の複製を連結します。最後に、s を整列して、これをリターン値とします。
《Note》Java の this と、Python の self との違いのひとつは、その変数名が任意であることです。self は予約語ではなく、慣例として用います。そのため、self 以外の任意の変数名を使って、コードを記述できます。ここでは、2つの引数が同格であることを強調するためにあえて、self ではなく s1 にしています。□

class VDM_Set:
def inter(s1,s2):
"""
s1 inter s2
; set of A * set of A -> set of A
;
; Intersection
; yields the intersection of sets s1 and s2, i.e. the
; set containing the elements that are in both s1
; and s2.
"""
return VDM_Set(s1._Intersection(s2))
def _Intersection(s1,s2):
s = [e for e in s1.list if e in s2.list]
s.sort()
return s

メソッド inter では、集合 s1 および s2 の積集合を生成して、これをリターン値とします。
メソッド _Intersection は、inter の補助関数です。まず、リスト s1.list に含まれる要素 e の中から、リスト s2.list に含まれるものを列挙した、リスト s を生成します。最後に、s を整列して、これをリターン値とします。
《Note》リストの複製を生成するには、[:] とするのが便利です。もとのリストをそのまま連結すると、後で思わぬ副作用に苦しむことがあるので、注意が必要です。□

自己紹介する関数オブジェクト

単体テストには、次のようなコードの断片が含まれます。

    print VDM_Set.union.__doc__

これは、VDM の仕様を銘記したものです。関数オブジェクト VDM_Set.union の属性 __doc__ を参照すると、そこに記述されたドキュメントコメントが得られます。このコードを再実行すると、次のように

        s1 union s2
; set of A * set of A -> set of A
;
; Union
; yields the union of the sets s1 and s2, i.e. the
; set containing all the elements of both s1 and
; s2.

メソッド union の本体に記述したコメントが出力されます。すると、要求仕様/テスト結果を混在させたレポートを、簡単に作成できます。
《Tips》javadoc のようなドキュメント作成ツールは便利ですが、不満も残ります。プログラミングを支援する真の統合開発環境の下では、コーディングに必要なリソースの一部として、これらの情報を活用できます。それには、これらの情報が(ツールを介さずに)オブジェクト自身から得られると便利です。つまり、__doc__を介して、オブジェクト自身に自己紹介をさせるのです。Python では、関数もオブジェクトなので、他のオブジェクトと同様に、自己紹介が可能です。要求仕様の一部などを銘記しておくと、必要なときに参照できます。このように、真の(シームレスな)統合開発環境下では、ツール/オブジェクトはコインの裏表と見なせます。□

演算の型を規定する

演算の型を規定するのに、次のような表現をします。

    set of A * set of A -> set of A

A は任意の型を表わします。すると、set of A は、A 型の要素を列挙した集合型を意味します。すると、

    set of nat1

は、自然数を表わす nat1 型の要素を列挙した集合型を意味します。たとえば、

    {1,2,3}

は、この型に適合するのが分かります。set 型に適用できる演算子 union は、2つの値を与えると、1つの値が得られます。たとえば、

    {1,2,3} union {3,4}        = {1,2,3,4}

は、nat1 型の要素を列挙した2つの集合に対して、演算子 union を適用すると、nat1 型の要素を列挙した集合が得られます。すると、この演算の型を規定するには、次のように、

    set of nat1 * set of nat1 -> set of nat1

と表現します。
《Note》VDM++ では、整数を表現する3つの型が規定されています。nat1 は自然数を表わします。nat は、nat1 に 0 を加えたものを表わします。int は、nat に負数を加えた「すべての」整数を表わします。□


《ひよ子のきもち♪2006/10/05》
Previous|4/20|Next

事例:演算 union/inter

VDM++ での演算 union/inter に準拠した事例を紹介します。

    s1 = VDM_Set(range(1,3))
s2 = VDM_Set(range(2,4))
print "s1: %s"%s1
print "s2: %s"%s2
print ">>> s1 union s2"
print s1.union(s2)

集合 s1 および s2 の和集合を得るには、メソッド union を利用します。このコードを実行すると、

s1: {1, 2}
s2: {2, 3}
>>> s1 union s2
{1, 2, 3}

和集合は {1, 2, 3} になります。

    s1 = VDM_Set(range(1,3))
s2 = VDM_Set(range(2,4))
print "s1: %s"%s1
print "s2: %s"%s2
print ">>> s1 inter s2"
print s1.inter(s2)

集合 s1 および s2 の積集合を得るには、メソッド inter を利用します。このコードを実行すると、

s1: {1, 2}
s2: {2, 3}
>>> s1 inter s2
{2}

積集合は {3} になります。