hack集合:Examples
以下是有关如何使用Hack集合的各种示例。
简单的矢量使用
这个简单的例子显示了如何创建,查询,添加项目和从中删除项目Vector。
<?hh
namespace Hack\UserDocumentation\Collections\Examples\Examples\Vec;
function use_vector(): void {
$fruit_basket = Vector {'Banana', 'Apple'}; // initialize
$fruit_basket[] = 'Orange'; // Add to the next available index, which is 2
$fruit_basket[] = 'Banana'; // You can have duplicates
var_dump($fruit_basket);
try {
// Can't use an explicit index that doesn't exist, even if it would be the
// next available index.
$fruit_basket[4] = 'Plum';
} catch (\OutOfBoundsException $ex) {
var_dump($ex->getMessage());
}
// Query the fruit at index 1
var_dump($fruit_basket[1]);
// Iterate over the entire fruit basket using foreach
foreach ($fruit_basket as $fruit) {
// This should only be a query. Do not add or remove from a Vector while
// in a foreach
var_dump($fruit);
}
// Iterate over some of the fruit basket using for and ints
for ($i = 0; $i < $fruit_basket->count() - 2; $i++) {
if ($i % 2 === 0) {
// We can change the vector in a for loop by index
$fruit_basket[$i] = 'Grape';
}
}
var_dump($fruit_basket);
// Remove an item
$fruit_basket->removeKey(0);
var_dump($fruit_basket);
}
use_vector();
Output
object(HH\Vector)#1 (4) {
[0]=>
string(6) "Banana"
[1]=>
string(5) "Apple"
[2]=>
string(6) "Orange"
[3]=>
string(6) "Banana"
}
string(30) "Integer key 4 is out of bounds"
string(5) "Apple"
string(6) "Banana"
string(5) "Apple"
string(6) "Orange"
string(6) "Banana"
object(HH\Vector)#1 (4) {
[0]=>
string(5) "Grape"
[1]=>
string(5) "Apple"
[2]=>
string(6) "Orange"
[3]=>
string(6) "Banana"
}
object(HH\Vector)#1 (3) {
[0]=>
string(5) "Apple"
[1]=>
string(6) "Orange"
[2]=>
string(6) "Banana"
}
简单的地图使用
这个简单的例子显示了如何创建,查询,添加项目和从中删除项目Map。
<?hh
namespace Hack\UserDocumentation\Collections\Examples\Examples\UsingMap;
function use_map(): void {
$users = Map {1 => 'Joel', 2 => 'Fred'}; // initialize using keys and values
try {
// You must specify an explicit key when adding an item to a map
// You will also get an Invalid Assignment type error when doing the below
$users[] = 'Matthew'; // Add to the next available index, which is 2
} catch (\InvalidArgumentException $ex) {
var_dump($ex->getMessage());
}
// Instead add to a map this way
$users[3] = 'Matthew';
// Or this way
$users[] = Pair {4, 'Rex'};
var_dump($users);
// Query the user at id 1
var_dump($users[1]);
// Iterate over the entire fruit basket using foreach
foreach ($users as $id=>$name) {
// This should only be a query. Do not add or remove from a Vector while
// in a foreach
echo 'The user name at id: ' . strval($id) . ' is ' . $name . PHP_EOL;
}
// Remove an item
$users->removeKey(2);
var_dump($users);
}
use_map();
Output
string(37) "Parameter must be an instance of Pair"
object(HH\Map)#1 (4) {
[1]=>
string(4) "Joel"
[2]=>
string(4) "Fred"
[3]=>
string(7) "Matthew"
[4]=>
string(3) "Rex"
}
string(4) "Joel"
The user name at id: 1 is Joel
The user name at id: 2 is Fred
The user name at id: 3 is Matthew
The user name at id: 4 is Rex
object(HH\Map)#1 (3) {
[1]=>
string(4) "Joel"
[3]=>
string(7) "Matthew"
[4]=>
string(3) "Rex"
}
运用 filter()
filter() 允许您通过在集合的每个项目上应用条件来创建当前集合的子集(包括为空)的集合,如果该元素的条件为真,则它将成为新集合的一部分。
<?hh
namespace Hack\UserDocumentation\Collections\Examples\Examples\FilterM;
function filter_evens(Set<int> $numbers): Set<int> {
return $numbers->filter($x ==> $x % 2 === 0);
// We used a lambda above. This could have also been written as:
//return $numbers->filter(function (int $x): bool {return $x % 2 === 0;});
}
function run(): void {
$numbers = Set {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
var_dump(filter_evens($numbers));
}
run();
Output
object(HH\Set)#3 (6) {
int(0)
int(2)
int(4)
int(6)
int(8)
int(10)
}
请注意,我们使用lambda来创建过滤操作。我们也可以使用正常的关闭。
运用 map()
map()允许您创建一个与map()调用的集合具有相同元素的集合,但是对每个元素应用了一些操作。
<?hh
namespace Hack\UserDocumentation\Collections\Examples\Examples\MapM;
function apply_prefix(Vector<string> $phrases): Vector<string> {
return $phrases->map($x ==> 'Hello ' . $x);
// We used a lambda above. This could have also been written as:
//return $phrases->map(function (string $x): string {return 'Hello' . $x;});
}
function run(): void {
$phrases = Vector {'Joel', 'Rex', 'Fred', 'Matthew', 'Tim', 'Jez'};
var_dump(apply_prefix($phrases));
}
run();
Output
object(HH\Vector)#3 (6) {
[0]=>
string(10) "Hello Joel"
[1]=>
string(9) "Hello Rex"
[2]=>
string(10) "Hello Fred"
[3]=>
string(13) "Hello Matthew"
[4]=>
string(9) "Hello Tim"
[5]=>
string(9) "Hello Jez"
}
请注意,我们正在使用lambda来创建映射操作。我们也可以使用正常的关闭。
参考语义
Hack集合具有引用语义,而数组具有值语义。
<?hh
namespace Hack\UserDocumentation\Collections\Examples\Examples\Reference;
function vec_ref(Vector<int> $vec): Vector<int> {
for ($i = 0; $i < $vec->count(); $i++) {
// Changing the vector here actually changes the vector from the calling
// site
$vec[$i] += 2;
}
return $vec;
}
function arr_value(array<int> $arr): array<int> {
for ($i = 0; $i < count($arr); $i++) {
// Changing the array does not change the array at the calling site
$arr[$i] += 2;
}
return $arr;
}
function run(): void {
echo "-- VECTOR HAS REFERENCE SEMANTICS --\n\n";
$vec = Vector {0, 1, 2, 3, 4};
var_dump($vec);
vec_ref($vec);
var_dump($vec);
echo "\n\n-- ARRAY HAS VALUE SEMANTICS --\n\n";
$arr = array(0, 1, 2, 3, 4);
var_dump($arr);
arr_value($arr);
var_dump($arr);
}
run();
Output
-- VECTOR HAS REFERENCE SEMANTICS --
object(HH\Vector)#1 (5) {
[0]=>
int(0)
[1]=>
int(1)
[2]=>
int(2)
[3]=>
int(3)
[4]=>
int(4)
}
object(HH\Vector)#1 (5) {
[0]=>
int(2)
[1]=>
int(3)
[2]=>
int(4)
[3]=>
int(5)
[4]=>
int(6)
}
-- ARRAY HAS VALUE SEMANTICS --
array(5) {
[0]=>
int(0)
[1]=>
int(1)
[2]=>
int(2)
[3]=>
int(3)
[4]=>
int(4)
}
array(5) {
[0]=>
int(0)
[1]=>
int(1)
[2]=>
int(2)
[3]=>
int(3)
[4]=>
int(4)
}
Lazy
Hack集合使用该方法提供了Scala类视图lazy()。通常,当您创建集合的实例时,您将创建一个严格版本的集合。因此,当您创建一个包含一百万个元素的集合时,会立即为所有这些元素分配内存。然而,在一个视图通过lazy(),则可以创建一个非严格集合,使得当您使用的方法类似map()或filter()在收集(即,变压器方法),所述元件仅被访问时进行计算。
此示例显示了如何lazy()在一个相当大的集合上使用,以及对集合的严格和非严格版本的时间。由于我们最终只需要5个元素,所以懒惰的视图实际上允许我们在满足我们所需的5之后停止,而无需实际分配所有1000000个元素。
<?hh
namespace Hack\UserDocumentation\Collections\Examples\Examples\LazyM;
$vector = new Vector(range(0, 1000000));
$s = microtime(true);
$non_lazy = $vector->filter($x ==> $x % 2 === 0)->take(5);
$e = microtime(true);
var_dump($non_lazy);
echo "Time non-lazy: " . strval($e - $s) . PHP_EOL;
// Using a lazy view of the vector can save us a bunch of time, possibly even
// cutting this call time by 90%.
$s = microtime(true);
$lazy = $vector->lazy()->filter($x ==> $x % 2 === 0)->take(5);
$e = microtime(true);
var_dump($lazy->toVector());
echo "Time lazy: " . strval($e - $s) . PHP_EOL;
Output
object(HH\Vector)#4 (5) {
[0]=>
int(0)
[1]=>
int(2)
[2]=>
int(4)
[3]=>
int(6)
[4]=>
int(8)
}
Time non-lazy: 0.27819204330444
object(HH\Vector)#9 (5) {
[0]=>
int(0)
[1]=>
int(2)
[2]=>
int(4)
[3]=>
int(6)
[4]=>
int(8)
}
Time lazy: 0.015102863311768
Indexish
Indexish是一个表示可以由密钥建立索引的集合的接口。集合中的键可以是string或int。因此,您可以用于Indexish表示具有这两种关键类型之一的Hack集合。
<?hh
namespace Hack\UserDocumentation\Collections\Examples\Examples\IndexishIface;
// Since this returns Indexish, we can return both a Vector and Map from this
// function. Could also return a Pair too (not a Set since you don't access a
// Set by key).
function return_indexish(bool $which): \Indexish<int, mixed> {
return $which ? Vector {100, 200, 300} : Map {0 => 'A', 1 => 'B', 2 => 'C'};
}
function run(): void {
var_dump(return_indexish(true));
var_dump(return_indexish(false));
}
run();
Output
object(HH\Vector)#1 (3) {
[0]=>
int(100)
[1]=>
int(200)
[2]=>
int(300)
}
object(HH\Map)#1 (3) {
[0]=>
string(1) "A"
[1]=>
string(1) "B"
[2]=>
string(1) "C"
}
创建新集合
当前的具体收集类被标记为final。也就是说,它们不能被直接扩展和分类。但是,您可以使用提供的接口创建新的集合类。这个例子将不会创建一个完整的新集合,因为这样很多的方法必须被实现Iterable<T> 比这里介绍的更多的审查和细节。
但是,这个例子确实显示了如果你想创建一个新的集合集必须实现的所有方法。
<?hh
namespace Hack\UserDocumentation\Collections\Examples\Examples\SimpleColl;
final class AbsurdSet<Tv> implements \MutableSet<Tv> {
private \Set $cs;
public function __construct(?Traversable<Tv> $values) {
if (!$values) {
$this->cs = Set {0};
} else {
invariant($values !== null, "won't happen");
$this->cs = new Set($values);
foreach ($values as $value) {
// Yes, this is ridiculous
if (is_int($value)) {
$this->cs[] = $value + 2;
}
}
}
}
public function values(): \Vector<Tv> {
return $this->cs->values();
}
public function keys(): \Vector<mixed> {
return $this->cs->keys();
}
public function map<Tu>((function(Tv): Tu) $fn): \Set<Tu> {
return $this->cs->map($fn);
}
public function mapWithKey<Tu>(
(function(mixed, Tv): Tu) $fn): \Set<Tu> {
return $this->cs->mapWithKey($fn);
}
public function filter((function(Tv): bool) $fn): \Set<Tv> {
return $this->cs->filter($fn);
}
public function filterWithKey(
(function(mixed, Tv): bool) $fn): \Set<Tv> {
return $this->cs->filterWithKey($fn);
}
public function zip<Tu>(
Traversable<Tu> $traversable): \Set<Pair<Tv, Tu>> {
return $this->cs->zip($traversable);
}
public function take(int $n): \Set<Tv> {
return $this->cs->take($n);
}
public function takeWhile((function(Tv): bool) $fn): \Set<Tv> {
return $this->cs->takeWhile($fn);
}
public function skip(int $n): \Set<Tv> {
return $this->cs->skip($n);
}
public function skipWhile((function(Tv): bool) $fn): \Set<Tv> {
return $this->cs->skipWhile($fn);
}
public function slice(int $start, int $len): \Set<Tv> {
return $this->cs->slice($start, $len);
}
public function concat<Tu super Tv>(
Traversable<Tu> $traversable): \Vector<Tu> {
return $this->cs->concat($traversable);
}
public function firstValue(): ?Tv {
return $this->cs->firstValue();
}
public function firstKey(): mixed {
return $this->cs->firstKey();
}
public function lastValue(): ?Tv {
return $this->cs->lastValue();
}
public function lastKey(): mixed {
return $this->cs->lastKey();
}
public function isEmpty(): bool {
return $this->cs->isEmpty();
}
public function count(): int {
return $this->cs->count();
}
public function contains<Tu super Tv>(Tu $value): bool {
return $this->cs->contains($value);
}
public function add(Tv $value): AbsurdSet<Tv> {
return new AbsurdSet($this->cs->add($value));
}
public function addAll(?Traversable<Tv> $values): AbsurdSet<Tv> {
return new AbsurdSet($this->cs->addAll($values));
}
public function clear(): \Set<Tv> {
return $this->cs->clear();
}
public function getIterator(): \KeyedIterator<mixed, Tv> {
return $this->cs->getIterator();
}
public function items(): \Iterable<Tv> {
return $this->cs->items();
}
public function lazy(): \KeyedIterable<mixed, Tv> {
return $this->cs->lazy();
}
public function remove(Tv $value): AbsurdSet<Tv> {
return new AbsurdSet($this->cs->remove($value));
}
public function toArray(): array<Tv, Tv> {
return $this->cs->toArray();
}
public function toImmMap(): \ImmMap<mixed, Tv> {
return $this->cs->toImmMap();
}
public function toImmSet(): \ImmSet<Tv> {
return $this->cs->toImmSet();
}
public function toImmVector(): \ImmVector<Tv> {
return $this->cs->toImmVector();
}
public function toMap(): \Map<mixed, Tv> {
return $this->cs->toMap();
}
public function toSet(): \Set<Tv> {
return $this->cs->toSet();
}
public function toVector(): \Vector<Tv> {
return $this->cs->toVector();
}
public function toKeysArray(): array<Tv> {
return $this->cs->toKeysArray();
}
public function toValuesArray(): array<Tv> {
return $this->cs->toValuesArray();
}
}
$aset = new AbsurdSet(array(2, 3));
var_dump($aset);
Output
object(Hack\UserDocumentation\Collections\Examples\Examples\SimpleColl\AbsurdSet)#1 (1) {
["cs":"Hack\UserDocumentation\Collections\Examples\Examples\SimpleColl\AbsurdSet":private]=>
object(HH\Set)#2 (4) {
int(2)
int(3)
int(4)
int(5)
}
}