ニコニコ大百科のPHP問題を解いてみる

ニコニコ大百科の[PHP](https://dic.nicovideo.jp/a/php)の項目になんか問題があったので解いてみる。

いまいち文意がはっきりしない問題が多いので、そのへんは適当にどうにかしています。
あと問題文の英数字に半角全角が混ざってるのがとてもアレ。


# 初級編

## 問１：２つの変数、`$val1`と`$val2`を、if式を使わずに比較して、大きい値を`$larger`を格納する式を一行で書いてみてね。

```php
$larger = max($val1, $val2);
```

ヒントには三項演算子を使えとか書いてあるのだけど、`$larger = $val1 > $val2 ? $val1 : $val2;`とか書くより普通に標準関数使った方が楽ですね。


## 問２：改行コードを含む文字列に対し、改行コードに何が含まれているか調べるにはどうしたらよいか答えてね。

```php
echo strpos($text, "\r\n") !== false ? 'CRLF' : ( strpos($text, "\r") !== false ? 'CR' : (strpos($text, "\n") !== false ? 'LF' :'無い'));
```

なんか題が意図しているものと違う気がしてならない。


## 問３：変数`$obj`に、配列を使わずに`$name`,`＄age`,`$address`の三種類の値を入れて取り出せるようにしてね。

```php
$obj = new class('安部菜々', 17, 'ウサミン星') {
    public $name, $age, $address;
    public function __construct($name, $age, $address) {
        $this->name = $name;
        $this->age = $age;
        $this->address = $address;
    }
};

echo $obj->name; // 安部菜々
```

たしかに配列は使ってないけどほぼ同じだろ。これでいいのか？


# 中級編

## 問４：小数点２の桁を丸める定義関数、roundmath()を作ってね。ただし、正数、負数どちらにも対応できるようにね。

```php
function roundmath($num){
    return round($num, 1);
}

echo roundmath(12.35); // 12.4
```

> 正数、負数どちらにも対応できるようにね

とわざわざ太字で書いてあるんだけど、[round](https://www.php.net/manual/ja/function.round.php)は普通に負数を扱えるので何を意図した主張なのかよくわからず。


## 問５：任意の行数`$column`だけあるテーブル"HOGE"に対するSQLのInsert文で、一行でプレースホルダを埋め込んだ式を作成してみてね。なお、プレースホルダの記号は何を使ってもOKです。

え…これ難しくね？

```php
((new PDO($dsn, $user, $pass))->prepare('INSERT INTO HOGE (id, value) VALUES(:id, :value)'))->execute([':id' => $id, ':value' => $value]);
```

そんなでもなかった。
行数はINSERTには全く関係ないので、あえて記載している意味がわからない。


## 問６：`$desc_number = function(){ echo $number--;};` この無名関数に少しだけ手を加えて、関数`desc_number()`を実行するたびに、数を減らしていくようにしてね。なお、`＄number`には最初に100だけ代入されています。

```php
$desc_number = function () {
    static $number = 100;
    echo $number--;
};

$desc_number(); // 100
$desc_number(); // 99
```

なんかこれも出題者が考えていた答えとちがう気がする。


# 上級編

## 問７：文字連結演算子( . )を使わずに、文字列の中に定数や今日の日付を埋め込んでみてね。

ヒントを見るに題意は変数展開でしょう。

```php
    $d = date('Y年m月d日');
    $os = PHP_OS;
    echo "今日は${d}です。OSは${os}です。";
```

二重引用符では変数展開できますが、関数や定数は展開できないので1行では書けません。

まあ`.`を使わずに文字列結合すればいいだけなんですけどね。

```php
printf('今日は%1$sです。OSは%2$sです。',  date('Y年m月d日'), PHP_OS);
echo '今日は', date('Y年m月d日'), 'です。OSは', PHP_OS, 'です。';
```

ちなみに2行使っていいなら面白い書き方があります。

```php
$i = function ($v) { return $v; };
echo "今日は{$i(date('Y年m月d日'))}です。OSは{$i(PHP_OS)}です。";
```

参考：[PHPで文字列リテラルに式展開](https://qiita.com/tadsan/items/e4796449c736cfb5c9bd)



## 問８：同じページ内で定義関数`function hogehoge($val1)`と`function hogehoge($val2)`を呼び出せるよようにしてみてね。ただし、クラスのメンバに入れるのは禁止です。

ヒントから察するに名前空間で分けろってことなのだろうか？

```php
namespace A {
    function hogehoge($val1)
    {
        echo 'A:hogehoge' . $val1;
    }
}

namespace B {
    function hogehoge($val2)
    {
        echo 'B:hogehoge' . $val2;
    }
}

namespace {
    \A\hogehoge('A'); // A:hogehogeA
    \B\hogehoge('B'); // B:hogehogeB
}
```

でもこれ単に`\A\hogehoge`と`\B\hogehoge`を作ってるだけであって、`hogehoge`を二つ作っているわけではないよね。

つまり、これが正解。

```php
function hogehoge($val1) {
    echo 'hogehoge' . $val1;
}
hogehoge('A'); // hogehogeA

uopz_del_function('hogehoge');

function hogehoge($val2) {
    echo 'hogehoge' . $val2;
}
hogehoge('B'); // hogehogeB
```

うん、まあ絶対違うね。


## 問９：異なる要素数の2つの配列`$ary1`と`$ary2`を同時にループして、順番に値を取り出したい。foreachを使って取り出してみてね。ただし、NULLを出さないでね。

順番にってのはどういうことだろうか。
`$ary1`の1番目、`$ary2`の1番目、`$ary1`の2番目、`$ary2`の2番目、って順でいいんかな？
あと余りは後ろにまとめればいいのか？

```php
function array_mix(array $ary1, array $ary2){
    $multipleIterator = new MultipleIterator(MultipleIterator::MIT_NEED_ANY);
    $multipleIterator->attachIterator(new ArrayIterator($ary1), 1);
    $multipleIterator->attachIterator(new ArrayIterator($ary2), 2);
    foreach ($multipleIterator as $v1) {
        foreach ($v1 as $v2) {
            if ($v2 !== null) {
                yield $v2;
            }
        }
    }
}

foreach (array_mix([1, 2], [3, 4, 5, 6]) as $k => $v) {
    echo $v; // 132456
}
foreach (array_mix([1, 2, 3, 4], [5, 6]) as $k => $v) {
    echo $v; // 152634
}
```

むつかしかった。


## 問１０：一つのメソッドを使うだけで、全１０学級、国数英社理のテスト結果に対して、各生徒の偏差値と学年順位、学級順位を返すシステムを作ってね（横暴）。

1メソッド中にだばだば書いていけば特に問題なく実装できますね。
もちろんメンテコストなんてものは度外視だ。

ヒントはトレイトを使えとのこと。トレイト？？？

```php:動かしてないけどこんなかんじでいけるやろ
class Grades{

    public static function getRank(array $students){
        // 教科毎の母集団作成
        $grades = $ranks = [];
        foreach ($students as $class => $classStudents) {
            foreach ($classStudents as $number => $student) {
                foreach ($student as $grade => $score) {
                    $grades[$grade]['cnt']++;
                    $grades[$grade]['total'] += $score;
                    $ranks[$grade][$class . '_' . $number] = $score;
                }
            }
        }

        // 順位
        foreach ($ranks as $subject => $grades) {
            $cnt = 1;
            arsort($grades);
            foreach ($grades as $grade => $score) {
                [$class, $number] = explode('_', $grade, 2);
                $students[$class][$number][$subject . '_rank'] = $cnt++;
            }
        }

        // 偏差値とか。疲れたのであとよろ
        return $students;
    }
}

$students = [ /* [クラス=>[学生番号=>[成績]]] */
    1 => [
        1 => [
            'Japanese' => 100,
            'English' => 90,
            'Mathematics' => 80,
            'Sociaty' => 70,
            'Science' => 60,
        ],
        ...
    ],
    ...
];

$result = Grades::getRank($students);
```

むしろトレイトをどう使えばいいのかわからなかった。


# 感想

設問が曖昧でよくわからない問題があるのを別にしても、明らかに出題者が想定してない解法を書いてしまってる問いが多々あるわけですがまあこんなものでどうでしょう。