はじめに
PHPとRubyにはおおまかに以下のような用語の対応関係があります。
PHP | Ruby |
---|---|
関数 | メソッド |
メソッド | |
数字添字配列 | 配列 |
連想配列 | ハッシュ |
stdClass | |
無名関数 | ブロック |
proc | |
lambda |
実装
それぞれの言語で、以下の関数・メソッドを書いてみることにします。用語はRuby側に統一します。
- ハッシュの各要素の 値 に対してブロックを適用し、それらの返り値を集めた新しいハッシュを返す
- ハッシュの各要素の キー に対してブロックを適用し、それらの返り値を集めた新しいハッシュを返す
- ハッシュの各要素の 値とキー に対してブロックを適用し、それらの返り値を集めた新しいハッシュを返す
なお、Rubyは初心者です。
PHP
コード
<?php
function array_kmap(callable $func, array $arr) {
return array_combine(array_map($func, array_keys($arr)), $arr);
}
function array_kvmap(callable $func, array $arr) {
$new = array_map($func, array_keys($arr), $arr);
return $new ? call_user_func_array('array_merge', $new) : $new;
}
$arr = [
'k1' => 10,
'k2' => 15,
'k3' => 20,
];
print_r(array_map(function ($v) { return $v / 100.0; }, $arr));
print_r(array_kmap(function ($k) { return $k . 'a'; }, $arr));
print_r(array_kvmap(function ($k, $v) { return [$k . 'a' => $v / 100.0]; }, $arr));
結果
Array
(
[k1] => 0.1
[k2] => 0.15
[k3] => 0.2
)
Array
(
[k1a] => 10
[k2a] => 15
[k3a] => 20
)
Array
(
[k1a] => 0.1
[k2a] => 0.15
[k3a] => 0.2
)
array_merge
を引数なしでコールするとエラーが発生するために、空配列のチェックが必要になってしまうところが惜しいです。
Ruby
コード
class Hash
def vmap
map{|k, v| [k, (yield v)]}.to_h
end
def kmap
map{|k, v| [(yield k), v]}.to_h
end
def kvmap
map{|k, v| yield k, v}.to_h
end
end
h = {
'k1' => 10,
'k2' => 15,
'k3' => 20,
}
p h.vmap{|v| v / 100.0}
p h.kmap{|k| k + 'a'}
p h.kvmap{|k, v| [k + 'a', v / 100.0]}
結果
{"k1"=>0.1, "k2"=>0.15, "k3"=>0.2}
{"k1a"=>10, "k2a"=>15, "k3a"=>20}
{"k1a"=>0.1, "k2a"=>0.15, "k3a"=>0.2}
やはりRubyは美しいですね。コードが短い。標準クラスをすんなり拡張出来ちゃうあたりも優秀。
なお、Ruby2.0以前だと array.to_h
が使えないので Hash[array]
で代用します。ぶっちゃけ kvmap
に関しては to_h
生やすだけなのでメソッド要らないかも…
あとがき
キーと値を両方使うとき、返り値を [k, v]
とするか {k => v}
とするかが共通の問題になりそうです。
- Rubyは言語実装が
[k, v]
型を想定しているのでこちらの方が優位です。
(編集前は{k => v}
型だったのでinject
メソッド使ってました…) - PHPは
[k, v]
型で実装すると…ちょっと無理矢理感出てしまいます。
あくまで関数型コーディングだ!(但しPHP5.5以降に限る)
function array_kvmap(callable $func, array $arr) {
$new = array_map($func, array_keys($arr), $arr);
return array_combine(array_column($new, 0), array_column($new, 1));
}
手続き型で妥協しました(でも多分一番速い)
function array_kvmap(callable $func, array $arr) {
$new = [];
foreach ($arr as $k => $v) {
list($k, $v) = $func($k, $v);
$new[$k] = $v;
}
return $new;
}
コメント欄にてPythonのコードも投げつけてくれると多分喜びます。