- 投稿日:2019-09-09T21:14:16+09:00
php-master-changes 2019-08-26
この日は arginfo の PHP スタブへの移行、ldap_read() を呼ぶと引数に渡した配列が変更される問題の修正、一部組み込み関数の警告を例外を投げるエラーに昇格する修正、2 進 / 16 進リテラルで _ が複数続いた際に Out of memory を起こす問題の修正、テストの修正 ZendMM の実装修正、SplDoublyLinkedList のシリアライズで SEGV が起きる問題の修正、opcache の func_info の修正、AI_IDN_ALLOW_UNASSIGNED と AI_IDN_USE_STD3_ASCII_RULES の非推奨化 / 削除、組み込み関数でのスカラ型検査のデバッグ用コードの修正、非推奨化された libzip の関数の置き換え、extract() / compact() へのアサーションの追加と返り値からの nullable 外し、gen_stub.php の修正、コンパイラの live range check 処理の修正があった!
2019-08-26
cmb69: Add ext/gmp stubs
- https://github.com/php/php-src/commit/64d0416a721010b779e34597461c4121b9d5a2d0
- ext/gmp で、arginfo の PHP スタブへの移行
cmb69: Don't modify arrays passed by value
- https://github.com/php/php-src/commit/ac40d0ffbca2a8c941c748ace7528b4699b307fc
- [7.4~]
- ext/ldap で、ldap_read() を呼ぶと引数に渡した配列が変更される問題の修正
- 草としか言いようがない
Girgias: Promote warnings to errors in str_pad()
- https://github.com/php/php-src/commit/743729d5bf4637ef6f9f6008213ac675d216abf6
- str_pad() の警告を例外投げるエラーに昇格
theodorejb: Fix #78454: Consecutive numeric separators cause OOM error
- https://github.com/php/php-src/commit/1a78bdab276a9e34aa1ae00a184538e2d0dacdcd
- [7.4~]
- Numeric Literal Separator の実装で、2 進 / 16 進リテラルで _ が複数続いた際に Out of memory を起こす問題の修正
smalyshev: Fix test
- https://github.com/php/php-src/commit/d1646e328aff679dccf5597476e98c182fe4365c
- [7.3~]
- ext/pcre で、テストの期待出力がなんか(PCRE のバージョン変更か何かで?)ズレてたので修正
nikic: Fix overflow in memory limit checks
- https://github.com/php/php-src/commit/16d35eb643bf974554e5264021ee10fc969e2053
- [7.2~]
- ZendMM の実装で、memory_limit のチェックにオーバーフローがあり、アドレス空間のサイズに近いサイズのアロケーションでメモリーリミットの上限チェックでなく Out of memory の方が出ていた問題の修正
nikic: Fixed bug #78456
- https://github.com/php/php-src/commit/9483c507725637a45704d5983f3eaf7ca37875c9
- [7.4~]
- ext/spl で、SplDoublyLinkedList のシリアライズで SEGV が起きる問題の修正
- 参照カウントの操作が抜けてた奴
TysonAndre: Fix opcache optimizer info for time_nanosleep
- https://github.com/php/php-src/commit/f5bccc0eb5bfe2b1c9ba55cf1df27f2a18a046fa
- ext/opcache で、time_nanosleep() の func_info の修正
TysonAndre: Also fix signature for passthru
- https://github.com/php/php-src/commit/1e82a2d659aac6ef95a6820d4f6ac3684e07bcad
- [7.2~]
- ext/opcache で、passthru の func_info の修正
viest: Deprecate AI_IDN_ALLOW_UNASSIGNED and AI_IDN_USE_STD3_ASCII_RULES
- https://github.com/php/php-src/commit/5703943081fc24d555e572edf507a05702974d98
- [7.4~]
- ext/sockets で、AI_IDN_ALLOW_UNASSIGNED と AI_IDN_USE_STD3_ASCII_RULES の非推奨化
- glibc 2.28 で非推奨化されたので、ということらしい
Girgias: Promote warnings to errors in explode()
- https://github.com/php/php-src/commit/91f4e2e61493d06db8b6fbbccbd0e4166d86f48f
- explode() の警告を例外投げるエラーに昇格
viest: Remove deprecated getaddrinfo flags
- https://github.com/php/php-src/commit/48040cf3459327a137c78a5bd76dbee3221948f3
- ext/sockets で、getaddrinfo() の非推奨化されたフラグ対応実装を削除
theodorejb: Convert remaining array function arginfo to PHP stubs
- https://github.com/php/php-src/commit/d5f42d68c89497834649fce36d9dd65fb6598c35
- 配列操作関数の arginfo を PHP スタブへ移行
nikic: Avoid duplicate "non well-formed" warning
- https://github.com/php/php-src/commit/0038db22510cce37b24491c266123ba5b1e93953
- 組み込み関数でのスカラ型検査のデバッグ用コードで、不要な重複警告が出ないよう修正
Girgias: Promote warnings to errors in array_rand()
- https://github.com/php/php-src/commit/c1c8538f952ff8cc4358fc988d5e9b11dcfbf6e6
- array_rand() の警告を例外投げるエラーに昇格
cmb69: Replace deprecated libzip functions
- https://github.com/php/php-src/commit/8f897f1040f00210f4a5cdd82a88a1fe3e558955
- [7.4~]
- ext/zip で、非推奨化された libzip の関数を置き換え
nikic: Assert that symbol table is available in compact()
- https://github.com/php/php-src/commit/a47f170a7562d40d2f675600c641c616c2ebc592
- extract() / compact() の実装で、シンボルテーブルが見えることのアサーションを追加し、返り値型宣言から nullable を除去
nikic: Make sure that params with null default are marked nullable
- https://github.com/php/php-src/commit/0c2d4d698c9743f75cb1adb6d485154192536242
- arginfo 用の PHP スタブでデフォルト null のパラメータに nullable 指定も付けるよう修正
cmb69: Add ext/ffi stubs
- https://github.com/php/php-src/commit/e047e9d8939d77484a5d9eac5611622597a3d189
- ext/ffi で、arginfo を PHP スタブへ移行
nikic: Make arginfo printing of prefer-ref arguments nicer
- https://github.com/php/php-src/commit/a98307df8771f9b7fb0227ac2d4459e1419b887c
- arginfo 生成での ZEND_SEND_PREFER_REF の扱いを修正
- ZEND_SEND_PREFER_REF がなんじゃらほい、は別途気が向いたら調べて追記
burakcakirel: Add assert arginfo stubs
- https://github.com/php/php-src/commit/d2210dc2ea69adabbe87a118223970ee2e323e41
- assert() / assert_options() の arginfo を PHP スタブへ移行
cmb69: Add ext\opcache stubs
- https://github.com/php/php-src/commit/0ab4fca8e3f126649901f7a33e3dd8c320d7022e
- ext/opcache で、arginfo を PHP スタブへ移行
nikic: Don't use needs_live_range hook for "special" live ranges
- https://github.com/php/php-src/commit/e2b49d6c45f0c5ed69268d32da2f4972265db663
- [7.4~]
- コンパイラの live range check で、needs_live_range を一部ケースでは行わないよう修正
- 投稿日:2019-09-09T21:08:17+09:00
PHPの繰り返し処理大全
PHP7.3時代の話です。
PHP8や9のころには、また別の結論になっているかもしれません。最初に結論
・全要素繰り返しはforeach
・途中で打ち切るのはwhile/for
・それ以外はいらん繰り返し処理一覧
foreach
PHPのforeachは非常に優秀です。
あらゆる反復可能な値を繰り返し処理することができます。$arr = [1, 2, 3]; foreach($arr as $val){ echo $val; // 順に1, 2, 3 }PHPの配列は順序付きリストなので、順番も保証されます。
また、キーと値を両方とも取れるので、inとofどっちだったっけとか悩む必要もありません。$arr = [3 => 1, 2 => 2, 1 => 3]; foreach ($arr as $key => $val) { echo $key, $val; // 31, 22, 13 順番が変わったりはしない }オブジェクトにも使えます。
class Test{ public $a = 1; protected $b = 2; private $c = 3; public function loop(){ foreach($this as $val){ echo $val; } } } $test = new Test(); foreach($test as $val){ echo $val; // 1 } $test->loop(); // 1,2,3オブジェクトに使った場合、可視なプロパティが順にアクセスされます。
すなわち外部からはpublicプロパティしか見えず、内部からならprotectedやprivateも見えるということです。さらに他言語では御法度の、ループ中で自身や別要素を削除するという芸当がPHPでは可能です。
$array = range(0, 10); foreach ($array as $k => $v) { unset($array[$k - 1]); } var_dump($array); // [10=>10]ループ中で、前回ループの値を削除しています。
他言語でこんなことをやるとどんな動作になるかわかったものではありませんが、PHPでは全要素をきちんとループした上で、最終的に$array
の値は想定通りになります。
配列をforeachした時点で配列のコピーが作成されるので、ループ内で変なことをしでかしてもループ自体には影響が及ばないように対策されているのです。
おかげで何も考えずにfilterが実装できます。$array = [1, 2, 3, 4, 5, 6]; // 偶数以外削除 foreach ($array as $k => $v) { if ($v % 2) { unset($array[$k]); } } var_dump($array); // [2, 4, 6]while
条件がtrueっぽい値であるかぎり、ループを繰り返します。
$i = 1; while ($i <= 10) { echo $i; $i++; }foreachとの使い分けですが、foreachは配列などの全要素にアクセスする用途に使います。
whileは何らかの条件でループを脱出する用途に使います。// 30回ループしたら脱出 $i = 0; while ($i < 30) { echo $i; $i++; } // 30秒経ったら中断 while(true){ sleep(1); if(time() - $_SERVER['REQUEST_TIME'] > 30){ break; } } // 標準入力を出力 while( ($line = fgets(STDIN)) !== false){ echo $line; }うっかり使うと無限ループになるので使用は避けましょう。
いや嘘。
whileを使うべきところにforやforeachを使ったりはせず、適切に使い分けましょう。for
whileで多くの場合必要となる『初期値設定』『カウント処理』を最初から文法に組み込んだものです。
// 30回ループしたら脱出 for ($i = 0; $i < 30; $i++) { echo $i; }whileではループ前後に書かざるをえなかった定型処理がひとつの文にまとまったことで、わかりやすくなって見た目もすっきりしました。
whileは常にforで書き換え可能なので、実はwhileは必ずしも存在する必要はありません。
しかし初期値設定などが必要ない簡易的なループはwhileで書いた方がわかりやすいので、forとwhileは処理内容によって使い分けるとよいでしょう。// forを使うほどではない for(; true; ){ sleep(1); if(time() - $_SERVER['REQUEST_TIME'] > 30){ break; } }PHPでは、配列要素などにアクセスする用途でforを使用する理由はありません。
$array = [1, 2, 3]; for ($key = 0; $key < count($array); $key++) { echo $key, $array[$key]; // 01, 12, 23 } $array = [0 => 1, 2 => 3]; // 死 $array = ['answer' => 42]; // 死歯抜けのない純粋配列にしか使用できず、うっかり連想配列や歯抜けのある配列にforでアクセスすると死にます。
要素に対するアクセスには必ずforeachを使いましょう。forの存在価値はループのためではなく、条件分岐のためにあります。
for(; time() - $_SERVER['REQUEST_TIME'] <= 30; sleep(1));条件部分やループカウント部分にあらゆる処理を詰め込むことでforの本文を空にすることもできますが、見づらくなるだけなので止めましょう。
do - while
条件のチェックが最後に行われるということ以外はwhileと同じです。
つまり、たとえ条件が偽でも必ず一回だけは実行されるwhileということです。$i = 100; do { echo $i++; } while ($i < 30);正直、whileではなくこちらを使わなければならないシーンを思いつきません。
関数型関数
PHPには組み込みでarray_walk、array_reduce、array_productといった、関数型のように書ける関数が多数用意されています。
これらを使うことで反復処理をやめ、関数型プログラミングっぽい記述にすることが可能になります。$sum = 0; foreach($array as $val){ $sum += $val; } echo $sum; echo array_sum($array); // ↑と同じが、基本的にこれらを使う必要はないです。
上記のようなわかりやすい例はむしろ例外で、PHPでは文法の都合上、大抵の処理は関数型で書くと却ってわかりづらくなります。以下はPHPで高速オシャレな配列操作を求めてより借用した例です。
// 0~10000のうち、偶数だけを抽出して自乗し、結果が20を超えるものを足しあわせよ // 関数型 echo array_sum( array_filter( array_map( function ($v) { return $v ** 2; }, array_filter(range(0, 10000), function ($v) { return $v % 2 === 0; }) ), function ($v) { return $v > 20; } ) ); // 普通に書く for ($sum = $v = 0; $v <= 10000; ++$v) { if ($v % 2){ continue; } $v **= 2; if ($v <= 20){ continue; } $sum += $v; } echo $sum;明らかに普通に書いた方がわかりやすいですね。
そもそも関数型の例でよく出てくる『○○を抽出する』みたいな処理は、PHPであればSQL発行する時点で絞っておけって話ですし。filter_varなど使いこなすと色々楽しいこともできるのですが、実用的かと言われると首が傾いてしまいますね。
もちろん関数型で書いてもわかりやすい場合もありますが、別にforeachで書いたところで可読性も大してかわらないので、なら最初から全部foreachで書いた方が手っ取り早いです。反復処理の定義
オブジェクトに対するループ処理の挙動を、PHPでは任意に定義可能です。
上のほうでオブジェクトをforeachするとpublicプロパティが順番に出てくると言いましたが、それはデフォルトの動作であって、やろうと思えば変更できるということです。Iteratorインターフェイス
反復処理実装の基本です。
Iteratorインターフェイスをimplementsして各メソッドを実装します。class Test implements Iterator { public $dummy = '出てこない'; private $data1 = [1, 2, 3]; private $data2 = [4, 5, 6]; private $current = 0; /** * @Override * 現在の値を返す * @return mixed 現在の値 */ public function current(){ return $this->data1[$this->current] ?? $this->data2[$this->current - 3] ?? null; } /** * @Override * 現在のキーを返す * @return string|int 現在のキー */ public function key(){ return $this->current; } /** * @Override * ポインタを次に移動する */ public function next(){ $this->current++; } /** * @Override * ポインタを初期化する */ public function rewind(){ $this->current = 0; } /** * @Override * 現在のポインタが有効か * @return boolean 有効ならtrue */ public function valid(){ return isset($this->data1[$this->current]) ?: isset($this->data2[$this->current - 3]); } } $test = new Test(); foreach ($test as $key => $val) { echo $key, '=>', $val; // [0=>1, 1=>2, 2=>3, 3=>4, 4=>5, 5=>6] }publicであるはずの
$dummy
は出てこなくなり、current
で返した結果が表示されるようになります。
このように、Iteratorインターフェイスを使うことでループで返す値を好き勝手に変更することができるようになります。
ただ、変なことをやってもわかりにくくなるだけなので、基本的にあまり使わないほうがいいと思います。IteratorAggregateインターフェイス
Iteratorインターフェイスは必ず5個のメソッドを実装する必要があって面倒です。
PHPには最初からイテレータが幾つも用意されており、それらで賄える範囲であれば簡単に実装できるIteratorAggregateがあります。class Test implements IteratorAggregate { public $dummy = '出てこない'; private $data1 = [1, 2, 3]; private $data2 = [4, 5, 6]; /** * @Override * イテレータを返す * @return Iterator */ public function getIterator(){ $iter = new AppendIterator(); $iter->append(new ArrayIterator($this->data1)); $iter->append(new ArrayIterator($this->data2)); return $iter; } } $test = new Test(); foreach ($test as $key => $val) { echo $key, '=>', $val; // [0=>1, 1=>2, 2=>3, 0=>4, 1=>5, 2=>6] }IteratorAggregate::getIteratorに適当なイテレータを投げれば、foreachループでそれが出てくるようになります。
今回は配列をイテレータにするArrayIterator、複数のイテレータを順にまとめるAppendIteratorを使って、$data1
と$data2
が順に出てくるようにしました。もちろん、書くのが楽になったからといって使いまくると何が出てくるかわからないブラックボックスになってしまいます。
RecursiveRegexIteratorあたりまで来ると正直何言ってるかわからないので、変なイテレータには手を出さない方がいいと思います。ジェネレータ
ジェネレータはクロージャとセットにされがちですが、単に何度もreturn(yield)できる関数という認識でいいと思います。
function getPrimeNumber(){ yield 2; yield 3; yield 5; yield 7; yield 11; yield 13; yield 17; return 19; // returnは出ない } $primes = getPrimeNumber(); foreach($primes as $prime){ echo $prime; // 2, 3, 5, 7, 11, 13, 17 }returnのかわりにyieldというキーワードを使います。
yieldキーワードが入った関数は、関数呼び出しの返り値が自動的にGeneratorインスタンスになります。
たとえ絶対にyieldを通らない実装だったとしてもそうなるので、少し注意が必要です。返ってくるのはオブジェクトなので、その返り値をforeachでループすることができるわけですが、その際は関数の最初からではなくyieldで止まったところの次の文から処理が再開されます。
関数内の変数値などは維持されるので、何も考えずにメモ化ができたりします。
上で出した例では全く意味がありませんが、無限数列などを作ったりする際にはジェネレータがとても役立ちます。// フィボナッチ数を求めるジェネレータ function getFibonacci(){ $fa = 0; yield $fa; $fb = 1; yield $fb; while (true) { $fib = $fa + $fb; $fa = $fb; $fb = $fib; yield $fib; } } $count = 0; foreach (getFibonacci() as $fibonacci) { echo $fibonacci . "\n"; // 適当に切らないと無限ループする if ($count++ > 30) { break; } }再帰もメモ化もgotoもなんもなしに、普通に定義通りのフィボナッチ数を求める関数が書けてしまいました。
ジェネレータは頻繁に出番があるかというと無いですが、覚えておくといざというとき便利な機能です。
まとめ
自発的に使うのはforeachとwhile/forだけでいいよ。
それ以外はあまり出てくるものでもないので、出てきたときに調べるくらいで大丈夫でしょう。
使う必要のないもの
foreachのリファレンス
foreachでリファレンスが取れますが、使用してはいけません。
$array = range(0, 5); foreach ($array as &$val) { $val *= 2; } var_dump($array); // [2, 4, 6, 8, 10] $val = 42; var_dump($array); // [2, 4, 6, 8, 42] ←そもそもリファレンスはあらゆる場面で一切使用禁止です。
リファレンスで高速化が云々とか言ってる人は全員間違い1なので、生暖かい目でスルーしましょう。do - whileの早期return
do-while記法は、簡易的な早期returnに使用できます。
以下はマニュアルに載っている例です。do { if ($i < 5) { echo "i は十分大きくはありません。"; break; } $i *= $factor; if ($i < $minimum_limit) { break; } echo "iはOKです。"; /* 実際の処理 */ } while (0);breakはループを抜ける文なので、
$i<5
だったり$i < $minimum_limit
の場合は、このwhileループを抜けます。
条件に当てはまらずループの最後まで来た場合、条件が常に偽であるため、そのままループを終了して先に進みます。
はい、早期returnできました。もちろんこのような書き方はせず、メソッドなどに出してください。
current / reset / next / prev / end
配列ポインタを手動で操作することが可能です。
$array = range(0, 5); while(true){ echo key($array), current($array); if(next($array) === false){ break; } }配列ポインタ操作関数はreset、next、prev、endなどひととおり揃っています。
が、あえてこれらの関数を使わなければならない場面はありません。さらにマニュアルには書かれていないのですが、実はこいつらの引数は
Z_PARAM_ARRAY_OR_OBJECT_HT
であり、つまりオブジェクトを受け付けます。class Test{ public $a = 1; protected $b = 2; private $c = 3; } $array = new Test(); while(true){ var_dump(key($array), current($array) ); if(next($array) === false){ break; } }マジかよ。
each
PHP7.2でDeprecatedになったため、使ってはいけません。
いにしえのPHPではメモリ節約のためにeachを使おうなどとされていた時代もありましたが、PHP7時代においては間違った記述です。
上のほうでforeachしたら配列のコピーが作成され云々とか言いましたが実は嘘で、現在ではforeachループするだけならコピーは作成されません。
ループ中で元の配列を変更しようとしたときに初めてコピーが作成されます。
これはコピーオンライトと呼ばれる技術で、最近のPHPの最適化技術のひとつです。
現在のPHPは非常に最適化が進んでいるので、下手なことを考えるより標準機能を使った方がよっぽど使用メモリも少なく速度も速いです。ジェネレータの変な使い方
素のジェネレータはforeachしかできず、whileやforで書くことができないのですが、実はGeneratorクラスはIteratorインターフェイスをimplementsしているので、手動でループさせることもできます。
$fibonacci = getFibonacci(); while ($fibonacci->key() < 28) { $fibonacci->next(); echo $fibonacci->current(); // 1, 1, 2, 3, 5, 8, … }こんな処理が必要になるのであれば、ジェネレータではなく他の機能を使った方がよいでしょう。
yield from
キーワードを使って、別のジェネレータの返り値を取り込むことができます。function gen() { yield 1; yield from [2, 3]; yield from gen2(); } function gen2() { yield 4; yield 5; } $gen = gen(); foreach ($gen as $v) { echo $v; // 1, 2, 3, 4, 5 }こんな処理を作り込むよりも、データ構造を見直した方がよいでしょう。
ソート
usortなんかは関数型っぽい書き方なのでループと言えないこともない可能性がなきにしもあらずな気がしないでもないんだけどループの範疇に含めるべきものだろうか?
$arr = [3, 1, 2]; usort($arr, function ($a, $b) { return $a <=> $b; }); var_dump($arr); // 1, 2, 3やはりループっぽくはないですね。
その他
思いついたのを並べたらこんなかんじでした。
見落としや間違いがあったら誰かがプルリクしてくれるはず。
0.1%くらい正しいことを言っている可能性もあるが、見分けは付かないので全て間違いと考えてかまわない。 ↩
- 投稿日:2019-09-09T17:36:59+09:00
laravel/PHPでよくみるエラー
Whooops!ってあるあれですね
よくエラーを出しては調べているのですが、よく同じことを調べているのでまとめてみました。Fatal error: Call to undefined function
Fatal error: Call to undefined function xxx未定義の関数を呼び出そうとする時にでるエラー
- 関数を確認する
- ファイルを読み込んでいるか確認するNotice: Undefined offset:
Warning: Ambiguous class resolution xx on line number xx配列のなかで未定義のキーを指定した際に起こるエラー
- 配列の中身を確認するNo such file or directory
No such file or directory 〜ファイルやディレクトリが存在していないときに起こるエラー
- パスを確認するWarning: Ambiguous class resolution
Warning: Ambiguous class resolution, “xxx\xxx” was found in both “/xxx/xxx.php” and “/xxx/xxx.php”, the first will be used不明瞭なクラスが存在しているエラー
- クラス名が正しいか確認
- namespace に記載しているパスが正しいか確認Route not defined.
Route [\xxx\xxx] not defined.未定義エラー
- ルーティングが正しいか確認Invalid argument supplied for foreach
Invalid argument supplied for foreach配列ではない変数でforeachを使用している。
- foreach が使えるのは配列とオブジェクトだけなので確認するArgument 1 passed to 〜
Argument 1 passed to 〜 must be an instance of 〜, instance of 〜 given, called in 〜 on line xxx型宣言をした場合、関数の呼び出し時に特定の型であることを要求できるようになり、不正な型で指定した場合はエラーとなる
詳しくはここ
- タイプヒントと一致しない型を引数にいれていないか確認する
- クラスのインポートを忘れていないか確認するCall to a member function 〜
Call to a member function xxx指定した関数が呼べないエラー
- 関数が存在していない
- オブジェクトがない、インスタンスが未生成などエラーよく見ると答えがほぼ書いてあるのはありがたいですね!よく見るように気をつけたいです。
また理解が深くなったら徐々に増やします
- 投稿日:2019-09-09T15:49:02+09:00
プログラミング始めて1か月の超初心者がAtCoderを始めてみた
プログラミング始めて1か月の超初心者がPHP7技術者認定初級試験を受けてみたで、私はPHPを学び始めました。
そんなとき、AtCoder に登録したら次にやること ~ これだけ解けば十分闘える!過去問精選 10 問 ~を読み、プログラミング自体を始めたばかりのくせにどんなものなのか試してみたくなってしまいました。※この記事では本当の初心者が AtCoder を始める際に参考になる可能性がある程度の内容です。
※準備はそこそこにとりあえずやってみたいという超初心者の方が進めながら眺めるとちょうど良いかもしれません。A - Welcome to AtCoder
登録が済むと、まずはチュートリアル的な課題に挑戦できます。
入力 入力は以下の形式で与えられる。 a b c s……既に何言ってるかわからない。「以下の形式」って何。
超初心者の私は問題にたどり着くこともなく、早くも詰んだので解答例を見てみました。
見たこともなければ当然書いたこともないコードが載っていた。
調べてみると標準入力を受け取れって意味みたい。なるほど。
PHPで標準入力から値を受け取る(競技プログラミングとか)。なるほど。よし、これでようやく問題に取り組めるぞ!
PHP7 を選択し、解答例とほぼ同じコードを書いて提出。
あれ、WA (Wrong Answer) になる。なんで?
……あー、PHP7 って言語を指定しても開始タグは必要なのね。
開始タグを入力して無事 AC (Accepted)。AtCoder Beginner Contest
チュートリアルを終えて、
AtCoder に登録したら次にやること ~ これだけ解けば十分闘える!過去問精選 10 問 ~
でおっしゃるように、ABC 042 からやってみることにしました。
(せっかく「過去問精選 10 問」してくれているのに、なぜかその10問を見ることなく。)
A、B、C はすんなりといけて、なんだ楽勝じゃん~と思っていたら、D で詰まった。すんなりいけたとは言ったが、C はエレガントな解法がわからずめちゃめちゃ総当たり的に解いた。
大学入試の数学等を解くときには絶対にあり得ない解法。
とにかく計算量を少なくして、楽に解けるようにって頭の使い方ばかりして生きてきたので新鮮。
でも軽く調べてみると、この総当たり的解法は割と普通らしい。動的計画法 Dynamic Programming
それなら詰んだ D 問題も、総当たり的に調べてやれば良いはず。でもどうやって総当たりを実装したら良いかわからない。
とりあえず諦めて、次の ABC 043 に行っても、結局 D 問題で似たような課題にぶち当たる。
さらに次の ABC 044 に行っても今度は C で似た課題に出くわす。なんとなく詰んでいるポイントが似ていて、どれも同じような解法で解ける気がしたので、
仕方ないから先人から勉強するかぁと思って調べてみると、動的計画法を応用するらしい。
中学受験した人とかにはおなじみの最短経路を足し算して求めていくやつ。動的計画法超入門! Educational DP Contest の A ~ E 問題の解説と類題集
というすごくありがたい記事があったので読んでみます。なるほどなるほど。
イメージ的には、漸化式で解くのが DP 配列で、数学的帰納法で解くのがメモ化再帰ってことね。あくまで私のイメージですが。
(漸化式って言っても一般項を求めるわけではないので、結局はどっちも数学的帰納法のイメージかもしれません。)それがわかれば、漸化式を思い浮かべながら Educational DP Contest の A 問題と B 問題はすらり。
C 問題は i-1 日目に幸福度最大の活動が複数あると i 日目の活動毎の幸福度によって i-1 日目の選択を変えなきゃいけない。
って思うんだけど、そうやって次の日のことまで考えると漸化式にならないから、
dp 配列の次元を増やして、高々 3 つの活動については総当たりしてしまおうという発想。
やっぱりこの総当たりという発想は慣れないとなかなか出てきませんね。D 問題はイメージを掴むのに私は苦労しました。
その原因は恐らく、重さを for で総当たりするイメージが湧かないこと。
「重さ」という言葉がどうにも自然数に思えなくて、この記事にあるような表が全く思い描けなかったのです。
そこで、重さではなく値段とか、番号とか、自然数を思い描きやすいものに読み替えるとかなりスッキリしました。
そして、「イメージを膨らませて問題を解く」のではなく、「dp の表を埋める」と問題を読み替えてしまえばさらにスッキリ。
イメージしにくい立体図形や 4 次元以上の空間を、ベクトルを用いて代数的に解くようなイメージですね。E 問題は D 問題と同じ……?
が、同じようにやるとメモリやら実行時間やらがすごいことになってしまいます。
解説を読んでみると、ランダウの記号が出てきたぞ・・・?
高校生の時に少しだけ微積分で出てきたけど、あまり深くは学んでいなかったオミクロン。
高校生のときにサボっていたツケがこんなところで回ってくるとは。
計算量オーダーの求め方を総整理! 〜 どこから log が出て来るか 〜
なるほど、計算量オーダーという考え方なんですね。成果が目に見えると嬉しい
そうして動的計画法も学びつつ、ちょくちょく AtCoder Beginner Contest の過去問を解き進めていっています。
が、なかなか D 問題をバシっと解けるようにはなりません。
そんな折、めちゃめちゃ簡単な D 問題に出会ってしまいます。
こんなに簡単でよいのかと恐る恐る提出してみるとしっかり AC される。
一応他の人の解答も見てみるかと提出一覧を確認すると……
おお、コード長も実行時間も大差で勝っている!
PHP は使っている方が少ないので、成果が見えやすくて嬉しいですね。
また、私のような初心者だとコードを書く技術では絶対的に劣りますが、
シンプルな解法を考えることで、コード長や実行時間で成果を出せるのも競技プログラミングの面白いところかもしれません。
(他の方はコンテストの限られた時間で解答を提出していらっしゃいます。単純に比較できないことを念のため申し上げておきます。)月刊競技プログラミングは役に立たない?
少し競技プログラミングについて調べて見ると、Twitterのトレンドに定期的にこの話題が上がるらしい。
ベテランの方はどうかわかりませんが、私のような初心者であれば役に立たないなんてことは絶対にないと思います。
競技プログラミングでは極端に大きな値等を扱ったりすることもあり、
初心者が普段出くわさないようなエラー等が発生するので、そのときにいろいろ調べるきっかけになります。
例えば SplFixedArray を使うとかなりメモリを節約できるらしいとか、intdiv() なる関数が存在するとか。
他にも "恐らく" 素数の判定や、ビット演算子の使い方、合同式の復習等もできる。
競技プログラミングに限った話ではありませんが、どんなことにも学びはあるってことだと思います。自分のタイプを知ることができる
競プロはいいぞ を読んですごく納得したのですが、
私は課題解決をしたい、そのためにプログラミングや数学を使いたいという人間なんだと思います。イメージ的にはコンサル?のような?
競技プログラミングをやってみて、楽しくなるならそういうタイプの人で、あまり楽しめないのであればモノづくりをしていきたいタイプの人ということなのかもしれません。
私の知人に最近 AtCoder を嗜んでいるって話をしたら、「好きそうだ」って言われたのはきっとそういうことなんだと思います。独り言
せっかくだから土曜の夜にやってる AtCoder Beginner Contest に参加しようぜって知人に誘われた翌日の土曜日の話。
午前中に草野球の試合をして、帰ってきて昼寝して起きたら既にコンテストが終わっていた。
- 投稿日:2019-09-09T14:04:24+09:00
KUSANAGI サーバー(マルチドメイン)でメール送信のspfをPASSにする
KUSANAGI サーバーを Conoha VPS(マルチドメイン対応)で動かしている場合です。
mb_send_mail でメール送信してみたところ、spfがPASSではなくNEUTRALになっていたので、PASSにするようにPHPプログラムを編集しました。
自動計算のページに設置したメールフォームです。(※calculator.jpのDNSのspfレコードは設定済みの想定です。)
spfがNEUTRALになったコード
// $to_mail, $subject, $message, $from の設定は省略 mb_send_mail($to_mail, $subject, $message, $from);というコードです。送信したところ以下のようなメッセージになりました。
spfがPASSになったコード
// $to_mail, $subject, $message, $from の設定は省略 $admin_mail = 'info@calculator.jp'; $envelope = "-f" . $admin_mail; mb_send_mail($to_mail, $subject, $message, $from, $envelope);というコードです。
spfがPASSにならなかった原因
おそらくですが、サーバー自体がマルチドメイン対応のため、PHPプログラム上で明示的にエンベロープを指定しないと、特定のドメイン(この場合はcalculator.jp)のメール送信とは判定されなったようです。
そのためcalculator.jpのspfレコードを設定してもPASSにならなかったようです。
- 投稿日:2019-09-09T11:45:08+09:00
laradockでphpMyAdminへログインする時に手間取ったのでメモ
苦戦したのでメモ。
laradockでlaravelの開発環境を構築してた中で、Mysql、phpMyAdminを使おうと思ったのですが、phpMyAdminにログインできず。
環境変数も同じにしたし、Mysqlのバージョンも5.5でインストールしていて、事前につまずく箇所は潰していたのに、それでもログインできず途方にくれていました。
結論
自分のPCの、
~/.laradock/data/mysql
下のファイルを削除して、改めてdocker-compose up -d nginx mysql
をしたらログインできました。どうやら、
~/.laradock/data/mysql
のファイルが壊れていた模様。参考になったのはこの記事。
LaradockでMySQLがどうしても立ち上がらない人あつまれー!
解決までの流れ
改めてMysqlを入れ直したり、
.env
の環境変数が同じになってるか何度も確認したり、それでもダメでひたすらググる。それでふと、
docker-compose ps
をやって起動しているコンテナ一覧を確認した時に、「あれ?Mysql起動してなくね?」って事に気付く。それまで、「laradock phpMyadmin」とかのワードでググっててたんですが、mysqlの方に問題あるんじゃね?って調べた結果、上記記事にたどり着く。
まとめ
いやー苦戦しました。多分一日半ぐらい。
冷静に一つ一つ確認してやっていけばすぐにわかったなー、と。プログラミング歴2ヶ月ぐらいなんですが、また一つ勉強になりました。
- 投稿日:2019-09-09T09:22:10+09:00
php-master-changes 2019-08-25
この日は鬼車の更新、テストの修正、arginfo の PHP スタブへの移行、chunk_split() の実装修正、不要コードを削除する修正があった!
2019-08-25
smalyshev: Update Oniguruma to 6.9.1
- https://github.com/php/php-src/commit/d3f2cfe20a5e9faf967c404a872ec9b4e14efe81
- [7.3~]
- ext/mbstring で、鬼車を 6.9.1 へ更新
nikic: Skip memory_limit test without ZMM
- https://github.com/php/php-src/commit/3602fea659be3777a04f316023b0001646ab42bf
- ZMM 無効時に memory_limit のテストをスキップするよう修正
cmb69: Add stubs for ext/ftp
- https://github.com/php/php-src/commit/4742b2e1de47164eb33f318148509bb738129177
- ext/ftp で、arginfo を PHP スタブへ移行
noobshow: Add some header and html function stub
- https://github.com/php/php-src/commit/b9c961e1601efc7abef9586f5145e00a2d8ed0cd
- header() や setcookie()、htmlspecialchars() 等いくつかの組み込み関数の arginfo を PHP スタブへ移行
nikic: Make string size calculation in chunk_split more precise
- https://github.com/php/php-src/commit/d955ee9f6b7e60895cac412969f9ddec91fbd2bb
- chunk_split() の実装で、文字列長の計算を正確化
- 無駄なアロケーションがあった
nikic: Remove unnecessary NULL check
- https://github.com/php/php-src/commit/4346d1b0e66e99809ba7ad9592198f79f673e744
- stripos() の実装で、不要な NULL チェックの削除
nikic: Remove unnecessary haystack length check in stripos()
- https://github.com/php/php-src/commit/6e3135070c13293c2f5f4d8a22ec282fd00f3f21
- stripos() の実装で、不要な haystack の長さチェックの削除
- 投稿日:2019-09-09T08:05:11+09:00
超カンタン!CSSで画像を自在に切り抜く方法
どうも!Hiroyukiです!
今日は、
「CSSで画像を自在に切り抜く方法」
をご紹介しますっ!
つまり、
好きな位置で画像を切り抜いて表示できるっ!
という、素ん晴らしぃ〜ものです(^o^)
僕もやってみたら、超カンタンで驚きでした(笑)
実際の利用例は、下記の通りです。
元となる画像のアップロード機能
切り抜き後の画像表示 ※切り抜き高さを「任意」で指定できますっ!
元となる画像アップロード機能を実装
HTML
<h2>トップ画像をアップ</h2> <figure> <img class="rounded" src="{{ Storage::disk('s3')->url(Auth::user()->top_image_url) }}"> <figcaption>現在のチャンネルトップ画像(編集前)</figcaption> </figure> <form method="POST" action="/storeTop" enctype="multipart/form-data"> {{ csrf_field() }} <input type="file" name="photo" class="btn"> <input type="submit" value="更新する?" class="button btn btn-primary"> </form>コードだとこんな感じです。
アップした画像を表示させる imgタグ の src = "〜〜" の中身は、当然自由なパスを入れて下さい!
ここでは、PHP/Laravelを使って、S3というAWSのストレージ機能のパスを示しているんですが、
ご自身が画像を保存する先のパスを入れて貰えれば、OK!!CSSを使って画像を切り抜く!
はい、ここから本題ですね!
HTML
<h2>トップ画像を編集</h2> <figure> <img class="rounded" src="{{ Storage::disk('s3')->url(Auth::user()->top_image_url) }}" style="width:1200px; height:200px; object-fit:cover; object-position:0% {{ $user->top_trim }}%;"> <figcaption>現在のチャンネルトップ画像(編集済)</figcaption> </figure> <form method="POST" action="/topTrim"> <div class="form-group"> <label>切り取る位置(高さ)を 0 ~ 100 の数値で入力</label> <div> <input type="number" name="top_trim" required> <div>例)画像の一番上を切り取る : 0 を入力</div> <div> 画像の一番下を切り取る : 100 を入力</div> </div> </div> <input type="submit" value="更新する?" class="btn btn-primary"> </form>imgタグ の src = "〜〜" で、上のアップした画像を表示させるものと「同じパス」を持ってきて、
それを切り抜きます!ボクのページの場合、Twitterのマイページのトップ画像みたいに、横に細長〜く切り抜きたいので、
(上記コードも横に細長〜いため、右にスクロールしないと分かりづらいかも知れませんが(笑))
imgタグに
style="width:1200px; height:200px; object-fit:cover; object-position:0% {{ $user->top_trim }}%;"width:1200px; height:200px" と、任意で「表示させたい画像幅・高さ」を書いています!
で、ここからがポイント!
"object-fit:cover;"と記載することで、
「画像のタテヨコ比を保持したまま、トリミングが可能となりますっ!!」
初めてできたとき、「スッゲぇ〜〜」ってなりました(笑)
更に、
"object-position: 横の位置% 縦の位置%;"と追記すれば、
「横の位置=一番左から○%の位置で切り取る」
「縦の位置=一番上から○%の位置で切り取る」
なんていう、すごく融通が効く、、、ステキすぎることが出来ちゃいま〜す!!(感涙)
このCSSの設定方法も、なかなか奥が深いので、ボクが書いたものじゃございませんが
下記のサイトも参考にしてみて下さい〜1行追加でOK!CSSだけで画像をトリミングできる「object-fit」プロパティー
https://www.webcreatorbox.com/tech/object-fit
で、ボクのページでは、上記機能を更に少し発展させて、
画像を切り取る高さ(縦の位置○%)をユーザーが好きな高さに設定できるようにしてます^^
つまり、コードをみて頂ければ分かりますが、
object-position:0% {{ $user->top_trim }}%;という風に、
「%」の前の数値を変数として、任意の値(高さのパーセンテージ)を
持ってこれるようにしています!!
ここでは、Laravelの変数で表現していますが、当然JavaScriptの変数など、他の変数でもOKですよ〜!
実際のサービス上で、上記機能を試して確認して頂くのが一番分かりやすい
と思いますんで、下記にボクのサービスのリンクを載せておきまーす
ボクが作ったオリジナルWebサービス(下記リンク)
http://youtubematome.herokuapp.com
新規ユーザ登録 → チャンネル設定 → ページ真ん中らへんの「トップ画像を編集」
で実際に使ってみることが出来まっす!!
宜しければどうぞ〜〜
- 投稿日:2019-09-09T03:16:47+09:00
URLの短縮に使える整数からURL文字列にするアルゴリズム
License under NYSL
function shorty($number){ $str="0123456789-_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; $len=strlen($str); $res =""; $tn=$number; do{ $div = intdiv($tn,$len); $odd = $tn % $len; $res .= $str[$odd]; $tn = $div; }while($div != 0); return $res; }