忍者ブログ
[PR]
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。



2024/05/08 08:19 |
PHP5.6.0 「第21回オフラインリアルタイムどう書くの参考問題」をPHPで解く
http://qiita.com/torazuka/items/cbdb6b581a57e4754dd4
http://d.hatena.ne.jp/torazuka/20140509/yhpg

希望日を出して優先順に振り分けるという、現実でもよくある問題です。
今回は受講対象がひとつで、曜日を分けるだけなのでさほど難しくありません。
<?php
	
	class LESSON{
	
		/**
		* レッスンを振り分け
		* @param String 「1_12345|2_12345」みたいな文字列
		* @return String 「1_1:2」みたいな文字列
		*/
		public function get($input){
			
			// 入力を分解
			foreach(explode('|', $input) as $val){
				$tmp = explode('_', $val);
				$arr[$tmp[0]] = $tmp[1];
			}
			
			// レッスンを埋める
			$list = $this->fillLesson([], $arr, 0);
			
			// 整形して返す
			ksort($list);
			$list = array_map(function($tmp){
				sort($tmp);
				return implode(':', $tmp);
			}, $list);
			$ret = '';
			foreach($list as $key=>$val){
				$ret .= $key . '_' . $val . '|';
			}
			return substr($ret, 0, -1);
		}
		
		/**
		* レッスンを埋める
		* @param array [希望番号=>[社員]]
		* @param array [社員番号=>希望番号リスト]
		* @param int 第x-1希望
		* @return array $list
		*/
		private function fillLesson($list, $arr, $hope = 0){
			// くるくる
			$nextArr = [];
			foreach($arr as $key=>$val){
				if(!isset($list[$val[$hope]]) || count($list[$val[$hope]]) < 4){
					// 余裕があれば埋める
					$list[$val[$hope]][] = $key;
				}else{
					// 埋まっていれば第二希望に持ち越し
					$nextArr[$key] = $val;
				}
			}
			// 次の希望に持ち越して埋める。希望がなくなれば打ち切り
			if($nextArr && $hope<4){
				return $this->fillLesson($list, $nextArr, $hope+1);
			}
			
			return $list;
		}
		
	}
	
	// 以下はテスト
	$test = [
		['1_12345', '1_1'],
		['30_32451', '3_30'],
		/* 省略 */
	];

	$lesson = new LESSON();
	foreach($test as $key=>$data){
		$answer = $lesson->get($data[0]);
		if($answer !== $data[1]){
			print('えらー');
		}
	}
簡単とか言いながら色々と微妙。
特に整形のあたりはもうちょっとどうにかならなかったものか。
かかった時間は1時間くらい。
PR


2014/05/19 22:19 | Comments(0) | PHP
PHP5.6.0 PHPでFizzBuzz最短56バイト
http://golf.shinh.org/p.rb?FizzBuzz#PHP
PHPのFizzBuzzで最短56バイトを投稿、9位になりました。


以下を読み進める前に、とりあえず前提知識
・php変態文法最速マスター
  http://d.hatena.ne.jp/milieu/20100203
・可変変数
  http://www.php.net/manual/ja/language.variables.variable.php
・文字列のビット演算
  http://perl-users.jp/articles/advent-calendar/2010/sym/2
を見といてください。


PHPでは現状56バイトが最高記録ですが、何故か何処にも公開されていません。
公開されているものでは59バイトが最短のようです。
http://d.hatena.ne.jp/milieu/20100204/1265305089
http://oflow.me/archives/110
http://d.hatena.ne.jp/ts_asano/20130320/1363791538
よく見たら最後のは改行ではなくスペースになってるので間違ってるな。

私はずっと58バイトで詰まっていたのですが、このたびようやく56バイトに成功しました。
<?while($i++<100)echo$i%3?!$$i=$i:Fizz,$i%5?$$i:Buzz,~ ;
<?for(;$i++<100;)echo$i%3?!$$i=$i:Fizz,$i%5?$$i:Buzz,~ ;
まず公開されている59バイトのから始めてみます。
私も当初はこの方向性で考えていたのですが、結局こちらでは58バイト止まりでした。

60バイトまでは、普通に三項演算子とかをいじっていれば減らせます。
そこから"\n"を直接LFにすると1バイト減って59バイトになります。

さて、~の後ろの空白は普通の文字ではないのでたぶん正しくアップできてませんが、chr()ASCIIコード245になる文字です。
この文字を~でビット反転するとASCIIコード10、つまりLFになるので『~ 』は『"\n"』と同じ意味です。
直接LFを書くよりさらに1バイト減らすことができ、該当部分を差し替えるだけで58バイトになります。

