演算 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 に負数を加えた「すべての」整数を表わします。□
事例:演算 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} になります。