Actor classes:
Actor classes能够允许你以编程的方式创建canister智能合约。目前,actor classes不得不定义在一个单独的.mo源文件中。为了说明如何定义并导入(import) actor classes,接下来的例子演示一个以Nat类型为键映射到Text类型的分布式map。它提供简单的插入put(k, v)
和查询函数get(k)
,用于处理这些键和值。
为了分配该示例的数据,一系列键值被划分到n个容器(buckets)中。现在,我们仅设置n = 8
.键值k对应的容器i由n除以k的余数来决定,即i = k % n
。第i个容器( i in [0..n) )接受一个被分配的actor来存储该键值所对应的text值。
正如被定义在样例Buckets.mo文件中的那样,负责第i个容器的actor被作为actor class Bucket(i)
的实例来获取:
import Nat "mo:base/Nat";
import Map "mo:base/RBTree";
actor class Bucket(n : Nat, i : Nat) {
type Key = Nat;
type Value = Text;
let map = Map.RBTree<Key, Value>(Nat.compare);
public func get(k : Key) : async ?Value {
assert((k % n) == i);
map.get(k);
};
public func put(k : Key, v : Value) : async () {
assert((k % n) == i);
map.put(k,v);
};
};
A bucket stores the current mapping of keys to values in a mutable map
variable containing an imperative RedBlack tree, map
, that is initially empty.
On get(k)
, the bucket actor simply returns any value stored at k
, returning map.get(k)
.
On put(k, v)
, the bucket actor updates the current map
to map k
to ?v
by calling map.put(k, v)
.
Both functions use the class parameters n
and i
to verify that the key is appropriate for the bucket by asserting ((k % n) == i)
.
然后,map的使用者可以通过Map actor与之交互,实现如下:
import Array "mo:base/Array";
import Buckets "Buckets";
actor Map {
let n = 8; // number of buckets
type Key = Nat;
type Value = Text;
type Bucket = Buckets.Bucket;
let buckets : [var ?Bucket] = Array.init(n, null);
public func get(k : Key) : async ?Value {
switch (buckets[k % n]) {
case null null;
case (?bucket) await bucket.get(k);
};
};
public func put(k : Key, v : Value) : async () {
let i = k % n;
let bucket = switch (buckets[i]) {
case null {
let b = await Buckets.Bucket(n, i); // dynamically install a new Bucket
buckets[i] := ?b;
b;
};
case (?bucket) bucket;
};
await bucket.put(k, v);
};
};
As this example illustrates, the Map
code imports the Bucket
actor class as module Buckets
.
The actor maintains an array of n
allocated buckets, with all entries initially null
. Entries are populated with Bucket
actors on demand.
On get(k, v)
, the Map
actor:
- uses the remainder of key
k
divided byn
to determine the indexi
of the bucket responsible for that key - returns
null
if thei
th bucket does not exist, or - delegates to that bucket by calling
bucket.get(k, v)
if it does.
On put(k, v)
, the Map
actor:
- uses the remainder of key
k
divided byn
to determine the indexi
of the bucket responsible for that key - installs bucket
i
if the bucket does not exist by using an asynchronous call to the constructor,Buckets.Bucket(i)
, and, after awaiting the result, records it in the arraybuckets
- delegates the insertion to that bucket by calling
bucket.put(k, v)
.
While this example sets the number of buckets to 8
, you can easily generalize the example by making the Map
actor an actor class, adding a parameter (n : Nat)
and omitting the declaration let n = 8;
. For example:
actor class Map(n : Nat) {
type Key = Nat
...
}
现在,Map actor class的使用者可以通过构建actor时传递一个参数在网络中自由决定容器的(最大)数量。
Comments 1 条评论
博客作者 Usha Fiorillo
Would love to always get updated great site! .