insert、insertWith、insertWithKey、insertLookupWithKey (Data.Map)

データの挿入について。insertWith の第1引数である関数は既存の値と新しい値の2つの引数をとり、insertWithKey および insertLookupWithKey の関数はキー、新しい値、古い値の3つの引数をとる。insertWithKey と insertLookupWithKey の共通点は前者の戻り値と後者の戻り値のタプルの2番目の要素は連想リストであることで違いは後者の戻り値のタプルの1番目の要素が Maybe 型であること。

> let a = fromList [(1,3),(2,5)]
> insert 2 7 a
fromList [(1,3),(2,7)]
> insert 3 7 a
fromList [(1,3),(2,5),(3,7)]
> insertWith (+) 2 7 a
fromList [(1,3),(2,12)]
> insertWith (+) 3 7 a
fromList [(1,3),(2,5),(3,7)]
> let f key new_value old_value = key + new_value + old_value
> insertWithKey f 3 7 afromList [(1,3),(2,5),(3,7)]
> insertWithKey f 2 7 a
fromList [(1,3),(2,14)]
> insertLookupWithKey f 2 7 a
(Just 5,fromList [(1,3),(2,14)])
> insertLookupWithKey f 3 7 a
(Nothing,fromList [(1,3),(2,5),(3,7)])

null、member、notMember、lookup、findWithDefault

member、notMember、lookup、findWithDefault はすべて要素の検索を行うことが共通で戻り値は member、notMember が Bool 型、lookup は Maybe 型、findWithDefault は指定した値が存在しなければ第1引数の値を返す。

> Data.Map.null(empty)
True
> Data.Map.null(singleton 1 'a')
False
> let a = fromList [(3,'a'),(5, 'b')]
> member 3 a
True
> member 7 a
False
> notMember 3 a
False
> notMember 7 a
True
> Data.Map.lookup 3 a
Just 'a'
> Data.Map.lookup 7 a
Nothing
> findWithDefault 'c' 3 a
'a'
> findWithDefault 'c' 7 a
'c'

empty、singleton (Data.Map)

Data.List はだいぶやりつくしたので、Data.Map の写経に写る。

> size empty
0
> empty
fromList []
> size empty
0
> singleton 1 'a'
fromList [(1,'a')]
> size $ singleton 1 'a'
1

array_map のコールバック関数がキーを受け付けるようにする

少し前に array_map のキー対応の記事を書いたが、コールバック関数を受け付けられるようにしていなかったことに気づいたので、新しく記事を書くことにした。array_map は複数の配列を引数にとれるような仕様になっているが、この機能は使う機会はほとんどないので、受け取る配列は1つにした。そうするとコールバックと配列の順番を array_filter と合わせてもよいかもしれないが、既存の array_map との整合性を優先した。

function array_map_with_key(callable $callback, array $array) {
    $ret = [];
    foreach ($array as $key => $value) {
        $ret[] = $callback($key, $value);
    }
    return $ret;
}

function array_map_with_key2(callable $callback, array $array) {
    return array_map(
        function($key, $value) use($callback) {
            return $callback($key, $value);
        },
        array_keys($array),
        $array
    );
}

$array = range(0, 10, 2);
$callback = function($key, $value) {
    return pow($key, 2) + 1;
};

var_dump(
    array_map_with_key($callback, $array),
    array_map_with_key2($callback, $array)
);

array_fill にコールバック関数を受け付ける機能を追加する

コールバック関数はキーを引数にとれるようにした。

function array_fill_callback($start_index, $num, callable $callback) {
    $ret = array_fill($start_index, $num, '');
    foreach ($ret as $key => &$value) {
        $value = $callback($key);
    }
    return $ret;
}

$callback = function($key) { return pow($key, 2) + 1; } ;

var_dump(array_fill_callback(8, 3, $callback));

range にスタートインデックス指定を追加する

array_fill と range 関数を見比べると、range 関数のスタートインデックスを 0 だけでなく任意の値に指定できないかと思ったので書いてみた。

function range2($start_index, $start, $limit, $step = 1) {
    $count = floor(($limit - $start) / $step);
    $keys = range($start_index, $start_index + $count);
    return array_combine($keys, range($start, $limit, $step));
}

var_dump(range2(5, 8, 30, 3));

range の定義

数値だけを考える。

function range2($start, $limit, $step = 1) {
    $count = floor(($limit - $start) / $step);
    $ret = [];

    for ($i = 0; $i <= $count; ++$i) {
        $ret[$i] = $start + $step * $i;
    }
    return $ret;
}

var_dump(range2(8, 30, 3));

array_fill を for、foreach、array_map、array_reduce でユーザー定義する

array_map をユーザー定義する状況と似ている。foreach、array_map の場合、初期値の生成に array_flip と range 関数を使った。

function array_fill2($start_index, $num, $value) {
    $ret = [];

    for ($i=$start_index; $i < $start_index + $num; ++$i) {
        $ret[$i] = $value;
    }

    return $ret;
}

function array_fill3($start_index, $num, $value) {
    $ret = array_flip(range($start_index, $start_index + $num - 1));
    
    foreach ($ret as &$r) {
        $r = $value;
    }

    return $ret;
}

function array_fill4($start_index, $num, $value) {
    return array_map(
        function() use($value) { return $value; }, 
        array_flip(range($start_index, $start_index + $num - 1))
    );
}

function array_fill5($start_index, $num, $value) {
    return array_reduce(
        array_flip(range($start_index, $start_index + $num - 1)),
        function($result, $item) use($value) { 
            $result[] = $value; return $result;
        }, 
    '');
}

var_dump(
    array_fill2(8, 10, 9),
    array_fill3(8, 10, 9),
    array_fill4(8, 10, 9),
    array_fill5(8, 10, 9)
);

null はスカラーではない

is_scalar のコメントで見つけた情報。empty 関数で空と見なされる値に is_scalar を適用すると結果は次のようになった。

var_dump(
    array_map(
        function($value) { return is_scalar($value);}, 
        ['',  // true 
         0,  // true
         0.0, // true
         '0', // true
         null, // false
         false, // true
         [] // false
         ]
));

array_uintersect_assoc でバリューをフィルタリングする

array_filter の代わりに array_uinterect_assoc を使ってバリューをフィルタリングすることができる。array_uintersect も試してみたが、こちらはうまくゆかなかった。

function array_filter2(array $array, callable $callback) {
    return array_uintersect_assoc($array, $array,
        function($value1, $value2) use($callback) { 
            return $callback($value1) ? 0 : 1;
        });
}

$array = [0 => 2, 1 => 5, 2 => 15, 3 => 22, 4 => 34];
$callback = function($value) {
    return (0 === $value % 2) ? true : false;
};

var_dump(
    array_filter($array, $callback),
    array_filter2($array, $callback)
);