LoginSignup
1
0

More than 3 years have passed since last update.

【小ネタ】引数のコンマ区切りを卒業し、関数呼び出しの可読性を高めよう

Last updated at Posted at 2020-02-27

別にphpに限った話ではないけど。
eval関数の使える言語なら大抵似たようなことができると思う。
evalが使えなくても文字列演算をすれば代用可能。

1.執筆の動機

例えばPostgreSQLでは、文字列から部分文字列を取り出す関数として、
substring('string' from 2 for 3);
みたいのがある。このコードでは1オリジン2文字目から3文字だけ取り出すから、
結果は'tri'となる。

引数がコンマ,ではなく、fromとかforで区切られている。
そのおかげで、
substring('string', 2, 3);
のようなコードと比べ、可読性が向上しているとは言えないだろうか。

是非このような仕組みをほかの言語でも可能にしたいと思ったのが、本稿執筆のきっかけ。

2.実現方法

結論から言うと、引数全体を一つの文字列とし、それを連想配列に変換しちゃえばいい。

例: 'st r_ing'の2文字目から5文字取り出したい場合

まず、次のような関数で文字列を連想配列に変換する。

php
function arg2assoc(string $str)
{
    $str = str_replace("__", "_", preg_replace("/([^_]|\A)_([^_]|\z)/", "$1 $2", rtrim(preg_replace("/[ \t\r\n]*([^ \t\r\n]+)[ \t\r\n]*([^ \t\r\n]*)[ \t\r\n]*/", "'$1'=>$2,", $str), ",")));
    return eval("return [$str];");
}

以上のようにarg2assoc関数を書いたら、
var_dump(arg2assoc("str 'st_r__ing' from 2 for 5"));
を実行すると次のようになる。

array(3) {
  ["str"]=>
  string(8) "st r_ing"
  ["from"]=>
  int(2)
  ["for"]=>
  int(5)
}

後は、この連想配列を関数内部で利用すればよい。
例えば
substring('st r_ing', 2, 5);
substr("'st_r__ing' from 2 for 5");
のように書けるようにしたい場合は、次のようにすればよい。

php

function substr(string $argstr)
{
    $assoc = arg2assoc("str ".$argstr);
    return substring($assoc['str'], $assoc['from'], $assoc['for']);
}

3.仕様説明

arg2assoc関数をもう少し丁寧に書くと次のようになる。

php
function arg2assoc(string $str)
{
    //"str 'st_r__ing' from 2 for 5"
    //(ホワイトスペースを引数に含める場合、「_」で置き換える)
    //(「_」を含めたい場合は「__」と2個連続で。)

    $str = preg_replace("/[ \t\r\n]*([^ \t\r\n]+)[ \t\r\n]*([^ \t\r\n]*)[ \t\r\n]*/", "'$1'=>$2,", $str);
    //"'str'=>'st_r__ing','from'=>2,'for'=>5,"

    $str = rtrim($str, ",");
    //"'str'=>'st_r__ing','from'=>2,'for'=>5"

    $str = preg_replace("/([^_]|\A)_([^_]|\z)/", "$1 $2", $str);
    $str = str_replace("__", "_", $str);
    //"'str'=>'st r_ing','from'=>2,'for'=>5"

    return eval("return [$str];");
}

'st r_ing''st_r__ing'に置き換えて渡すことの必要性について説明する。
正規表現では原理的にカッコ言語を受理できないため、
例え引用符でくくってあったとしても、配列の区切りの空白と、文字列としての空白の区別をつけることができないのである。
そこで、文字列としての空白は_で置き換えて渡す仕様とした。
また、この仕様により、空白の置き換えとしての_と、文字列としての_の区別がつかない問題が新たに発生した。
そこで文字列としての_は、自身を2連続した__で表現することとした。

1
0
7

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0