前書き
PHPではxxxxx($yyy)
のような式が函数呼び出しである… とは限らない。
ほかの言語でも if($yyy) zzzzz();
のような構文があるが、PHPには「あたかも函数のように擬態した、函数ではないもの」が際だって多いように感じる。
その最たるものがlist()
であり、初めて見たときには筆者も目を疑った。
<?php
list($x, $y) = array(1, 2);
list
とarray
のどちらも、函数呼び出しではない。
PHP 7.1以降では
まったく同じ意味を短く書けます。
<?php
[$x, $y] = [1, 2];
PHPの配列(array)
PHPの配列は、その名前に反して、実に多様な機能を併せ持ったデータ型です。
PHP の配列は、実際には順番付けられたマップです。マップは型の一種で、 値をキーに関連付けます。 この型は、さまざまな使い道にあわせて最適化されます。 配列としてだけでなく、リスト (ベクター)、 ハッシュテーブル (マップの実装の一つ)、辞書、コレクション、スタック、 キュー等として使用することが可能です。 PHP の配列には他の PHP 配列を値として保持することができるため、 非常に簡単にツリー構造を表現することが可能です。
php.net, PHP: 配列 - Manual, 2014年8月12日閲覧. 引用は@tadsanによる.
以上のように、数多くのデータ構造の機能を兼ねて持ちます。大クラス主義を標榜するRubyのArrayよりも、さらに大ざっぱです。
これだけの機能を持つ配列さんですが、さらにもうひとつ、タプルの役割も持って居ます。
多値
PHPやRubyを含めたいくつものプログラミング言語では、函数には引数としていくつもの値を渡すことができますが、返り値は常にひとつ です。
一方で、函数からひとつだけでなく、複数の値を返したい需要があります。また、任意の個数の引数を受けるのに返り値がひとつしかないのは「非対称」であるようにも感じられます。
函数がいくつもの値を返すことを、いくつかのプログラミング言語では 多値(Multiple Values) と呼びます(多値論理;Many-valued logicは別物)。多値を実現するプログラミング言語にはGoやLua, Scheme, Common Lispなどがあります。
タプル
タプル(tuple)または組とは、 いくつのも値を組み合せた値 のことです。これを使って多値を実現するプログラミング言語もいくつもあります。
例として、「ジュースの名前を渡すとメーカー名と値段と在庫数が返ってくる函数」があったとします。このようなときに函数を返り値をどのようなデータとして表現するかにはいくつかの方法がありますが、ひとつの方法はタプルを返すことです。
(以下のコードはPythonで書いたサンプルコードですが、わざと回りくどく書いてます)
def vendor(drink_name):
"""自動販売機"""
#
# なんかいろいろ処理がある
#
val = (maker, price, stock_quantity)
return val
drink_tup = vendor("十六茶")
print(drink_tup)
# ('シャンソン化粧品', 150, 24)
maker = drink_tup[0] #=> シャンソン化粧品
price = drink_tup[1] #=> 150
stock_quantity = drink_tup[2] #=> 24
このプログラムで大切なのは、「0番めはメーカー名」「1番めは値段(円)」「2番めは在庫数」と、 タプルの中の位置には意味がある といふことです。
PHPやCなどのプログラミング言語にも、タプルに近い概念があります。それは 引数リスト です。
<?php
function trapezoid($height, $top, $bottom)
{
return (($top + $bottom) / 2) * $height;
}
$top = 10;
$bottom = 15;
$height = 6;
echo trapezoid($height, $top, $bottom);
このようなプログラムを利用するとき、引数の順番は非常に重要です。うっかりと echo trapezoid($top, $bottom, $height);
と書いてしまったら、間違った結果が表示されることになります。これがタプルの考へかたです。
備考
タプルの実装は、言語によって大きく異なります。Pythonの
tuple
型は、一見して単なる変更不可能な配列のように見えます。また、OCamlやF#のタプルは特定の型ではなく、(int * int * int)
のように、含まれる値(要素)の集まりとして個別の型がつきます。(この記事の趣旨ではないので省きますが、("apple", 30)
と("apple", "30")
は別の型です)
PHPでの多値
さきほどのPythonの自動販売機コードをPHPバージョンに直してみます。PHPにおいてもarray
を使って複数の返り値を表現するパターンに知られて居ります(PHP: 返り値 - Manual)。
<?php
/**
* 自動販売機
*/
function vendor($drink_name)
{
//
// なんかいろいろ処理がある
//
$val = array($maker, $price, $stock_quantity);
return $val;
}
$drink_tup = vendor("十六茶")
var_export(drink_tup);
// array (
// 0 => 'シャンソン化粧品',
// 1 => 150,
// 2 => 15,
// )
$maker = $drink_tup[0]; //=> シャンソン化粧品
$price = $drink_tup[1]; //=> 150
$stock_quantity = $drink_tup[2]; //=> 24
もう説明がめんどくさくなったのでサンプルコードだけ出して一気に片付ける。あとはわかるな?
<?php
/**
* 自動販売機
*/
function vendor($drink_name)
{
//
// なんかいろいろ処理がある
//
return array($maker, $price, $stock_quantity);
}
list($maker, $price, $stock_quantity) = vendor("十六茶");
echo $maker; //=> シャンソン化粧品
echo $price; //=> 150
echo $stock_quantity; //=> 24
まとめ
- 実際、PHP: 返り値 - Manualの内容を、周辺について説明しつつ、迂遠に書いただけだ
- PHPの
list()
を初めて見たときはPHPの歪んだ構文設計の象徴のように感じたが、今は愛しい - この記事はPHPを使って説明したが、Rubyも大クラス主義によりタプルは存在しないので、同様に配列を利用する
- この記事はPHPを使って説明したが、Pythonの
tuple
について「イミュータブルな配列」のような認識をしてたら改めてほしい - サンプルコードのPythonのコードは、わざと冗長に書いた
- OCaml, F#のような言語はタプルをスマートに分解する言語構造がいくつもある
- これらの言語の函数は「引数と返り値は必ずひとつづつ」とすることで対称的である
別案
各位知っての通り、このようなパターンも存在し、list
を使ったパターンとどちらが適するかは、実際のところケースバイケースである。読者各位の懸命な判断に任せる。
<?php
/**
* 自動販売機
*/
function vendor($drink_name)
{
//
// なんかいろいろ処理がある
//
return array(
'maker' => $maker,
'price' => $price,
'stock_quantity' => $stock_quantity
);
}
$drink = vendor("十六茶");
echo $drink['maker']; //=> シャンソン化粧品
echo $drink['price']; //=> 150
echo $drink['stock_quantity']; //=> 24
以上。
別案の2018年追記
PHP 7.1以降では連想配列も受け取ることができる。 ⇒ PHP7.1新機能: list()
におけるキーのサポート
[
'maker' => $maker,
'price' => $price,
'stock_quantity' => $stock_quantity,
] = vendor("十六茶");
echo $maker; //=> シャンソン化粧品
echo $price; //=> 150
echo $stock_quantity; //=> 24