For Development HEAD DRAFTSearch (procedure/syntax/module):

9.39 gauche.version - バージョン番号の比較

Module: gauche.version

このモジュールは、バージョン番号(リリース番号)を比較する便利な手続きを提供します。 ここでのバージョン番号とはソフトウェアのリリースにつけられる "0.5.1"、"3.2-3"、"8.2pl1"といった文字列で、 通常これらの番号間には順序関係が定義できます。 例えば "1.2.3" は "1.2" より新しいが "2.1" より古い、といった具合です。 以下のようにこれらの文字列の順序を比較することができるようになります。

(version<? "2.2.3" "2.2.11")     ⇒ #t
(version<? "2.3.1" "2.3")        ⇒ #f
(version<? "2.3.1-1" "2.3.1-10") ⇒ #t
(version<? "13a" "5b")           ⇒ #f

バージョン番号の付け方には特に標準というものはありませんが、 よく使われている方法を採用しました。全ての場合に使えるとはいきませんが、 大抵の場合はカバーできるのではないかと思います。

厳密に言えば、分岐などの場合があるのでバージョン番号間の関係は半順序関係にしかなりません。 このモジュールでは単純な定義を使って、全順序関係があるものとして扱います。

まず、ここでのバージョン番号は以下の構文に従うものとします。

 <version> : <principal-release>
           | <version> <post-subrelease>
           | <version> <pre-subrelease>
 <principal-release> : <relnum>
 <post-subrelease>   : [.-] <relnum>
 <pre-subrelease>    : _ <relnum>?
 <relnum>            : [^._-]+

通常<relnum>は数字部分と、それに続く省略可能な拡張部分とからなります。 例えば "23a" は数字部分23と拡張部分"a"からなります。 もし<relnum>が数字で始まらない場合、その数字部分は-1であると考えることにします。

<relnum>の順序関係を次のように定義します。

  1. relnum Aとrelnum Bが異なる数字部分を持っている場合、拡張部分を無視して 数字部分を数値として比較します。例:"3b" < "4a"。
  2. relnum Aとrelnum Bの数字部分が等しい場合、拡張部分を辞書順に比較します。 例:"4c" < "4d"、"5" < "5a"。

<relnum>間の順序が定義できたところで、バージョン番号の順序を次のように定義します。

  1. 各々のバージョン番号を分解して <principal-release> とそれに続く サブリリース部品のリストに入れる。このリストの各要素のことを「リリース部品」 と呼びます。
  2. もし両方のリストの最初のリリース部品が同じなら、それを両方のリストから 取り除きます。これをリストの先頭が異るまでくりかえします。
  3. そうすると次のような場合に分られます。
    1. 両方のリストが空: バージョンは同じ。
    2. 一方のリスト (A) が空で、他方のリスト (B) の先頭がポストサブリリース: A が B より前のバージョン。
    3. 一方のリスト (A) が空で、他方のリスト (B) の先頭がプリサブリリース: B が A より前のバージョン
    4. リスト A の先頭がポストサブリリースで、リスト B の先頭がプリサブリリース: B が A より前のバージョン
    5. 両方のリストとも先頭がポストサブリリースであるかプリサブリリース: relnum を比較する。

以下はいくつかの例です。

"1" < "1.0" < "1.1" < "1.1.1" < "1.1.2" < "1.2" < "1.11"
"1.2.3" < "1.2.3-1" < "1.2.4"
"1.2.3" < "1.2.3a" < "1.2.3b"
"1.2_" < "1.2_rc0" < "1.2_rc1" < "1.2" < "1.2-pl1" < "1.2-pl2"
"1.1-patch112" < "1.2_alpha"

<pre-subrelease> があるのは、「リリース候補」あるいは 「プリリリース」のバージョンをつかえるようにするためです。

ヒント: 「1.2正式リリースもしくはそれ以降」をチェックした場合は (version<=? "1.2" v) とします。これは、 1.2_pre3といったプレリリース版を除外します。一方、 「プレリリース版も含めて1.2もしくはそれ以降」としたい場合は (version<=? "1.2_" v)と書けます。これは、 1.2_pre1等は含み、それ以前の例えば1.1.99999等は除外します。

許容できるバージョンを条件の組み合わせで指定したいことはよくあります。 例えば「バージョン1.3以降、ただし1.4.1を除く」とか、 「バージョン1.1以上で、1.5より前」等。 バージョン指定 (version spec) は、S式でバージョンに関する条件を表現する手段です。 version-satisfy?手続きで、バージョンがバージョン指定を満たしている かどうかチェックできます。

バージョン指定の構文は以下の通りです。

<version-spec> : <version>
               | (<op> <version>)
               | (and <version-spec> ...)
               | (or <version-spec> ...)
               | (not <version-spec>)

<version> : version string
<op>      : = | < | <= | > | >=
Function: version=? ver1 ver2
Function: version<? ver1 ver2
Function: version<=? ver1 ver2
Function: version>? ver1 ver2
Function: version>=? ver1 ver2

{gauche.version} ふたつのバージョン番号文字列 ver1ver2 の順序関係によって 真偽値を返します。もし引数が、バージョン番号を定義するのに不正な文字列を 含んでいた場合、エラーが上ります。

Function: version-compare ver1 ver2

{gauche.version} ふたつのバージョン番号文字列 ver1ver2 を比較し、 ver1 のほうが ver2 より前、ver1ver2 は同じ、 ver1ver2 の後、の3つの場合によって、それぞれ、 -1、0、1 を返します。

Function: relnum-compare rel1 rel2

{gauche.version} これは version-compareの下位レベルの手続きです。 ふたつのリリース番号 (relnum) rel1rel2 を比較し、 rel1rel2 より前、rel1rel2 が同じ、 rel1rel2 より後、の3つの場合によって、それぞれ、 -1、0、1 を返します。

以下の手続きは、与えられたバージョンがバージョン指定を満たすかどうかを 検査するためのものです。

Function: valid-version-spec? spec

{gauche.version} 構文チェッカです。specがバージョン指定として正当なフォームであれば #tを、そうでなければ#fを返します。 バージョン指定フォームについてはモジュールgauche.versionの記述を参照してください。

Function: version-satisfy? spec version

{gauche.version} バージョン番号versionがバージョン指定specを満たせば#tを、 そうでなければ#fを返します。 バージョン指定フォームについてはモジュールgauche.versionの記述を参照してください。



For Development HEAD DRAFTSearch (procedure/syntax/module):
DRAFT