version::Internals - Perl 擴充套件,適用於版本物件
過載版本物件,適用於所有現代版本的 Perl。這記錄了 version.pm 的內部資料表示法和底層程式碼。有關每日使用,請參閱 version.pod。此文件僅對有興趣了解血腥細節的使用者有用。
就本模組而言,版本「數字」是由一個或多個小數點分隔的正整數值序列,並可選擇一個底線。這對應於 Perl 本身用於版本的內容,以及擴充了 Camel 書籍各版本中討論的「版本為數字」。
實際上有兩種不同的版本物件
任何「看起來像數字」的版本,請參閱 「十進位版本」。這也包括具有單一小數點和單一嵌入底線的版本,請參閱 「字母版本」,即使必須加上引號才能保留底線格式。
又稱「點分整數」,包含多個小數點,且可能包含一個嵌入式底線,請參閱 「點分十進位版本」。這是大多數開放原始碼軟體中用作「外部」版本(用於標籤或 tar 檔案名稱的一部分)的常見版本。現在需要開頭的 'v' 字元,如果遺漏,將會發出警告。
這兩種方法都會產生類似的版本物件,因為預設字串化僅在需要時才會產生 「標準格式」 版本。
$v = version->new(1.002); # 1.002, but compares like 1.2.0
$v = version->new(1.002003); # 1.002003
$v2 = version->new("v1.2.3"); # v1.2.3
特別是,初始化為 「十進位版本」 的版本號碼會以其原始建立的樣式字串化(即傳遞給 new()
的字串)。初始化為 「點分十進位版本」 的版本號碼會字串化為 「標準格式」。
這些版本對應於 Perl 本身在 5.6.0 之前的歷史版本,以及所有遵循 $VERSION 標量的 Camel 規則的其他模組。十進位版本使用看起來像浮點數的數字初始化。前導零是重要的,後置零是隱含的,因此子版本之間至少保留三個位數。這表示任何包含少於三個位數的子版本(小數點右邊的位數)都會加上後置零以彌補差異,但僅用於與其他版本物件比較。例如
# Prints Equivalent to
$v = version->new( 1.2); # 1.2 v1.200.0
$v = version->new( 1.02); # 1.02 v1.20.0
$v = version->new( 1.002); # 1.002 v1.2.0
$v = version->new( 1.0023); # 1.0023 v1.2.300
$v = version->new( 1.00203); # 1.00203 v1.2.30
$v = version->new( 1.002003); # 1.002003 v1.2.3
無論輸入值是否加上引號,前述所有範例都是正確的。重要的特點是輸入值僅包含一個小數點。另請參閱 「字母版本」。
重要注意事項:如上所示,如果您的十進位版本在小數點後包含多於 3 個有效位數,它將會在每 3 的倍數處拆分,因此 1.0003 等於 v1.0.300,因為需要與 Perl 本身的 5.005_03 == 5.5.30 解釋保持相容性。任何後置零在數學比較中都會被忽略。
這些是最新形式的版本,對應於從 5.6.0 開始的 Perl 自身版本樣式。從 Perl 5.10.0 開始,而且很可能是 Perl 6,這可能會是首選格式。此方法通常需要輸入參數加上引號,儘管 Perl 5.8.1 之後可以使用 v 字串作為引號的特殊形式,但這並不鼓勵。
與 「十進位版本」 不同,點分十進位版本有多個小數點,例如
# Prints
$v = version->new( "v1.200"); # v1.200.0
$v = version->new("v1.20.0"); # v1.20.0
$v = qv("v1.2.3"); # v1.2.3
$v = qv("1.2.3"); # v1.2.3
$v = qv("1.20"); # v1.20.0
一般來說,點分十進位版本允許最大的自由度來指定版本,而十進位版本則強制執行某種統一性。
就像 "十進位版本" 一樣,點分十進位版本可以用作 "Alpha 版本"。
對於使用 CPAN 的模組作者,慣例是在版本字串中使用底線來標示不穩定的版本。(請參閱 CPAN。)version.pm 遵循此慣例,alpha 版本會測試為比最近的穩定版本更新,但小於下一個穩定版本。只有最後一個元素可以用底線分隔
# Declaring
use version 0.77; our $VERSION = version->declare("v1.2_3");
# Parsing
$v1 = version->parse("v1.2_3");
$v1 = version->parse("1.002_003");
請注意,在撰寫 alpha 十進位版本時,必須引用版本。十進位版本的字串化形式將永遠與用於初始化版本物件的字串相同。
版本字串的合法形式的正式定義包含在 version::regex
類別中。基本元素包含在常見元素中,儘管它們的範圍僅限於檔案,因此它們僅可用於參考目的。有兩個公開存取的標量可以用於其他程式碼(未匯出)
$version::LAX
此正規表示式涵蓋當前版本字串解析器允許的所有合法形式。這並不是說建議使用所有這些形式,其中一些形式只能在引用時使用。
對於點分十進位數
v1.2
1.2345.6
v1.23_4
如果出現兩個或更多小數,則前導的 'v' 是可選的。如果只包含一個小數,則需要前導的 'v' 來觸發點分十進位數分析。允許前導零,但不建議在引用時使用,因為 Perl 會將數字視為八進位數的風險。尾隨底線加上一個或多個數字表示 alpha 或開發版本(且必須引用才能正確分析)。
對於十進位版本
1
1.2345
1.2345_01
需要整數部分、可選的小數點,以及小數點右側可選的一個或多個數字。允許尾隨底線,並允許前導零。就像寬鬆的點分十進位版本一樣,引用值是正確分析 alpha/開發形式的必要條件。
$version::STRICT
此正規表示式涵蓋範圍更有限的格式集,並構成初始化版本物件的最佳實務。然而,您選擇採用十進位或點分十進位是個人偏好。
提供的兩個標量都已編譯為正規表示式,且不包含錨點或隱含群組,因此可以自由包含在您自己的正規表示式中。例如,考慮以下程式碼
($pkg, $ver) =~ /
^[ \t]*
use [ \t]+($PKGNAME)
(?:[ \t]+($version::STRICT))?
[ \t]*;
/x;
這將比對下列形式的列
use Foo::Bar::Baz v1.2.3; # legal only in Perl 5.8.1+
其中 $PKGNAME
是另一個定義套件名稱合法形式的正規表示式。
當 Perl 5.6.0 發布時,決定提供舊式十進位版本與新式點分十進位版本之間的轉換
5.6.0 == 5.006000
5.005_04 == 5.5.40
浮點數會先以單一小數點分割,然後小數點右側每三組數字會組成下一個數字,依此類推,直到用盡有效數字,加上足夠的尾數零以達到下一個三的倍數。
這是 version.pm 也採用的方法。以下是一些有用的範例
equivalent
decimal zero-padded dotted-decimal
------- ----------- --------------
1.2 1.200 v1.200.0
1.02 1.020 v1.20.0
1.002 1.002 v1.2.0
1.0023 1.002300 v1.2.300
1.00203 1.002030 v1.2.30
1.002003 1.002003 v1.2.3
由於 Perl 分析和標記化常式的特性,某些初始化值必須加上引號才能正確分析為預期的版本,特別是在使用 declare
或 "qv()" 方法時。在建立版本物件時,您不必為十進位數字加上引號,但使用 version.pm 方法時,為所有初始化值加上引號總是安全的,因為這將確保您輸入的內容就是使用的內容。
此外,如果您為初始化值加上引號,那麼輸入的帶引號值將會與您的 $VERSION 列印(字串化)時輸出的內容完全相同。如果您不為值加上引號,Perl 的一般數字處理就會發揮作用,您可能無法取得預期的結果。
如果您使用會解析為浮點數的數學公式,您就依賴 Perl 的轉換常式產生您預期的版本。例如,透過除以 10 的次方,您相當安全,但其他運算不太可能是您預期的。例如
$VERSION = version->new((qw$Revision: 1.4)[1]/10);
print $VERSION; # yields 0.14
$V2 = version->new(100/9); # Integer overflow in decimal number
print $V2; # yields something like 11.111.111.100
Perl 5.8.1 及更新版本能夠自動為 v 字串加上引號,但在較早版本的 Perl 中無法這麼做。換句話說
$version = version->new("v2.5.4"); # legal in all versions of Perl
$newvers = version->new(v2.5.4); # legal only in Perl >= 5.8.1
輸入 v-字串有兩種方式:一個沒有小數點或有兩個以上小數點的數字,或是一個沒有小數點或有一個以上小數點且前面有一個「v」字元(也沒有小數點)的數字。例如
$vs1 = 1.2.3; # encoded as \1\2\3
$vs2 = v1.2; # encoded as \1\2
不過,在任何情況下都強烈建議不要使用沒有小數點的 v-字串來初始化版本物件。此外,在 Perl 5.8.1 之前的任何版本中,都沒有完全支援沒有小數點的 v-字串。
如果你堅持要在 Perl > 5.6.0 中使用沒有小數點的 v-字串,請注意以下限制
1) 對於 Perl 版本 5.6.0 到 5.8.0,v-字串程式碼只會根據 v-字串的一些特性進行猜測。你必須使用三個部分的版本,例如 1.2.3 或 v1.2.3,才能讓這個啟發式方法成功。
2) 對於 Perl 版本 5.8.1 和更新版本,Perl 核心中的 v-字串已變更為神奇字串,這表示 version.pm 程式碼可以自動判斷是否使用了 v-字串編碼。
3) 在所有情況下,使用 v-字串建立的版本都會有一個字串化形式,前面有一個「v」字元,原因很簡單,因為有時不可能判斷一開始是否有「v」字元。
version.pm 提供一個超載的版本物件,設計用來封裝作者預期的 $VERSION 指定,並讓它可以像數字一樣自然地使用這些物件(例如用於比較)。為此,版本物件包含作者輸入的原始表示法,以及一個已剖析的表示法,以簡化比較。版本物件使用 超載 方法來簡化需要比較、列印等物件的程式碼。
版本物件的內部結構是一個經過祝福的雜湊,有幾個組成部分
bless( {
'original' => 'v1.2.3_4',
'alpha' => 1,
'qv' => 1,
'version' => [
1,
2,
3,
4
]
}, 'version' );
用來初始化這個版本物件的值的忠實表示法。唯一不會與原始檔中存在的字元完全相同的時間,是如果使用了像 v1.2 這樣的短點分小數版本(這種情況下它會包含「v1.2」)。強烈建議不要使用這種形式,因為它會讓你和你的使用者感到困惑。
一個布林值,表示這是十進位或點分十進位版本。請參閱 "is_qv()" in version。
一個布林值,表示這是 alpha 版本。注意:底線只能出現在最後一個位置。請參閱 "is_alpha()" in version。
一個非負整數陣列,用於與其他版本物件進行比較。
除了版本物件之外,此模組也用一個使用版本物件進行比較的核心 UNIVERSAL::VERSION 函式取代。此運算子的回傳值總是字串化的形式,為一個簡單的純量(即非物件),但產生的警告訊息會包含字串化的形式或一般形式,視其呼叫方式而定。
例如
package Foo;
$VERSION = 1.2;
package Bar;
$VERSION = "v1.3.5"; # works with all Perl's (since it is quoted)
package main;
use version;
print $Foo::VERSION; # prints 1.2
print $Bar::VERSION; # prints 1.003005
eval "use foo 10";
print $@; # prints "foo version 10 required..."
eval "use foo 1.3.5; # work in Perl 5.6.1 or better
print $@; # prints "foo version 1.3.5 required..."
eval "use bar 1.3.6";
print $@; # prints "bar version 1.3.6 required..."
eval "use bar 1.004"; # note Decimal version
print $@; # prints "bar version 1.004 required..."
重要事項:這表示搜尋特定字串(以判斷特定模組是否可用)的程式碼可能需要變更。永遠建議使用 use
或 require
中內建的比較,而非手動調整 class->VERSION
,再自行進行比較。
替代的 UNIVERSAL::VERSION,當以函式使用時,如下所示
print $module->VERSION;
也將只回傳字串化的形式。請參閱 "字串化" 以取得更多詳細資訊。
version.pm 模組盡可能維持與所有現有程式碼的相容性。不過,如果您的模組使用已使用版本類別定義 $VERSION
的模組,則有幾件事需要注意。為了討論目的,我們假設已安裝下列模組
package Example;
use version; $VERSION = qv('1.2.2');
...module code here...
1;
以下形式的程式碼
use Example 1.002003;
將永遠正確運作。use
將使用模組名稱後第一個項目給定的浮點數值(例如上方為 1.002.003)執行自動 $VERSION
比較。在此情況下,已安裝的模組對要求的行太舊,因此您會看到類似以下的錯誤
Example version 1.002003 (v1.2.3) required--this is only version 1.002002 (v1.2.2)...
在 Perl >= 5.6.2 中,您也可以使用類似以下的行
use Example 1.2.3;
而且它將再次運作(即給出如上所述的錯誤訊息),即使使用通常不支援 v 字串的 Perl 版本(請參閱上方的「v 字串怎麼樣?」)。這與下列事實有關:use
只檢查第二個字詞是否「看起來像數字」,並將其傳遞給替換 UNIVERSAL::VERSION。然而,這在 Perl 5.005_04 中並非如此,因此強烈建議您在程式碼中始終使用十進位版本,即使對於支援點分十進位版本的 Perl 版本也是如此。
與許多 OO 介面一樣,new() 方法用於初始化版本物件。如果將兩個引數傳遞給 new()
,則會將第二個引數用於「v」開頭。這是為了支援使用 qw
算子與 CVS 變數 $Revision 的歷史用法,而 CVS 會在每次將檔案提交至儲存庫時自動遞增 $Revision。
為了簡化此功能,可以使用下列程式碼
$VERSION = version->new(qw$Revision: 2.7 $);
而且會建立版本物件,就像使用下列程式碼一樣
$VERSION = version->new("v2.7");
換句話說,版本會自動從字串中解析出來,而且會加上引號,以保留 CVS 通常對版本所具有的意義。CVS $Revision$ 的遞增方式不同於十進位版本(即 1.10 接在 1.9 之後),所以必須將其視為點分十進位版本來處理。
可以建立一個新版本物件作為現有版本物件的副本,作為類別方法
$v1 = version->new(12.3);
$v2 = version->new($v1);
或作為物件方法
$v1 = version->new(12.3);
$v2 = $v1->new(12.3);
而且在每種情況下,$v1 和 $v2 都會相同。注意:如果您使用現有物件建立新物件,如下所示
$v2 = $v1->new();
新物件不會是現有物件的複製。在範例案例中,$v2 會是與 $v1 相同類型的空物件。
建立新版本物件的另一種方法是透過匯出的 qv() 子常式。這與其他 q? 算子(例如 qq、qw)並不完全相同,因為唯一支援的分隔符號是括號(或空格)。這是初始化簡短版本而不觸發浮點數詮釋的最佳方法。例如
$v1 = qv(1.2); # v1.2.0
$v2 = qv("1.2"); # also v1.2.0
如您所見,通常可以互換使用裸數字或帶引號的字串,但尾數零的情況除外,必須加上引號才能正確轉換。因此,強烈建議 qv() 的所有初始化項都使用帶引號的字串,而不是裸數字。
若要防止 qv()
函數匯出至呼叫者的命名空間,請使用帶有空參數的版本
use version ();
或僅需要版本,如下所示
require version;
這兩種方法都會防止 import() 方法觸發並匯出 qv()
子項。
以下範例將使用下列三個物件
$ver = version->new("1.2.3.4"); # see "Quoting Rules"
$alpha = version->new("1.2.3_4"); # see "Alpha Versions"
$nver = version->new(1.002); # see "Decimal Versions"
對於任何使用多個小數位元 (已引用或可能為 v 字串) 初始化,或使用 qv() 算子初始化的版本物件,字串化表示會以標準化或簡化的格式 (無多餘零) 回傳,並帶有前導 'v'
print $ver->normal; # prints as v1.2.3.4
print $ver->stringify; # ditto
print $ver; # ditto
print $nver->normal; # prints as v1.2.0
print $nver->stringify; # prints as 1.002,
# see "Stringification"
為了保留已處理版本的意義,標準化表示會始終包含至少三個子項。換句話說,以下保證永遠為真
my $newver = version->new($ver->stringify);
if ($newver eq $ver ) # always true
{...}
雖然預設禁止對版本物件執行所有數學運算,但可以透過使用 $obj->numify 方法來擷取對應版本物件的數字。在格式化的目的上,在顯示對應版本物件的數字時,假設所有子版本都有三個小數位元。例如
print $ver->numify; # prints 1.002003004
print $nver->numify; # prints 1.002
與字串化算子不同,永遠不需要附加尾數零來保留正確的版本值。
版本物件的預設字串化會回傳與用於建立它的字串完全相同的字串,無論您使用 new()
或 qv()
,只有一個例外。唯一的例外是如果物件是使用 qv()
建立,且初始化器沒有兩個小數位元或前導 'v' (兩者都是選用的),則字串化格式會有一個前導 'v' 加在前面,以支援來回處理。
例如
Initialized as Stringifies to
============== ==============
version->new("1.2") 1.2
version->new("v1.2") v1.2
qv("1.2.3") 1.2.3
qv("v1.3.5") v1.3.5
qv("1.2") v1.2 ### exceptional case
另請參閱 UNIVERSAL::VERSION,因為當用作類別方法時,它也會回傳字串化格式。
重要注意事項:上表中顯示了一個例外情況,其中「初始化器」與字串化表示在字串上不相等。如果您對沒有前導 'v' 且只有一個小數位元的版本使用 qv
() 算子,則字串化輸出會有前導 'v',以保留意義。請參閱 "qv()" 算子以取得更多詳細資訊。
重要注意事項 2:嘗試透過手動套用 numify() 和 normal() 來繞過正常的字串化規則,有時會產生令人驚訝的結果
print version->new(version->new("v1.0")->numify)->normal; # v1.0.0
原因是 numify() 算子會將「v1.0」轉換為等效字串「1.000000」。強制外部版本物件為 normal() 格式會顯示數學等效的「v1.0.0」。
如 "new()" 中的範例所示,你可以使用非常簡潔的
$v2 = $v1->new($v1);
來建立一個現有版本物件的副本,且其值相同,並確保 $v1
和 $v2
完全相等,包括相同的內部表示和字串化。
cmp
和 <=>
運算子在字詞之間執行相同的比較(自動升級到版本物件)。Perl 會自動根據這兩個運算子產生所有其他比較運算子。除了下面列出的明顯相等性之外,附加一個尾隨的 0 字詞並不會改變版本在比較目的下的值。換句話說,「v1.2」和「1.2.0」會被比較為相同。
例如,以下關係成立
As Number As String Truth Value
------------- ---------------- -----------
$ver > 1.0 $ver gt "1.0" true
$ver < 2.5 $ver lt true
$ver != 1.3 $ver ne "1.3" true
$ver == 1.2 $ver eq "1.2" false
$ver == 1.2.3.4 $ver eq "1.2.3.4" see discussion below
為了減少混淆,最好選擇十進位記法或字串記法並堅持使用。Perl6 版本物件可能只支援十進位比較。另請參閱 "引號規則"。
警告:比較具有不等小數點數量的版本(無論是明確或隱式初始化),乍看之下可能會產生意外的結果。例如,以下不等式成立
version->new(0.96) > version->new(0.95); # 0.960.0 > 0.950.0
version->new("0.96.1") < version->new(0.95); # 0.096.1 < 0.950.0
如果你需要測試版本物件是否已初始化,你可以直接測試它
$vobj = version->new($something);
if ( $vobj ) # true only if $something was non-blank
你也可以測試版本物件是否為 alpha 版本,例如為了防止使用主要版本中不存在的一些功能
$vobj = version->new("1.2_3"); # MUST QUOTE
...later...
if ( $vobj->is_alpha ) # True
John Peacock <jpeacock@cpan.org>
perl.