热门问题
时间线
聊天
视角
惰性初始化
来自维基百科,自由的百科全书
Remove ads
在程式設計中, 惰性初始化(英語:lazy initialization)是一種拖延戰術。在第一次需求出現以前,先延遲創建物件、計算值或其它昂貴程序。這通常是以一個旗號來實現,用旗號來標示是否完成其程式。每次請求對象時,會先測試此旗號。如果已完成,直接傳回,否則當場執行。
對於此想法更一般的論述,可見惰性求值。對指令式語言,這個模式可能潛藏著危險,尤其是使用共享狀態的程式習慣。
"惰性工廠"
计算机科学
在理论计算机科学领域中,惰性初始化(也叫做惰性数组)[2],是设计数据结构的技术,使其可以工作于不需要被初始化的内存。尤其是假定了要访问n
个(编号从 1
到n
)未初始化内存单元的一个表格T
,并希望赋值这个数组的m
个单元,比如赋值T[ki] := vi
,它针对键值对(k1, v1), ..., (km, vm)
,并具有所有的ki
都是不同的。惰性初始化技术允许只用O(m)
次运算来完成它,而非耗费O(m+n)
次运算来首先初始化所有数组单元。这项技术简单的分配一个表格V
以任意次序存储键值对(ki, vi)
,并为每个i
在单元T[ki]
中写入键ki
存储在V
中的位置,保留T
的其他单元不初始化。这可以用来处理下列方式的查询:在针对某个k
查看单元T[k]
的时候,可以在{1, ..., m}
范围内检查T[k]
;如果不在其中,则T[k]
是未初始化的。否则检查V[T[k]]
,并验证这个键值对的第一个组件等于k
;如果不等于,则T[k]
是未初始化的(它只是偶然落入范围{1, ..., m}
中);否则确知了T[k]
是初始化的单元之一,而对应的值是这个键值对的第二个组件。
Remove ads
示例
在下面的C#例子中,Fruit
類別本身在這裡不做任合事。_typesDictionary
變數則是一個存 Fruit
實例的 Dictionary 或 Map ,其透過typeName
來存取。
using System;
using System.Collections;
using System.Collections.Generic;
public class Fruit
{
private string _typeName;
private static Dictionary<string, Fruit> _typesDictionary = new Dictionary<string, Fruit>();
private Fruit(String typeName)
{
this._typeName = typeName;
}
public static Fruit GetFruitByTypeName(string type)
{
Fruit fruit;
if (!_typesDictionary.ContainsKey(type))
{
// 惰性初始
fruit = new Fruit(type);
_typesDictionary.Add(type, fruit);
}
else
fruit = _typesDictionary[type];
return fruit;
}
public static void ShowAll()
{
if (_typesDictionary.Count > 0)
{
Console.WriteLine("Number of instances made = {0}", _typesDictionary.Count);
foreach (KeyValuePair<string, Fruit> kvp in _typesDictionary)
{
Console.WriteLine(kvp.Key);
}
Console.WriteLine();
}
}
}
class Program
{
static void Main(string[] args)
{
Fruit.GetFruitByTypeName("Banana");
Fruit.ShowAll();
Fruit.GetFruitByTypeName("Apple");
Fruit.ShowAll();
// returns pre-existing instance from first
// time Fruit with "Banana" was created
Fruit.GetFruitByTypeName("Banana");
Fruit.ShowAll();
Console.ReadLine();
}
}
Remove ads
Java例子:
import java.util.*;
public class Fruit
{
private static final Map<String,Fruit> types = new HashMap<String,Fruit>();
private final String type;
// using a private constructor to force use of the factory method.
private Fruit(String type) {
this.type = type;
}
/**
* Lazy Factory method, gets the Fruit instance associated with a
* certain type. Instantiates new ones as needed.
* @param type Any string that describes a fruit type, e.g. "apple"
* @return The Fruit instance associated with that type.
*/
public static synchronized Fruit getFruit(String type) {
if(!types.containsKey(type))
types.put(type, new Fruit(type)); // Lazy initialization
return types.get(type);
}
}
Remove ads
C++的例子:
import std;
class Fruit {
private:
static std::map<std::string, Fruit*> types;
std::string type_;
// Note: constructor private forcing one to use static |GetFruit|.
Fruit(const std::string& type):
type_{type} {}
public:
// Lazy Factory method, gets the |Fruit| instance associated with a certain
// |type|. Creates new ones as needed.
static Fruit* getFruit(const std::string& type) {
auto [it, inserted] = types.emplace(type, nullptr);
if (inserted) {
it->second = new Fruit(type);
}
return it->second;
}
// For example purposes to see pattern in action.
static void printCurrentTypes() {
std::println("Number of instances made = {}", types.size());
for (const auto& [type, fruit] : types) {
std::println({}, type);
}
std::println();
}
};
// static
std::map<std::string, Fruit*> Fruit::types;
int main(int argc, char* argv[]) {
Fruit::getFruit("Banana");
Fruit::printCurrentTypes();
Fruit::getFruit("Apple");
Fruit::printCurrentTypes();
// Returns pre-existing instance from first time |Fruit| with "Banana" was
// created.
Fruit::getFruit("Banana");
Fruit::printCurrentTypes();
}
// OUTPUT:
//
// Number of instances made = 1
// Banana
//
// Number of instances made = 2
// Apple
// Banana
//
// Number of instances made = 2
// Apple
// Banana
//
Remove ads
import functools
class lazyproperty():
def __init__(self, f):
self.f = f
functools.update_wrapper(self, f)
def __get__(self, obj, objtype=None):
if obj is None:
#Invocation from a class
return self
val = self.f(obj)
obj.__dict__[self.f.__name__] = val
return val
class Employee():
def __init__(self, name, employer):
self.name = name
self.employer = employer
@lazyproperty
def summary_report(self):
#Spending a lot of time and effort to get a result
result = "Voluminous document: 100,000 words omitted here."
return result
其执行:
>>> employee = Employee("Taciturn Man", "Large Institutions")
>>> employee.summary_report
'Voluminous document: 100,000 words omitted here.'
Remove ads
下面的Smalltalk例子,具有典型的访问子方法,它返回使用惰性初始化的一个变量的值。
height
^height ifNil: [height := 2.0].
非惰性的替代者,使用的初始化方法是在对象被创建时运行的,并且使用简单的访问子方法来取回这个值。
initialize
height := 2.0
height
^height
注意惰性初始化也可以用在非面向对象编程语言中。
下面的Ruby例子,具有惰性初始化的来自远程服务的身份验证令牌。 @auth_token
被缓存的方式也是记忆化的示例。
require 'net/http'
class Blogger
def auth_token
@auth_token ||=
(res = Net::HTTP.post_form(uri, params)) &&
get_token_from_http_response(res)
end
# get_token_from_http_response, uri and params are defined later in the class
end
b = Blogger.new
b.instance_variable_get(:@auth_token) # returns nil
b.auth_token # returns token
b.instance_variable_get(:@auth_token) # returns token
PHP 7.4的惰性初始化的例子:
<?php
header('Content-Type: text/plain; charset=utf-8');
class Fruit
{
private string $type;
private static array $types = array();
private function __construct(string $type)
{
$this->type = $type;
}
public static function getFruit(string $type): Fruit
{
// Lazy initialization takes place here
if (!isset(self::$types[$type])) {
self::$types[$type] = new Fruit($type);
}
return self::$types[$type];
}
public static function printCurrentTypes(): void
{
echo 'Number of instances made: ' . count(self::$types) . "\n";
foreach (array_keys(self::$types) as $key) {
echo "$key\n";
}
echo "\n";
}
}
Fruit::getFruit('Apple');
Fruit::printCurrentTypes();
Fruit::getFruit('Banana');
Fruit::printCurrentTypes();
Fruit::getFruit('Apple');
Fruit::printCurrentTypes();
/*
OUTPUT:
Number of instances made: 1
Apple
Number of instances made: 2
Apple
Banana
Number of instances made: 2
Apple
Banana
*/
Remove ads
另見
引用
外部連結
Wikiwand - on
Seamless Wikipedia browsing. On steroids.
Remove ads