しかしこっちはこれで行き止まりなので、!$$iとか書かれてるよくわからんのを見ていきましょう。
とりあえずコードをわかりやすく分解。
<?php
	while($i++<100){
		// $iが3の倍数であれば「Fizz」を表示、倍数でなければ「!$$i=$i」を表示
			echo $i%3 ? !$$i=$i : 'Fizz';
		// $iが5の倍数であれば「Buzz」を表示、倍数でなければ「$$i」を表示
			echo $i%5 ? $$i : 'Buzz';
		
			echo "\n";
	}
「!$$i=$i」ってなんだよ。

まず1行目。
3の倍数だった場合は単にFizzを表示して終わりです。
3の倍数でなかった場合、「$$i=$i」で、$$iに$iを代入しています。
例として$i=4だった場合、$$i = ${4} = 4、つまり${4}という変数に4が代入されます。
これはただの代入なので、「$$i=$i」全体の値は$iになります。
ところで数値を!するとfalseになります。
結果として「!$$i=$i」全体の値はfalseとなり、echoでは何も表示されません。

1行目のまとめは、
・$iが3の倍数であれば「Fizz」を表示する。
・$iが3の倍数でなければ${$i}に$iを代入する。何も表示しない。
です。

次に2行目ですが、
・$iが5の倍数であれば「Buzz」を表示する。
・$iが5の倍数でなければ${$i}を表示する。
です。

5の倍数であれば単にBuzzを表示して終了なので問題ありません。
5の倍数ではない時を見てみましょう。

${$i}は、$iが3の倍数でないときは1行目によって$iが代入されているので、結果として$iが表示されます。
$iが3の倍数だった場合、${$i}は未定義なのでNULLとなり、結果としてなにも表示されません。
例として$i=4の場合、1行目により$$i = ${4} = 4が代入されていたので4となります。
$i=6だった場合、$$i = ${6}は何も代入されていないのでNULLです。

以上をまとめると、
・$iが15の倍数である場合、1行目でFizzが表示され、2行目でBuzzが表示される
・$iが3の倍数であり5の倍数ではない場合、1行目でFizzが表示され、2行目で$$i=NULLなので何も表示されない
・$iが3の倍数ではなく5の倍数である場合、1行目で$$i=$iが実行され、2行目でBuzzが表示される
・$iが3の倍数でも5の倍数でもない場合、1行目で$$i=$iが実行され、2行目で$$i=$iが表示される

となりFizzBuzzの定義を満たしました。
めでたし。

ということで次は55バイトなわけですが、さすがに世界中誰も成功してないから無理かな?


2014/05/17 17:18 | Comments(0) | PHP
PHP5.5 PECL::BitSet
PECLに置いてあったBitSet
日本語はおろか英語ドキュメントすらもまったく見つからず、ソースとテストコードくらいしかないみたい。
よくわからないので使ってみます。
<?php
	$bitset = new BitSet();
	$bitset->set(1);
	print($bitset); // 0100000000000000000000000000000000000000000000000000000000000000
おっと、これ、もしかしてビット演算を簡単にやってくれる便利クラスじゃね?

<?php
	$bitset = new BitSet();
	$bitset->set(1);
	$bitset->set(3);
	$bitset->set(5);
	print($bitset);   // 0101010000000000000000000000000000000000000000000000000000000000
	$bitset->set(64); // Uncaught exception 'OutOfRangeException'
	$bitset->set(0);  // 全部1になる
	print($bitset);   // 1111111111111111111111111111111111111111111111111111111111111111
	$bitset->clear(); // クリア
	print($bitset);   // 0000000000000000000000000000000000000000000000000000000000000000
2進数じゃなくてただのフラグじゃねーか!
64桁も取ってるのに、たったの64ビットしかフラグ管理できません。
役に立たなすぎる。
あと0ビット目だけを操作する方法がありません。

<?php
	$bitset = new BitSet(8); // 8桁に設定
	$bitset->set(1);$bitset->set(2);$bitset->set(3); // メソッドチェーン非対応 01110000
	$bitset2 = BitSet::fromArray([1, 3, 5, 7]); // 01010101
	
	$bitset->andOp($bitset2);    // AND 01010000
	$bitset->orOp($bitset2);     // OR 01110101
	$bitset->xorOp($bitset2);    // XOR 00100101
	$bitset->andNotOp($bitset2); // $bitsetは1、$bitset2は0のときに1 00100000
	
	var_dump($bitset->size());        // BitSetの桁数。 8
	var_dump($bitset->length());      // BitSetを表せる最小桁数? 01010000なら4
	var_dump($bitset->cardinality()); // 1が立ってる数。 
	var_dump($bitset->isEmpty());     // 全て0のときだけtrue
	var_dump($bitset->get(1));        // 引数桁が1ならtrue
andOp()やorOp()など一見便利そうなのですが、どれも返り値がNULLでメソッドチェーン非対応のため、とても使いにくいです。
ビット管理がしたいからといって、正直わざわざBitSetを使うメリットは全く感じられませんでした。


