以下是有關(guān)如何使用Hack集合的各種示例。
這個(gè)簡單的例子顯示了如何創(chuàng)建,查詢,添加項(xiàng)目和從中刪除項(xiàng)目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"
}
這個(gè)簡單的例子顯示了如何創(chuàng)建,查詢,添加項(xiàng)目和從中刪除項(xiàng)目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() 允許您通過在集合的每個(gè)項(xiàng)目上應(yīng)用條件來創(chuàng)建當(dāng)前集合的子集(包括為空)的集合,如果該元素的條件為真,則它將成為新集合的一部分。
<?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)
}
請(qǐng)注意,我們使用lambda來創(chuàng)建過濾操作。我們也可以使用正常的關(guān)閉。
map()允許您創(chuàng)建一個(gè)與map()調(diào)用的集合具有相同元素的集合,但是對(duì)每個(gè)元素應(yīng)用了一些操作。
<?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"
}
請(qǐng)注意,我們正在使用lambda來創(chuàng)建映射操作。我們也可以使用正常的關(guān)閉。
Hack集合具有引用語義,而數(shù)組具有值語義。
<?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)
}
Hack集合使用該方法提供了Scala類視圖lazy()。通常,當(dāng)您創(chuàng)建集合的實(shí)例時(shí),您將創(chuàng)建一個(gè)嚴(yán)格版本的集合。因此,當(dāng)您創(chuàng)建一個(gè)包含一百萬個(gè)元素的集合時(shí),會(huì)立即為所有這些元素分配內(nèi)存。然而,在一個(gè)視圖通過lazy(),則可以創(chuàng)建一個(gè)非嚴(yán)格集合,使得當(dāng)您使用的方法類似map()或filter()在收集(即,變壓器方法),所述元件僅被訪問時(shí)進(jìn)行計(jì)算。
此示例顯示了如何lazy()在一個(gè)相當(dāng)大的集合上使用,以及對(duì)集合的嚴(yán)格和非嚴(yán)格版本的時(shí)間。由于我們最終只需要5個(gè)元素,所以懶惰的視圖實(shí)際上允許我們?cè)跐M足我們所需的5之后停止,而無需實(shí)際分配所有1000000個(gè)元素。
<?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是一個(gè)表示可以由密鑰建立索引的集合的接口。集合中的鍵可以是string或int。因此,您可以用于Indexish表示具有這兩種關(guān)鍵類型之一的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"
}
當(dāng)前的具體收集類被標(biāo)記為final。也就是說,它們不能被直接擴(kuò)展和分類。但是,您可以使用提供的接口創(chuàng)建新的集合類。這個(gè)例子將不會(huì)創(chuàng)建一個(gè)完整的新集合,因?yàn)檫@樣很多的方法必須被實(shí)現(xiàn)Iterable<T> 比這里介紹的更多的審查和細(xì)節(jié)。
但是,這個(gè)例子確實(shí)顯示了如果你想創(chuàng)建一個(gè)新的集合集必須實(shí)現(xiàn)的所有方法。
<?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)
}
}
更多建議: