Hack Arraysとは
HackにはPHPで利用される標準の配列に加えて、
vec、dict、keysetの三種類の配列が追加されています。
PHP配列や、HackのCollectionの両方を置き換えることができ、
かつPHPの配列とほぼ同様に利用できるため、手軽に利用できます。
Collectionはオブジェクトでしたが、このvec、dict、keysetは値扱いになっているため、
それ自体に関数などは用意されていません。
これらを操作する場合は、Collection同様にPHPと同じ配列操作の関数を利用します。
isset、unsetは概要編でも触れた様に利用できません
vec
純粋配列を扱う配列です。
Vector、ImmVectorを置き換えることができるもので、
foreachなどで利用できます。
<?hh
$v = vec[];
$vi = vec[1, 2, 3];
$vs = vec['a', 'b', 'c'];
出力は次の通り
vec(0) {
}
vec(3) {
int(1)
int(2)
int(3)
}
vec(3) {
string(1) "a"
string(1) "b"
string(1) "c"
}
PHPに慣れている方は違和感があるかもしれませんね。
vecかどうかを調べる場合は以下の方法があります。
<?hh
$v = vec[];
// PHPでもおなじみのis_xx
\var_dump(is_vec($v));
// Hackのみで利用できるis演算子
\var_dump($v is vec<_>);
dict
keyとvalueで構成される配列です。
Map、ImmMapを置き換えることができるもので、
これもforeachなどで利用できます。
配列キーはintかstringにしなければなりません。
<?hh
$d = dict[];
$di = dict[1 => 'a', 2 => 'b', 3 => 'c'];
出力は次の通り
dict(0) {
}
dict(3) {
[1]=>
string(1) "a"
[2]=>
string(1) "b"
[3]=>
string(1) "c"
}
dictかどうかを調べる場合は以下の方法に。
<?hh
$d = dict[];
// PHPでもおなじみのis_xx
\var_dump(is_dict($d));
// Hackのみで利用できるis演算子
\var_dump($d is dict<_, _>);
vecとそこまで大きく変わりません。
keyset
keysetはvecと同様ですが、重複しない値を持つ純粋配列になります。
Set、ImmSetを置き換えることができます。
配列キーはintかstringにしなければなりません。
<?hh
$ks = keyset[];
$ksi = keyset[2, 'a', 3, 1, 3];
入力された値をそのままの順番で、且つ重複を排除して保持しますので、
出力は次の通りです。
keyset(0) {
}
keyset(4) {
int(2)
string(1) "a"
int(3)
int(1)
}
vec, dictと同様にkeysetかどうかを調べる場合は以下の通り
<?hh
$ks = keyset[];
var_dump(is_keyset($ks), $ks is keyset<_>);
Hack Arrays hhi
このHackArrayの定義は、以下の通りに記述されています。
abstract final class dict<+Tk as arraykey, +Tv> implements Indexish<Tk, Tv>, XHPChild {}
abstract final class keyset<+T as arraykey> implements Indexish<T, T>, XHPChild {}
abstract final class vec<+T> implements Indexish<int, T>, XHPChild {}
arraykeyはこれもHack独自の型で、
用途としては、 arraykey is special union type of int and string.
となります。
つまりstringとintを利用することができるよ、というものです。
CollectionとHack Arrays
それぞれ置き換え可能ではありますが、
残念ながら Vector === vec
、Map === dict
といった比較等はできません。
比較はできませんが、CollectionからHack Arraysに変換することができます。
マニュアルに記載されている通りで、簡単です
<?hh // strict
<<__Entrypoint>>
function main(): void {
\var_dump(vec(Vector { 1, 2, 3, 1 }));
\var_dump(dict(Map {'a' => \ord('a'), 'b' => \ord('b'), 'c' => \ord('c') }));
\var_dump(keyset(Vector {1, 2, 3, 1 }));
\var_dump(keyset(Set {1, 2, 3, 1 }));
}
// hhvm ./hackarray.php
<<__Entrypoint>>
はAttribute(PHP等でいうアノテーションです)で、
hackのファイル実行時にこのAttributeが記載されている関数が実行されます。
goなどでもおなじみの func main
などと同じものです。楽チン
もちろんdictやvecからMap、Vectorに変換することもできます。
<?hh // strict
<<__Entrypoint>>
function main(): void {
\var_dump(new Vector(vec[1, 2, 3, 1]));
\var_dump(new ImmMap(dict['a' => \ord('a'), 'b' => \ord('b'), 'c' => \ord('c')]));
\var_dump(new Set(keyset[1, 2, 3, 1]));
}
通常のPHP配列も変換できます。
<?hh // strict
<<__Entrypoint>>
function main(): void {
$phpArrays = [1,2,3,'PHP Array'];
\var_dump(vec($phpArrays));
\var_dump(dict($phpArrays));
\var_dump(keyset($phpArrays));
}
PHPの配列に、オブジェクトが混在している場合は、keysetでTypeErrorとなります。
<?hh // strict
<<__Entrypoint>>
function main(): void {
$phpArrays = [1,2,3,'PHP Array', new stdClass()];
\var_dump(keyset($phpArrays));
}
上記の様にした場合は、
It is incompatible with an object of type stdClass
となります。
Collection同様にTypecheckerで見つけることができますので、
実際の開発でも利用することが多いものの一つです。
hsl / Hack Standard Library
hhvm/hsl
vecやdict、keysetを操作するには、関数を利用することになりますが、
一般的に利用されるものはこのライブラリで用意されています。
via Composer
ライブラリは基本的にComposerを経由してインストールしますが、
Hackで利用する場合は、
[2018] HHVM/Hackの始め方 導入編 で紹介している様に、
hhvm-autoload をインストールしなければなりません。
適当なプロジェクトを作成したら、composer.jsonを作成してライブラリのインストールをしていきます。
まずはcomposer.jsonを作ります。これはcomposerのコマンドで作成できます
$ composer ini
適当に入力しましょう。
次にhhvmを指定して hhvm/hhvm-autoload
をインストールします。
PHP環境では解決できない機能が含まれているためインストールできません。
$ hhvm $(which composer) require hhvm/hhvm-autoload
まっさらなプロジェクトにインストールすると、 hh_autoload.json
を作成してくれます。
次にやっとhslのインストールです。
$ hhvm $(which composer) require hhvm/hsl
*残念ながら古いバージョンのHHVM環境のままの方はインストールできませんので、
最新のLTSバージョン以上のバージョンにアップデートしましょう。
Hackを利用するため、 .hhconfig
ファイルを作成します。
プロジェクトルートに設置すればOKですので、
composer.jsonやhh_autoload.jsonと同じディレクトリに設置します。
記述内容は以下の通りです
assume_php = false
ignored_paths = [ "vendor/.+/tests/.+" ]
safe_array = true
safe_vector_array = true
unsafe_rx = false
最後に適当なphpファイルを作成して(hhファイルでもどちらでも良いです)、
hslを利用できる様にvendor/hh_autoload.phpをrequireします。
*vendor/autoload.phpではありません
次の内容そのままでいいでしょう。
<?hh // strict
require __DIR__ .'/vendor/hh_autoload.php';
<<__Entrypoint>>
function main(): void {
}
これでHackのコードを書く準備ができました。
PhpStormなどでは補完を利用することができないため、
忘れずにVisual Studio Code(+ Hack for Visual Studio Code)かNuclideを準備しましょう。
これでコードの補完も安心です。
hslすべての関数の利用方法を解説するのは大変なので、
ほんの一握りだけ紹介します。
HH\Lib\DictやHH\Lib\Vecといった名前空間に属する関数は、
名前空間に合わせたHack Arrayが返却されます。
例としてここでは適当なdictを生成するものを使います。
function hh_random_dict(): dict<int, int> {
$d = dict(range(11,20));
shuffle(&$d);
return $d;
}
Dict\filter
コールバック関数を使用して、Hack Arrayのvalueでフィルタリングする関数です
use namespace HH\Lib\Dict;
require __DIR__ .'/vendor/hh_autoload.php';
<<__Entrypoint>>
function main(): void {
var_dump(Dict\filter(hh_random_dict(), ($v) ==> $v >= 15));
}
asyncで利用する場合は、以下の様になります
var_dump(\HH\Asio\join(
Dict\filter_async(hh_random_dict(), async ($v) ==> ($v >= 15))
));
Dict\filter_with_key
コールバック関数を使用して、Hack Arrayのkeyでフィルタリングする関数です
use namespace HH\Lib\Dict;
require __DIR__ .'/vendor/hh_autoload.php';
<<__Entrypoint>>
function main(): void {
var_dump(Dict\filter_with_key(hh_random_dict(), ($k, $_) ==> $k >= 2));
}
Dict\from_entries
tupleで構成された配列からdict配列を生成する関数です。
<?hh
var_dump(Dict\from_entries(vec[
tuple(1, 'q'),
tuple(2, 'w'),
tuple(3, 'e'),
tuple(4, 'r'),
]));
Dict\merge
dictやvecなどを一つのdictにマージする関数です。
<?hh // strict
use namespace HH\Lib\{Vec,Dict,C};
require __DIR__ .'/vendor/hh_autoload.php';
<<__Entrypoint>>
function main(): void {
var_dump(Dict\merge(dict[1 => 2], dict[2 => 3], vec['testing']));
}
Dict\map
指定したHack配列の要素にコールバック関数を適用する関数です
<?hh // strict
use namespace HH\Lib\{Vec,Dict,C};
require __DIR__ .'/vendor/hh_autoload.php';
<<__Entrypoint>>
function main(): void {
var_dump(Dict\map(hh_random_dict(), ($v) ==> $v * 100));
}
vecやkeysetなどに合わせて関数も多く用意されています。
ほんの一部のみ紹介しましたが、どれもPHPの配列操作関数と大きな違いはありません。
(記法の違い程度です lambdaなど)
hslには文字列操作の関数なども含まれていますので、
開発時に利用してみましょう。