Help us understand the problem. What is going on with this article?

Hackにおける配列とCollection - Hack Arrays / hsl 編

More than 1 year has passed since last update.

Hack Arraysとは

HackにはPHPで利用される標準の配列に加えて、
vec、dict、keysetの三種類の配列が追加されています。

PHP配列や、HackのCollectionの両方を置き換えることができ、
かつPHPの配列とほぼ同様に利用できるため、手軽に利用できます。

Collectionはオブジェクトでしたが、このvec、dict、keysetは値扱いになっているため、
それ自体に関数などは用意されていません。
これらを操作する場合は、Collection同様にPHPと同じ配列操作の関数を利用します。
isset、unsetは概要編でも触れた様に利用できません

vec

純粋配列を扱う配列です。
Vector、ImmVectorを置き換えることができるもので、
foreachなどで利用できます。

hackarray.php
<?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かどうかを調べる場合は以下の方法があります。

hackarray.php
<?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にしなければなりません。

hackarray.php
<?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かどうかを調べる場合は以下の方法に。

hackarray.php
<?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にしなければなりません。

hackarray.php
<?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かどうかを調べる場合は以下の通り

hackarray.php
<?hh
$ks = keyset[];
var_dump(is_keyset($ks), $ks is keyset<_>);

Hack Arrays hhi

このHackArrayの定義は、以下の通りに記述されています。

hhi
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 === vecMap === dictといった比較等はできません。
比較はできませんが、CollectionからHack Arraysに変換することができます。

マニュアルに記載されている通りで、簡単です

hackarray.php
<?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配列も変換できます。

hackarray.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となります。

hackarray.php
<?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と同じディレクトリに設置します。
記述内容は以下の通りです

.hhconfig
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ではありません

次の内容そのままでいいでしょう。

sample.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には文字列操作の関数なども含まれていますので、
開発時に利用してみましょう。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away