18
5

More than 1 year has passed since last update.

【 #ゆめみからの挑戦状 ★第5弾】に挑戦 67バイト

Last updated at Posted at 2022-09-12

最近は地球を防衛しつつ戦うために生きつつ床を塗り潰しつつアイドルをプロデュースするのにとても忙しい。

PHPだったので挑戦してみることにします。

問題

入力$inを、出力$outに変換します。

// 入力
$in = [
    ['2nd' => 'two', 'four' => '4th'],
    'three' => '3rd',
    ['one' => '1st'],
    '10th' => 'ten',
    ['6th' => 'six'],
    '5th' => 'five',
    'seven' => '7th',
    ['fourteen' => '14th', '11th' => 'eleven'],
    ['8th' => 'eight'],
    'thirteen' => '13th',
    '12th' => 'twelve',
    'nine' => '9th',
    ['15th' => 'fifteen'],
];

// 出力
$out = [
    '1st' => 'one',
    '2nd' => 'two',
    '3rd' => 'three',
    '4th' => 'four',
    '5th' => 'five',
    '6th' => 'six',
    '7th' => 'seven',
    '8th' => 'eight',
    '9th' => 'nine',
    '10th' => 'ten',
    '11th' => 'eleven',
    '12th' => 'twelve',
    '13th' => 'thirteen',
    '14th' => 'fourteen',
    '15th' => 'fifteen',
];

ここではバイト数を、変数$outに適切な値を突っ込むまでのカウントとします。
出力はvar_dumpでもvar_exportでもprint_rでも好きなものを使ってもらうということで。

78バイト

78バイト (84バイト >= PHP8.0)
for(;$i++<15;$out[($s=numfmt_create)(en,6)->format($i)]=$s(en,5)->format($i));       // PHP <  PHP8.0
for(;$i++<15;$out[($s='numfmt_create')('en',6)->format($i)]=$s('en',5)->format($i)); // PHP >= PHP8.0

var_export($out);

$in
なにそれ?

解説

NumberFormatterは、数値をロケールに沿った文字列に変換してくれるフォーマッタです。

echo (new NumberFormatter('ja', NumberFormatter::SPELLOUT))->format(123456);
  // 十二万三千四百五十六

echo (new NumberFormatter('en', NumberFormatter::SPELLOUT))->format(123456);
  // one hundred twenty-three thousand four hundred fifty-six

echo (new NumberFormatter('fr', NumberFormatter::SPELLOUT))->format(123456);
  // cent vingt-trois mille quatre cent cinquante-six

echo (new NumberFormatter('de', NumberFormatter::SPELLOUT))->format(123456);
  // ein-hundert-drei-und-zwanzig-tausend-vier-hundert-sechs-und-fünfzig

便利ですね。

コンストラクタの第1引数$localeでロケール(≒言語)を、第2引数$style出力フォーマットを指定します。
定数値5はNumberFormatter::SPELLOUTを表し、英語の場合one/two/threeみたいな形、定数値6はNumberFormatter::ORDINAL1st/2nd/3rdみたいな形になります。
定数値はPHPバージョンによって異なる可能性があるので、通常は定数名で指定するべきです。

これだけ知っていれば問題文の出力は全て作れるので、入力を使う必要がなくなりました。
あとはなるべく短くしていきましょう。

NumberFormatter::create()よりnumfmt_create()のほうが短いのでそちらを使います。
それでも長いですが、PHPは$f='numfmt_create';$f()みたいに変数値から関数を呼べるので、複数回使う場合は変数に入れると短くなります。
実は変数値だけではなく'numfmt_create'()と文字列からでも直で呼べるんだけど、これの使い道は今のところ思いついていません。
未定義定数値が定数名と同じになる書き方はPHP8.0で死んだので、PHP8.0以降はクォートで囲むのが必須です。
まあそれ以前もE_WARNINGだったので、元々使ってはいけない書き方でしたが。

ということで、PHP7で78バイト、PHP8で84バイトが私の考えつく限界でした。
これ以上いけますかね?

もちろんこの回答は問題文に関わらず出力が固定なので、設問が少し変わるだけで動かなくなります。
コードゴルフ以外で使ってはダメな解法です。

まともに解く

流石にこれだけだとアレなので、問題文が変更されても動作する汎用的な解法も置いておきます。
こちらは特にショートコーディングとか考えず普通に解きました。
あとエラーも出ないようにします。

// そのいち
$out = iterator_to_array(new RecursiveIteratorIterator(new RecursiveArrayIterator($in)), true);
foreach ($out as $k=>$v) {
    if (is_numeric($v[0])) {
        $out[$v] = $k;
        unset($out[$k]);
    }
}
ksort($out, \SORT_NUMERIC);
var_export($out);

// そのに
$out = [];
array_walk_recursive($in, function ($v, $k) use (&$out) {
    is_numeric($v[0]) ? $out[$v] = $k : $out[$k] = $v;
});
ksort($out, \SORT_NUMERIC);
var_export($out);

すごい普通だ。
普通すぎてみんな書いてる書き方なので面白くもないですね。

そのいちは配列を手動でフラットにしたあとで、入れ替えが必要な項目のキーと値を入れ替えています。
そのにはフラット化と入れ替えを同時に行っています。

ただforeacharray_walk_recursiveとかは何故かキーにリファレンスが使えないので、何気にキーの書き換えが面倒だったりします。
array_walk_recursive($in, function (&$v, &$k){ is_numeric($v[0]) && [$k, $v] = [$v, $k]; });とか書きたいですよね。

いや、ゴルフ以外でこんなの書きたいか?

70バイト

2022/09/14更新。78バイト → 70バイト

実はdateでも"1st"って文字列を作れることに気がついた。

70バイト
for(;$i<15;$out[date(jS,$i++*86400)]=numfmt_create(en,5)->format($i));

68バイト

2022/09/15更新。70バイト → 68バイト

まじかよ。
そこまで考えが及ばなかったぜ…凄い!

68バイト
for(;$i<15;$out[date(jS,$i++*9e4)]=numfmt_create(en,5)->format($i)); 

67バイト

2022/11/14更新。68バイト → 67バイト

67バイト
for(;$i<15;$out[date(jS,$i++*9e4)]=numfmt_create(1,5)->format($i));

1文字減らせた。

numfmt_createの第一引数$localeに無効な値を与えると、3v4lではenとして扱われました。
めでたし。

なお、この出力は環境によって変わります。
手元のWindowsでは日本語になりました。

ちなみに無効な値を与えたときに使われるデフォルト値はintl.default_localeではないようです。
手元のWindowsではintl.default_locale=en_USしようがlocale_set_default('eu-US')しようがsetlocale(LC_ALL, 'en')しようが日本語になりました。
いったいどこの値を見ているんだこれ。

18
5
4

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
18
5