使用 Moose 程式設計/解決的問題/Scalar-Defer
Scalar::Defer 的 lazy 是計算一個值一次並快取它的一種方法,而 Memoize 是一種將函式的輸入和輸出儲存到快取表中以供將來呼叫的方法。 這兩種功能在很大程度上由 Moose 的原生 lazy=>1, default=> 包含。 這種 Moose 的致命組合(lazy 和 default)會快取函式的輸出,並將執行延遲到第一個執行時請求。
從程式上講,一個函式獲取引數或對全域性變數進行操作——如果它既不獲取也不操作,那麼它大機率是不可變[1]。 從面向物件的角度來看,一個方法[2] 經常從物件(它的環境)中獲取變數,有時會修改物件。 舉個例子,像 ->shout 這樣的方法,它可能會檢查物件是否可以 shout(),然後它可能會喊出物件正在 $self->thinking 什麼。 考慮到這一點,讓我們看看 Memoize 是怎麼做的...
“記憶化”一個函式透過用空間換取時間來使它更快。 它透過在表中快取函式的返回值來實現這一點。 如果你用相同的引數再次呼叫函式,“記憶化”就會介入並從表中給你返回值,而不是讓函式再次計算值。
你可能會問,這有什麼問題? 首先,它不是面向物件的[3]; 而且,對方法引用做任何事情都是醜陋且討厭的,應該避免。 Moose 的 Lazy/Default 組合替換了 Memoize 功能的一個小而重要的子集。 如果你只是想快取一個執行時函式的返回值,那麼 Moose 的原生功能可能更適合你。
最後,Memoize 的目的始終是讓某些東西更快,而這與 Moose 的目標在任何方面都不一致。 Moose 透過 lazy/default 提供的功能是出於完全不同的原因,所有這些都與程式設計師的生產力有關,而這正是 Moose 的目標。 Moose 的這種方法的邏輯缺點是,物件只會快取對函式的一次呼叫,需要許多物件才能進行許多呼叫。
至於 Scalar::Defer,這可能更符合 Moose 提供的功能,因為 Scalar::Defer 的目標直接與 Moose 的 Lazy/Default 的目標衝突。 讓我們用一個列表總結 Lazy/Default 組合可以做的事情。
- 只計算一次函式
- 快取輸出
- 延遲評估到第一次呼叫
我(Evan Carroll)以前從未在方法上見過使用記憶化。 這並不是說它不能完成,或者沒有完成過……但,我不會為了讓 Moose 更勝一籌而編造一個例子,我會向你展示一個典型的原始 Memoize。 也就是說,理論上可以把它做得更相似,但實際上不行。
這個例子將使用 lazy/default 來延遲執行和儲存結果; 它經常僅僅出於這個目的而使用。 但是,它也經常用於將資料庫連線延遲到執行時才需要。 這些是同一技術的兩個完全不同的應用。
use Memoize;
memoize( 'complex_operation' );
sub complex_operation {
my $num = shift;
my $result = $num+$num+$num+$num+$num+$num+$num
+$num+$num+$num+$num+$num+$num+$num
+$num+$num+$num+$num+$num+$num+$num
+$num+$num+$num+$num+$num+$num+$num
+$num+$num+$num+$num+$num+$num+$num
+$num+$num+$num+$num+$num+$num+$num
;
}
print complex_operation( 1 );
Moose 將以一種略微不同——面向物件——的方式獲得相同的效果。
package MyMoose;
use Moose;
has 'foo_var' => (
isa => 'Int'
, is => 'ro'
);
has 'foo' => (
isa => 'Int'
, is => 'rw'
, lazy => 1
, default => \&build_foo
);
sub build_foo {
my $self = shift;
my $num = $self->foo_var;
my $result = $num+$num+$num+$num+$num+$num+$num
+$num+$num+$num+$num+$num+$num+$num
+$num+$num+$num+$num+$num+$num+$num
+$num+$num+$num+$num+$num+$num+$num
+$num+$num+$num+$num+$num+$num+$num
+$num+$num+$num+$num+$num+$num+$num
;
}
package main;
my $m = MyMoose->new({ foo_var => 1 });
print $m->foo;
我覺得有義務向你展示這項技術的非常流行的應用。
package MyApp::DB;
use Moose;
use DBI;
has 'dbh' => (
isa => 'Object'
, is => 'ro'
, lazy => 1
, default => \&build_dbh
);
sub build_dbh {
my $self = shift;
my $dbh = DBI->connect(
'dbi:Pg:dbname=myDB;host=localhost'
, 'username'
, 'password'
) or die "Can not connect to DB $DBI::errstr";
$dbh;
}
為了清晰起見,以及那些無法從例子中學習的人
- lazy
- Lazy 可以簡化為:在執行時做這件事。 它允許延遲執行。
- default
- Default 總是與lazy結合使用,告訴 Moose 第一次呼叫 getter 時應該發生什麼。 lazy/default 的組合本質上做了一些事情
- 延遲執行到執行時
- 快取變數以供以後呼叫
- 允許動態過載
因此,如果你不想使用 default/lazy 組合,只需向它傳送一些東西。
Memoize v1.01:POD。 日期 2001-09-21。 訪問日期 2007-10-21