2014/04/25 23:46 | Comments(0) | PHP
PHP5.6.0 「第17回オフラインリアルタイムどう書くの参考問題」をPHPで解く
http://nabetani.sakura.ne.jp/hena/ord17scheherazade/
http://qiita.com/Nabetani/items/dabe8ec57e0313229552

10進数の数値を別の進数になおしたら、その数が回文数になることがある。
そうなる場合の基数を列挙せよ。
ちょっと考えただけで計算量が凄いことになりそうだが大丈夫だろうか。
<?php

	class SCHEHERAZADE{
	
		/**
		* 回文基数
		* @param String 「17301」みたいな文字列
		* @return String 「5,38,100,218,236,5766,17300」みたいな文字列
		*/
		public function get($input){
			$input = (int)$input;
			$ret = [];
			
			// 2からinput-1進数
			for($i=2;$i<$input;$i++){
				// 進数表現を取得
				$arr = $this->baseConvert($input, $i);
				// 逆順と等しければ回文
				if($arr === array_reverse($arr)){
					$ret[] = $i;
				}
			}
			// 答えがなければ-
			return $ret ? implode(',', $ret) : '-';
		}
		
		/**
		* $base進数を求める
		* @param int 元の数
		* @param int 基数
		* @return array ex:数字が1001、基数が25のとき、[1, 15 ,1]
		*/
		function baseConvert($num, $base){
			$result = [];
			while($num > 0){
				$result[] = $num % $base;
				$num = floor($num / $base);
			}
			return $result;
		}
	}
	
	// 以下はテスト
	$test = [
		['17301', '5,38,100,218,236,5766,17300'],
		['2', '-'],
		['1', '-'],
		['3', '2'],
		['4', '3'],
		['5', '2,4'],
		['6', '5'],
		['10', '3,4,9'],
		['101', '10,100'],
		['1001', '10,25,76,90,142,1000'],
		['10001', '10,24,30,42,80,100,136,10000'],
		['1212', '22,100,201,302,403,605,1211'],
		['123412', '62,100,205,215,30852,61705,123411'],
		['5179', '5178'],
		['4919', '4918'],
		['5791', '5790'],
		['5498', '2748,5497'],
		['453', '150,452'],
		['134', '66,133'],
		['8489', '27,652,8488'],
		['1234', '22,616,1233'],
		['5497', '41,238,5496'],
		['4763', '19,35,432,4762'],
		['3974', '17,27,1986,3973'],
		['3521', '44,55,502,3520'],
		['5513', '20,38,53,148,5512'],
		['8042', '23,29,60,4020,8041'],
		['7442', '37,60,121,3720,7441'],
		['4857', '25,1618,4856'],
		['22843', '49,69,91,141,430,22842'],
		['194823', '84,121,21646,64940,194822'],
		['435697', '160,169,235,626,1822,435696'],
		['142', '3,7,70,141'],
		['886', '5,14,442,885'],
		['3102', '7,65,93,140,281,516,1033,1550,3101'],
		['17326', '11,28,99,105,8662,17325'],
		['32982', '13,72,238,477,716,1433,5496,10993,16490,32981'],
		['36', '5,8,11,17,35'],
		['37', '6,36'],
		['251', '8,250'],
		['252', '5,10,17,20,27,35,41,62,83,125,251'],
		['253', '12,14,22,252'],
		['6643', '2,3,9,81,90,510,948,6642'],
		['5040', '71,79,83,89,104,111,119,125,139,143,167,179,209,239,251,279,314,335,359,419,503,559,629,719,839,1007,1259,1679,2519,5039'],
		['9240', '23,38,62,104,109,119,131,139,153,164,167,209,219,230,263,279,307,329,384,419,439,461,615,659,769,839,923,1154,1319,1539,1847,2309,3079,4619,9239'],
	];

	$scheherazade = new SCHEHERAZADE();
	foreach($test as $key=>$data){
		$answer = $scheherazade->get($data[0]);
		if($answer !== $data[1]){
			print('えらー');
		}
	}
わりと大丈夫だった。
作成30分、実行0.2秒。
まあ高速化とかは何も考えてないけど充分でしょう。


2014/04/14 23:19 | Comments(0) | PHP
PHP5.6の新機能
いつのまにかPHP5.6への移行ガイドができていたので、新機能を斜め読みしてみます。

■ Constant scalar expressions

オブジェクト定数に表現が使えるようになりました。
<?php
	const A = 1;
	
	class HOGE{
		const B = A + 100; // 101
		const C = self::B + 10; // 111
		const D = intval(1); // Parse error: syntax error
	}
ただし変数/メソッド呼び出しはできない模様。
どうせならそこまで対応してほしかったところ。

■ Variadic functions via ...

関数の引数に可変長引数が使えるようになりました。
これまでも可変長引数はfunc_get_args()で使えましたが、こいつは引数に明記されないからわかりにくいし、IDEにも捉えてもらえませんでした。
<?php
	function hoge($arg1, $arg2, ...$args){}
	
	hoge(1, 2);          // $args = []
	hoge(1, 2, 3, 4, 5); // $args = [3, 4, 5]
