hack集合:介绍

2018-10-20 11:19 更新

除了完全支持一刀切的PHP array 容器类型,Hack允许将额外的打字放置在数组上,并提供了一些实现更专门的集合模式的类。

Array Typing

在Hack中的数组行为类似于Generics可以提供关于它们如何专门的附加类型信息。例如,一个无索引的字符串数组采用形式array<string>。类似地,对于索引数组,可以首先指定键类型(int或string),后跟值类型。因此,带有字符串键和浮点值的字典可能看起来像array<string, float>

由于数组的值侧本身可能是一个数组(或一个Generic类),因此类型专门化可以嵌套,array<int, array<string, string>>其中包含数字索引的数组包含字符串字典。

<?hh

namespace Hack\UserDocumentation\Collections\Intro\Examples\Arr;

// array<string, string> is an array map with string keys and string values
function array_as_map(array<string, string> $arr): string {
  $r = substr(str_shuffle('ABCDEF'), 0, 1); // random letter
  return array_key_exists($r, $arr) ? $arr[$r] : 'Z';
}

// array<int> is an array vector with integer keys and integer values
function array_as_vector(array<int> $arr): int {
  $r = rand(0, 10);
  return array_key_exists($r, $arr) ? $arr[$r] : PHP_INT_MAX;
}

function run(): void {
  $v = array(100, 200, 300, 400);
  $v[] = 500; // element 5, value 500
  var_dump($v);
  var_dump(array_as_vector($v));

  $m = array('A' => 'California', 'B' => 'Oregon', 'C' => 'North Carolina');
  $m['D'] = 'Florida';
  var_dump($m);
  var_dump(array_as_map($m));
}

run();

Output

array(5) {
  [0]=>
  int(100)
  [1]=>
  int(200)
  [2]=>
  int(300)
  [3]=>
  int(400)
  [4]=>
  int(500)
}
int(9223372036854775807)
array(4) {
  ["A"]=>
  string(10) "California"
  ["B"]=>
  string(6) "Oregon"
  ["C"]=>
  string(14) "North Carolina"
  ["D"]=>
  string(7) "Florida"
}
string(7) "Florida"

Hack集合

虽然PHP数组是非常通用的,但灵活性偶尔会在性能,正确性或可读性方面付出代价。Hack Collection类旨在通过以普通容器模式的形式提供深度专门的数组对象版本来解决这些问题:Vector, Map,和 Set

集合类型

hack有七个集合:

类型描述
Vector可变的,int被索引的,有序的值序列。值可以是任何类型。所述indicies在开始0和结束处n-1,在那里n是元件的数量。
ImmVector一个不变的版本Vector一旦ImmVector被创建,元件不能改变,删除或添加。
Map可变的string或者int索引的索引序列的值序列。值可以是任何类型。订单被记住。这与array使用中最相似
ImmMap一个不变的版本Map一旦ImmMap被创建,元件不能改变,删除或添加。
Set可变的,有序的唯一值集。值只能是intstring有在无钥匙Set
ImmSet一个不变的版本Set一旦ImmSet被创建,元件不能改变,删除或添加。
Pair完全两个值的不可变序列。钥匙是01它们与元组类似,但灵活性较差。

可读性

想象一下第三方功能声明如下:

function foo(array<int, string> $arr): void {}

这是array一个带有顺序整数键的向量风格数组吗?或者可能是map具有潜在非顺序整数键的风格数组?这个数组中的值是否唯一?

现在想象这个函数声明:

function bar(Vector<string> $vec): void {}

通过查看函数签名,我们知道这些整数键是有序的和顺序的。实际的关键数字可能与其持有的东西相关程度不大。

类似地,如果函数声明是:

function bar(Set<string> $set): void {}

然后我们意识到这些整数键是不相关的,并且包含在该参数中的字符串都将具有唯一的值。

性能

在明显的好处Vectors,并Sets有该按键可以由运行时被忽略。对于Vectors,这意味着连续排列值并基于索引进行快速查找和迭代。在Sets 的情况下,它实际上意味着(作为实现细节)将内部结构转换为内部,使用值作为键(必须是唯一的),并忽略阵列对的另一半。这是PHP代码中已经提供的一种模式,但它消除了必须记住阵列内部的认知开销,并且以比脚本代码能够更有效的方式进行操作。

以下是密钥收集类的操作的典型的摊销时间复杂度array

CLASSACCESSITERATIONINSERTREMOVAL
VectorO(1)O(n)O(n)O(n)
MapO(1)O(n)O(1)O(1)
SetO(1)O(n)O(1)O(1)
PairO(1)O(1)N/AN/A
arrayO(1)O(n)O(1)O(1)

类型检查

与可读性有关,Hack类型检查器不能指出,例如,您的代码的上下文是否将array使用的向量传递给array使用类似地图的参数的函数。例如,以下代码通过类型检查器。

<?hh

namespace Hack\UserDocumentation\Collections\Intro\Examples\ArrTypeCheck;

// This is a array used like a map, but it can be passed an array used like
// a vector.
function array_as_map(array<int, int> $arr): int {
  $r = rand(0, 10); // random letter
  return array_key_exists($r, $arr) ? $arr[$r] : PHP_INT_MAX;
}

function run(): void {
  $v = array(100, 200, 300, 400);
  $v[] = 500; // element 5, value 500
  var_dump($v);
  var_dump(array_as_map($v));
}

run();

Output

array(5) {
  [0]=>
  int(100)
  [1]=>
  int(200)
  [2]=>
  int(300)
  [3]=>
  int(400)
  [4]=>
  int(500)
}
int(9223372036854775807)

但是,如果你把代码相似的风格,而是使用VectorMap,则typechecker很容易分辨的意图。

<?hh

namespace Hack\UserDocumentation\Collections\Intro\Examples\MapTypeCheck;

// array<string, string> is an array map with string keys and string values
function array_as_map(Map<int, int> $arr): int {
  $r = rand(0, 10); // random letter
  return array_key_exists($r, $arr) ? $arr[$r] : -1;
}

function run(): void {
  $v = Vector { 100, 200, 300, 400 };
  $v[] = 500; // element 5, value 500
  var_dump($v);
  // The call to array_as_map will not typecheck because you are trying to pass
  // a Vector into a function expecting a Map. You will also get a runtime
  // error as well trying to do this.
  var_dump(array_as_map($v));
}

run();

Output

object(HH\Vector)#1 (5) {
  [0]=>
  int(100)
  [1]=>
  int(200)
  [2]=>
  int(300)
  [3]=>
  int(400)
  [4]=>
  int(500)
}
Catchable fatal error: Argument 1 passed to Hack\UserDocumentation\Collections\Intro\Examples\MapTypeCheck\array_as_map() must be an instance of HH\Map, HH\Vector given in /data/users/joelm/user-documentation/guides/hack/23-collections/01-introduction-examples/map-typecheck.php.type-errors on line 9

使用Hack集合可以让类型检查器使用更多的数据来尝试确定你的代码中是否有打字问题。

以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号