固定の引数に入りきらない部分が、可変長引数に配列で入ってきます。
これは便利。

■ Argument unpacking via ...

関数の呼び出しにも...が使えるようになりました。
<?php
	function fuga(...$args){}
	
	fuga(1, [2, 3]);         // $args = [1, [2, 3]]
	fuga(1, ...[2, 3]);      // $args = [1, 2, 3]
	fuga(...[1], ...[2, 3]); // $args = [1, 2, 3]
配列を...付きで渡すと、引数を展開してから渡してくれます。
こちらはいったいどういうメリットがあるのかよくわかりません。

■ Exponentiation via **

累乗演算子です。

■ use function and use const

関数や定数を use 文でインポートできるようになりました。
<?php
	namespace HOGE{
		const FUGA = 1;
		function foo(){return __FUNCTION__;}
	}

	namespace{
		use const HOGE\FUGA;
		use function HOGE\foo;
		
		echo FUGA;  // 1
		echo foo(); // HOGE\foo
	}

http://www.php.net/manual/ja/language.namespaces.faq.php#language.namespaces.faq.nofuncconstantuse
> 関数や定数は use 文でインポートできない

お、おう。

■ phpdbg

デバッガとしてphpdbgが同梱されます。
ブレークポイントやステップ実行などの強力なデバッグ機能が使用可能です。
が、今のところコマンドラインツールしかないみたいなので、Eclipseのデバッグウィンドウみたいに扱えるIDEの出現が望まれるところ。
ひとつ見つけたのですが日本語ではないのでよくわかりません。

■ Default character encoding

デフォルトの文字コードがようやくUTF-8になりました。

■ php://input is reusable

いまいち意味がよくわかりませんが、php://inputを何度呼び出してもメモリを食いつぶさなくなったってこと?

■ Large file uploads

2ギガバイト以上のファイルをアップロードできるようになりました。
まあ普通はそんなのupload_max_filesizeで止めますが。

■ GMP supports operator overloading

GMPの演算子オーバーロードです。

■ hash_equals() for timing attack safe string comparison

hash_equals()関数のマニュアルはまだ無いようです。
ソースを見ると、どうもふたつの文字列が同じものかを調べてる、つまり===にしか見えないのですが、いったい何の機能があるのでしょう。

どうやらタイミング攻撃を防ぐもののようです。

100文字のパスワードをチェックする機構があったとして、
===は「文字列の長さが違うからすぐにエラーを返す」ときと「1文字目が違うからエラーを返す」ときと「100文字目が違うからエラーを返す」ときで処理にかかる時間がほんのわずかに違います。
その時間の違いからパスワードが何文字か推定できて、何文字目が違うか推定できて、結果として全探索より遙かに少ない回数で正解パスワードまで辿り着けてしまうということらしいです。
正直そんなの可能なのか?と思えるのですが、実際やってみた人が現れ、情報セキュリティスペシャリストの問題にも出てくるくらいになっています。

そこでhash_equals()は、途中の文字が違っても最後まで比較を行うことで、実行にかかる時間を一定にするようにしています。
でも文字列長が違ったら即座にFALSEを返してるように見えるんだがどうだろう。

■ gost-crypto hash algorithm

RFC4357で定義されているGOSTというアルゴリズムに対応したということですが、GOSTが何なのかよくわかりません。

■ SSL/TLS improvements

[SSLの機能](http://www.php.net/manual/ja/migration56.openssl.php)が色々追加になりました。
よくわかりませんが。


ついでに廃止となった機能

■ Calls from incompatible context

インスタンス内でstaticでないメソッドをstatic呼び出しするとE_DEPRECATEDが発生します。
<?php
	class A {
		public function foo() {}
	}
	class B {
		public function bar() { A::foo(); }
	}
	(new B)->bar(); // Deprecated: Non-static method A::foo() should not be called statically
	A::foo(); // Strict Standards: Non-static method A::foo() should not be called statically
どうして違うエラーになってるのかさっぱりだ。

■ $HTTP_RAW_POST_DATA and always_populate_raw_post_data

always_populate_raw_post_dataを有効にするとE_DEPRECATEDが発生します。
かわりに php://input を使えということらしいです。


正直、これは是非とも5.6に上げなければ、というような機能はあまりないように思えます。
一番魅力的なのはphpdbgだと思いますが、これはPHP5.4以降個別に入れられたりします。
まあChangeLogにはFixedがたくさん並んでいて、完成度を高める方向に向かっていると考えるといいことなのではないでしょうか。


2014/04/11 23:06 | Comments(0) | PHP
PHP5.6.0 「第17回オフラインリアルタイムどう書くの問題」をPHPで解く
http://nabetani.sakura.ne.jp/hena/ord17foldcut/
http://qiita.com/Nabetani/items/ebd9d7deb30c57447806

折って切る、何処かで見たと思ったら第18回の参考問題に似ている。
なので同じように考えれば解けるかな?
<?php

	class FOLDCUT{
	
		/**
		* 折って切る
		* @param String 「RRTRB-bl」みたいな文字列
		* @return int 「6」みたいな数値
		*/
		public function get($input){
			// 入力値を分解
			$input = explode('-', $input);
			$loop = str_split(strrev($input[0]), 1);
			
			// 初期値を作成
			switch($input[1]){
				case 'tl': $data = ['01', '11']; break;
				case 'tr': $data = ['10', '11']; break;
				case 'bl': $data = ['11', '01']; break;
				case 'br': $data = ['11', '10']; break;
			}
			
			// 展開する
			foreach($loop as $key=>$val){
				$data = $this->{'extract'.$val}($data);
			}
			
			// 穴を数えて返す
			$count = $rowflg = $colflg = 0;
			$collen = strlen($data[0]);
			foreach($data as $row=>$line){
				// 穴は必ず2*2になるので、右上でカウントしたら下の行は飛ばす
				if($rowflg){ $rowflg=0;continue; }
				$colflg = 0;
				for($col=0;$col<$collen;$col++){
					// 空があった
					if(!$line[$col]){
						// 最上段/最下段は穴にならない
						if(!$row || !isset($data[$row+1])){
							$rowflg++;
							continue;
						}
						// 次が1であればカウント
						if($colflg && isset($line[$col+1]) && $line[$col+1]){
							$count++;
							$rowflg++;
							continue;
						}
					// 空がなかった
					}else{
						$colflg++;
					}
				}
			}
			return $count;
		}
		
		// 右半分を手前に折って、左半分に重ねる
		private function extractR($data){
			return array_map(function($val){
				return $val . strrev($val);
			}, $data);
		}
		
		// 左半分を手前に折って、右半分に重ねる
		private function extractL($data){
			return array_map(function($val){
				return strrev($val) . $val;
			}, $data);
		}
		
		// 上半分を手前に折って、下半分に重ねる
		private function extractT($data){
			return array_merge(array_reverse($data), $data);
		}
		
		// 下半分を手前に折って、上半分に重ねる
		private function extractB($data){
			return array_merge($data, array_reverse($data));
		}
		
	}
	
	// 以下はテスト
	$test = [
		['RRTRB-bl', '6'],
		['R-tr', '0'],
		['L-br', '0'],
		['T-tl', '0'],
		['B-tl', '0'],
		['BL-br', '0'],
		['LB-tl', '0'],
		['RL-tl', '0'],
		['BL-tl', '0'],
		['TL-bl', '0'],
		['RT-tr', '1'],
		['TRB-tl', '0'],
		['TRL-bl', '0'],
		['TRB-br', '2'],
		['LLB-bl', '2'],
		['RTL-tr', '1'],
		['LBB-tr', '0'],
		['TLL-tl', '2'],
		['RLRR-tr', '0'],
		['BBTL-tl', '4'],
		['TBBT-tr', '0'],
		['LLBR-tl', '0'],
		['LBRT-tl', '2'],
		['RLBL-bl', '4'],
		['BRRL-br', '3'],
		['TBBTL-tl', '8'],
		['TLBBT-br', '0'],
		['LRBLL-br', '7'],
		['TRRTT-br', '6'],
		['BBBLB-br', '0'],
		['RTTTR-tl', '4'],
		['BBLLL-br', '6'],
		['RRLLTR-tr', '16'],
		['TTRBLB-br', '8'],
		['LRBRBR-bl', '14'],
		['RBBLRL-tl', '8'],
		['RTRLTB-tl', '12'],
		['LBLRTR-tl', '14'],
		['RRLTRL-tl', '16'],
		['TBLTRR-br', '12'],
		['TTTRLTT-bl', '30'],
		['TBBRTBL-tr', '15'],
		['TRTRTLL-tr', '28'],
		['TLLRTRB-tr', '24'],
		['RLLBRLB-tr', '15'],
		['LTLRRBT-tr', '32'],
		['RBBRBLT-br', '21'],
		['LLRLRLR-tr', '0'],
	];

	$foldcut = new FOLDCUT();
	foreach($test as $key=>$data){
		$answer = $foldcut->get($data[0]);
		if($answer !== (int)$data[1]){
			print('えらー');
		}
	}
解けませんでした。
展開図を作るところまでは同じなので一瞬だったのですが、そこから穴の数をどうやって数えるのかというところで詰まった。
結局今回の展開図の特徴を用いた超力業で解きました。
かかった時間は1.5時間くらい。

絶対なんかもっとまともな数え方があるはず。
というか図にせずに計算する方法があるはず。


2014/04/07 23:05 | Comments(0) | PHP
なりせなるてず 「PHPでXMLをパースして中身全てを連想配列にする」の感想
http://ichiy.hatenablog.com/entry/2014/03/28/203340

いや、SimpleXMLElementのままのほうが扱いやすいだろ。
というのはともかく、xml2arr()が無駄にややこしいので簡略化してみます。
public static function xml2arr($xmlobj){
    $arr = array();
    if (is_object($xmlobj))
    {
      $xmlobj = get_object_vars($xmlobj);
    }
    else
    {
      $xmlobj = $xmlobj;
    }

    foreach ($xmlobj as $key => $val)
    {
      if (is_object($xmlobj[$key]))
      {
        $arr[$key] = self::xml2arr($val);
      }
      else if (is_array($val))
      {
        foreach($val as $k => $v)
        {
          if (is_object($v) || is_array($v))
          {
            $arr[$key][$k] = self::xml2arr($v);
          }
          else
          {
            $arr[$key][$k] = $v;
          }
        }
      }
      else
      {
        $arr[$key] = $val;
      }
    }
    return $arr;
  }

  public static function xml2arr2($xmlobj){
    return json_decode(json_encode($xmlobj), true);
  }
1行で終わりました。
出力は全く同じです。
属性と要素を両方指定したら属性が無視されるところまで同じです。

ちなみにこれ、実はマニュアルのコメントに記載されています。

PHPでちょっとやりたいことってのはだいたい誰かが先にやっているから、先にそういうのを探してみるのがよいでしょう。

どうでもいいけどこのページ、<scirpt defer>の指定がおかしいせいで正常に動いていませんね。


2014/04/04 22:38 | Comments(0) | PHP
PHP5.6.0 「第18回オフラインリアルタイムどう書くの参考問題」をPHPで解く
http://nabetani.sakura.ne.jp/hena/ord18mafovafo/
http://qiita.com/Nabetani/items/373105e7fafd12f5e9fd

山折り谷折りした後、折り目がどうなっているかを求める問題。
この手の図形問題って苦手なんですよね。
裏返しになったところを折ったらどうなるってあたりで頭がこんがらがる。

とか思ってたけど、実際に作り始めてみたらわりと一瞬で終わった。
かかった時間は20分程度。
<?php
	
	class MAFOVAFO{
		/**
		* 山折り谷折り
		* @param String 「JZ」みたいな文字列
		* @return String 「mVVmV」みたいな文字列
		**/
		public function get($input){
			$str = '';
			for($i=strlen($input)-1; $i>=0; $i--){
				switch($input[$i]){
				case 'J':
					$str = $str . 'V' . $this->reverse($str);
					break;
				case 'L':
					$str = $this->reverse($str) . 'V' . $str;
					break;
				case 'Z':
					$str = $str . 'm' . $this->reverse($str) . 'V' . $str;
					break;
				case 'S':
					$str = $str . 'V' . $this->reverse($str) . 'm' . $str;
					break;
				case 'U':
					$str = $this->reverse($str) . 'V' . $str . 'V' . $this->reverse($str);
					break;
				}
			}
			return $str;
		}
		
		// 反転する
		private function reverse($input){
			// 文字列を逆に
			$input = strrev($input);
			// mVを逆に 一括でm←→Vができない
			$input = str_replace(['m', 'V'],['t', 'm'], $input);
			$input = str_replace('t','V', $input);
			return $input;
		}
		
	}
	
	// 以下はテスト
	$test = [
		['JZ', 'mVVmV'],
		['J', 'V'],
		['L', 'V'],
		['Z', 'mV'],
		['U', 'VV'],
		['S', 'Vm'],
		['JL', 'VVm'],
		['JS', 'VmVVm'],
		['JU', 'VVVmm'],
		['LU', 'mmVVV'],
		['SL', 'VVmmV'],
		['SS', 'VmVVmmVm'],
		['SU', 'VVVmmmVV'],
		['SZ', 'mVVmVmmV'],
		['UL', 'mVVVm'],
		['UU', 'mmVVVVmm'],
		['UZ', 'mVVmVVmV'],
		['ZJ', 'VmmVV'],
		['ZS', 'VmmVmVVm'],
		['ZZ', 'mVmmVVmV'],
		['JJJ', 'VVmVVmm'],
		['JJZ', 'mVVmVVmVmmV'],
		['JSJ', 'VVmmVVmVVmm'],
		['JSS', 'VmVVmmVmVVmVVmmVm'],
		['JUS', 'VmVVmVVmVVmmVmmVm'],
		['JUU', 'mmVVVVmmVVVmmmmVV'],
		['JUZ', 'mVVmVVmVVmVmmVmmV'],
		['LJJ', 'VmmVVVm'],
		['LLS', 'VmmVmVVmVVm'],
		['LLU', 'mmmVVVmmVVV'],
		['LLZ', 'mVmmVVmVVmV'],
		['LSU', 'mmVVVmmmVVVVmmmVV'],
		['LSZ', 'mVVmVmmVVmVVmVmmV'],
		['LZL', 'mmVVmVVmmVV'],
		['LZS', 'VmmVmVVmVVmmVmVVm'],
		['LZU', 'mmmVVVmmVVVmmmVVV'],
		['SJL', 'VVmVVmmmVVm'],
		['SLU', 'mmVVVVmmmVVmmmVVV'],
		['SLZ', 'mVVmVVmVmmVmmVVmV'],
		['SSU', 'VVVmmmVVVmmVVVmmmmVVVmmmVV'],
		['SUJ', 'mVVVmVVmmmVmmVVVm'],
		['SUS', 'VmVVmVVmVVmmVmmVmmVmVVmVVm'],
		['SZZ', 'mVmmVVmVVmVmmVVmVmmVmmVVmV'],
		['UJJ', 'VmmVVVmVVmm'],
		['ULU', 'mmmVVVmmVVVVmmmVV'],
		['ULZ', 'mVmmVVmVVmVVmVmmV'],
		['UUU', 'VVmmmmVVVmmVVVVmmVVVmmmmVV'],
		['ZJU', 'VVVmmmVVmmmVVVVmm'],
		['ZLS', 'VmVVmmVmmVmVVmVVm'],
		['ZSJ', 'VVmmVmmVVmmVVVmmV'],
		['ZUJ', 'mVVVmmVmmmVVmVVVm'],
		['JJLJ', 'mVVVmmVVmVVmmmV'],
		['JLJJ', 'VmmVVVmVVmmmVVm'],
		['JLJL', 'VmmVVVmVVmmmVVm'],
		['LJJL', 'VVmmVmmVVVmVVmm'],
		['LLJJ', 'VmmmVVmVVmmVVVm'],
		['SZUS', 'VmVVmVVmmVmmVmmVmVVmVVmVVmVVmmVmmVmmVmVVmVVmVVmmVmmVmmVmVVmVVmmVmmVmmVmVVmVVmVVm'],
		['ULLS', 'VmmVmmVmVVmVVmmVmVVmVVmVVmmVmmVmVVm'],
		['JJJJZJ', 'VmmVVVmmVVmVVmmVVmmmVVmVVmmVVVmmVVmmVmmVVmmmVVmVVmmVVVmmVVmVVmmVVmmmVVmmVmmVVVmmVVmmVmmVVmmmVVm'],
		['JULLLJ', 'mmVmmVVmmmVVmVVVmmVmmVVVmmVVmVVVmmVmmVVmmmVVmVVVmmVmmVVVmmVVmVVmmmVmmVVmmmVVmVVmmmVmmVVVmmVVmVV'],
		['LJJJUL', 'mVVVmVVmmmVVmVVVmmVmmmVmmVVVmVVmmmVmmVVVmmVmmmVVmVVVmVVmmmVVmVVVmmVmmmVVmVVVmVVmmmVmmVVVmmVmmmV'],
		['LJSJJL', 'VVmVVmmVVVmmVmmmVVmVVmmmVVmmVmmVVVmVVmmmVVmmVmmVVVmVVmmVVVmmVmmmVVmVVmmVVVmmVmmVVVmVVmmmVVmmVmm'],
		['LZLLLJ', 'mmVmmVVmmmVVmVVmmmVmmVVVmmVVmVVVmmVmmVVmmmVVmVVVmmVmmVVVmmVVmVVmmmVmmVVmmmVVmVVVmmVmmVVVmmVVmVV'],
		['SJJJJL', 'VVmVVmmVVVmmVmmVVVmVVmmmVVmmVmmVVVmVVmmVVVmmVmmmVVmVVmmmVVmmVmmmVVmVVmmVVVmmVmmVVVmVVmmmVVmmVmm'],
		['ZLJLJL', 'VmmVVVmmVmmmVVmVVmmVVVmVVmmmVVmmVmmVVVmmVmmmVVmmVmmVVVmVVmmmVVmVVmmVVVmmVmmmVVmVVmmVVVmVVmmmVVm'],
	];

	$mafovafo = new MAFOVAFO();
	foreach($test as $key=>$data){
		$answer = $mafovafo->get($data[0]);
		if($answer !== $data[1]){
			print('えらー');
		}
	}
折るのではなく、後ろから開いていく方向で考えた。
たとえばJを開くと、左側はそのまま、中央に谷折りのVが入り、右側は左側のちょうど裏返しになる。
よーしこれは斬新な解き方だぜー、とか思って他の解答を見てみたら、当然みんな普通に使ってた。


2014/03/24 23:11 | Comments(0) | PHP
PHP5.6.0α PHPで累乗根を求める
PHPには立方根など累乗根(冪根)を求める関数がありませんでした。

自作関数がStackOverflowにありましたが、わりとめんどいです。
というかこのNRoot()、NRoot('64','3')に'3'、NRoot('65','3')に'4'、NRoot('66','3')に'5'を返してくるんだがこれ合ってるのか?

PHP5.6では、このような怪しい自作関数に頼る必要はありません。
https://github.com/php/php-src/blob/php-5.6.0alpha3/NEWS

> 23 Jan 2014, PHP 5.6.0 Alpha 1
> Added gmp_root() and gmp_rootrem() functions for calculating nth roots.
<?php
	gmp_root('63', '3'), // 3
	gmp_root('64', '3'), // 4
	gmp_root('65', '3'), // 4
	gmp_rootrem('63', '3'), // 3あまり36
	gmp_rootrem('64', '3'), // 4あまり0
	gmp_rootrem('65', '3')  // 4あまり1
便利。
まあ累乗根なんていつ使うんだよという話ですが。
あとあくまで整数部分だけを返してくる関数なので、小数点以下まで求めたい場合はいったん1000を掛けてから計算してやる必要があります。

ところで、GMP演算の返り値はGMPオブジェクトです。
これまでGMPオブジェクトを扱う場合は、ただの足し算でも
$c = gmp_add($a, $b);
などと書く必要がありました。

PHP5.6ではGMPの演算子オーバーロードもサポートされました。
<?php
	gmp_init(1) + 2; // GMP(2)
	gmp_init(1) + '1234567890123456789012345678901234567890'; // GMP(123456789012345678901234567891)
	1 + '1234567890123456789012345678901234567890'; // float(1.2345678901235E+29)
GMPオブジェクトに対して演算を行った場合、GMPとして透過的に取り扱ってくれます。
もうgmp_add(gmp_add(gmp_add(gmp_add(gmp_init(1), 2), 3), 4), 5);なんて書かずにすみます。

もうひとつ数学関連の機能追加がありました。

> 13 Feb 2014, PHP 5.6.0 Alpha 2
> Added T_POW (**) operator
<?php
	print(5 ** 3); // 125
	print(4 ** 3 ** 2); // 262144
	print(gmp_init(5) ** 3); // GMP(125)
累乗演算子が追加になった模様。
こちらはpow()やgmp_pow()があるのであまり恩恵はない気もしますが、上記演算子オーバーロードと合わせるとその威力は歴然。

マニュアルの演算子の項目にはまだ載っていません。
優先順位は「*/%」のひとつ上で、かなり優先度高めでした。

これでPHPでも実用的な数学計算が行えますね。
まあそういうのはFORTRANやMATLABとかに任せとけよって話ですが。


2014/03/21 23:03 | Comments(0) | PHP
OREDON『今更phpでオブジェクト指向プログラミング応用その3 BBSを作る』の感想
http://oredon.yuipyong.com/blog/2014/03/phpbbs.php

とりあえず「,」が勝手に削除される残念仕様。
せっかくfputcsv()やらserialize()やらがあるんだから、自力パースなんぞせずに標準関数に任せるべき。

さて、本題はここからで、このフォームにはXSSが存在します。
『res=1&parent_no=<script>alert("a");</script>』
をPOSTすると見事にアラートが出ます。
まあChromeだとXSS Auditorに止められますが。
何なんですがねXSS Auditor。
こんなのがあると却って危ない気がしますがどうでしょう。

原因はsprintfの引数に%sを使っているせいですが、もっと根本的な原因としては出力を一切エスケープしていないせいです。
ん…?一切エスケープしていない?

普通にフォームに『<script>alert("a");</script>』って入れて投稿するだけでXSSになりました。
こっちはChromeでも出ます。
これはひどい。

もうひとつ深刻な問題があって、
『del=1&no=0』をPOSTすると全投稿が消えます。
実際は返信についてだけはデータ上に残っているのですが、表示されることはありません。
これは、全ての親記事が内部的にはNo0への返信という形式になっているためです。
削除ボタンは、自分の記事番号もしくは親の記事番号がリクエスト値であれば削除する、という処理になっています。
そのため削除する記事番号に0を入れると全投稿が消えます。

まあ、そもそもそれ以前に認証の一切存在しない削除ボタンなので、いくらでも好きに消せてしまうのですが。


http://oredon.yuipyong.com/blog/2014/02/phptweet.php

> header('Location: ' . $_SERVER['PHP_SELF']);

あかん。

テキストファイルに保存する段階でhtmlspecialchars()しているのもおかしいですね。
エスケープは使用する直前です。
htmlspecialchars()で変形する前のテキストが欲しいなんてことになった場合、いちいちhtmlspecialchars_decode()する羽目になります。

あと$_COOKIEについては、個人的には一切使用する必要のない変数だと思っています。
ほぼ全ての状況において$_SESSIONで代替できますし、そのほうがより安全です。


http://oredon.yuipyong.com/blog/2014/02/php-2.php

特にセキュリティ的な問題は無いように思えますが、カウンター程度で3回もfopenするのはあんまり気持ちよくないな。


2014/03/17 23:27 | Comments(0) | PHP

<<前のページ | HOME | 次のページ>>
忍者ブログ[PR]