Net_Pingの現時点での最新バージョン、2.4.3にはバグがあります。
http://pear.php.net/manual/ja/package.networking.net-ping.php
Net_Pingは指定したホストに対してpingを撃ってくれるPearパッケージですが、
そのメソッドのひとつ、checkHostはホストの生死判定を行います。
が、このメソッドを呼び出した場合、必ず下記のエラーが発生します。
Fatal error: Cannot use object of type Net_Ping_Result as array in Ping.php on line 425
該当部分は以下
preg_match_all('|\d+|', $res[3], $matches)
この$retはPear::pingの返り値なのですが、これはオブジェクトとして返ってきます。
オブジェクトに対して$ret[3]なんて指定は当然出来ませんのでエラーになります。
ロードマップには次回の更新予定が掲載されています。
http://pear.php.net/bugs/roadmap.php?package=Net_Ping&roadmapdetail=2.4.4#a2.4.4
>Windows XP
>Function CheckHost fails with error every time.
え?Windowsだけ?
それよりも2.4.3から一年も放置のままというのはどうなんだ。
それにしてもこのパッケージ、バージョン情報が
// $Id: Ping.php,v 1.47 2007/12/28 04:30:42 cconstantine Exp $
とかしか書いてないのだが本当に2.4.3?
エラーになる超簡単なサンプルコード。
ping.php
1
2
3
4
|
require_once "Net/Ping.php";
$ping = Net_Ping::factory();
$ping->checkHost('www.google.co.jp');
|
XMLが何なのかについては未だにさっぱりわかりませんが、PHPにおいてのXMLの役割ははっきりしていて、まあ要するに単なるタグ付きのテキストです。
XMLを利用したXML-RPCというサービスがあり、これはXML文書をHTTPで送りつけることによって$_REQUESTより複雑な内容を送受信したりするサービスのことです。
そんなわけでXML-RPCのようなサービスにXML文書を送りつけたり、逆に送りつけられたXML文書を解析したりするのがPHPでのXMLの主な使い方になるでしょう。
特に意味はありませんがXML-RPCサービスでググると一番に出てくる郵便番号チェックを行うクライアントを作ってみましょう。
http://yubin.senmon.net/service/xmlrpc/
yuubin.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
//XML
$yuubin_xml='
<methodCall>
<methodName>yubin.fetchAddressByPostcode</methodName>
<params>
<param>
<value><string>1000002</string></value>
</param>
</params>
</methodCall>
';
//URL
$yuubin_href='http://yubin.senmon.net:80/service/xmlrpc/';
//HTTPリクエスト作成
$yuubin_context = stream_context_create(
array(
'http' => array(
'method' => "POST",
'header' => "Content-Type: text/xml",
'content' => $yuubin_xml)
)
);
//XML-RPC
$ret=file_get_contents($yuubin_href, false, $yuubin_context);
print("<pre>");var_dump($ret);die();
|
あっさり成功。
簡単すぎるだろ。
レスポンスは同様にXMLで返ってきますので、こちらを解析してみましょう。
XMLツールとしては、DOM、SAX、SimpleXML、XMLReader、XSLT等々がありますがどれ使えばいいんだよってかどう違うんだこれ。
http://jp2.php.net/manual/ja/refs.xml.php
最も簡単なのはsimplexml_load_stringで、XMLをそのまま突っ込むだけで配列になります。
http://jp2.php.net/manual/ja/function.simplexml-load-string.php
ここではXMLパース目的としては一番簡単とされているSimpleXMLを用いてパースしてみます。
リクエスト用XMLはxmlrpc_encode_requestを用いれば面倒なタグを書く必要はありません。
http://jp2.php.net/manual/ja/function.xmlrpc-encode-request.php
yuubin2.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
//XML
$yuubin_xml_method='yubin.fetchAddressByPostcode';
$yuubin_xml_params=array('1000002');
$yuubin_xml=xmlrpc_encode_request($yuubin_xml_method,$yuubin_xml_params);
//URL
$yuubin_href='http://yubin.senmon.net:80/service/xmlrpc/';
//HTTPリクエスト作成
$yuubin_context = stream_context_create(
array(
'http' => array(
'method' => "POST",
'header' => "Content-Type: text/xml",
'content' => $yuubin_xml)
)
);
//XML-RPC
$yuubin_request=file_get_contents($yuubin_href, false, $yuubin_context);
//SimpleXMLElement
$yuubin_sxml=new SimpleXMLElement($yuubin_request);
//XMLの一部を抽出
foreach(
$yuubin_sxml
->params
->param
->value
->array
->data
->value
->struct
->member
as $yuubin_sxml_val){
print($yuubin_sxml_val->name.':'
.$yuubin_sxml_val->value->children()."<br>\n");
}
|
あとは郵便番号を引数にして関数化したり、他のメソッドも実装してクラス化したりするとよいかもしれません。
あと先方でも指摘されているエラー処理も忘れずに。
それにしても->のところはもうちょっとどうにかならないのだろうか。
配列に対するイテレータ、オブジェクトに対するイテレータと来て次はディレクトリに対するイテレータを使ってみます。
http://docs.php.net/manual/ja/class.directoryiterator.php
$dir=new DirectoryIterator(dirname(__FILE__));
$dir->rewind();
$filename=$dir->current();
var_dump($filename);
object(DirectoryIterator)#2 (0) { }
NO!何だこれ!
DirectoryIterator::current これ自身を返す (Iterator インターフェースに必要)
http://docs.php.net/manual/ja/directoryiterator.current.php
これまでの動作から言うとcurrentメソッドは現在のファイル名を返すべきであるのに、何故かインスタンス自身が返されます。
あと何気にnextで'.'や'..'、ディレクトリとファイルが混ざってやってきたりして面倒です。
ファイルのみを選び出すラッパクラスを作成しましょう。
DirectoryIteratorX.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
class DirectoryIteratorX extends DirectoryIterator{
//次のファイルを取得
function nextFile(){
while($this->valid() && !$this->isFile()){
$this->next();
}
}
//次のディレクトリを取得
function nextDir(){
while($this->valid() && !$this->isDir()){
$this->next();
}
}
//現在のファイルを取得
function current(){
return $this->getFilename();
}
}
$dir=new DirectoryIteratorX(dirname(__FILE__));
$dir->nextFile();
$dir->current();
|
DirectoryIteratorクラスやSplFileInfoクラスはファイルに関する情報を扱うので、currentやnextといった基本以外のメソッドが大量に揃っています。
といっても作成、更新日時や読み書き属性等を拾ってくるようなメソッドばかりなので、触ってみればすぐにわかります。
手元のWindows環境では$dir->getPerms()で33206が返ってくるのですが、これは8進数にすると100666となり、パーミッション0666ということらしいです。
10は何?
new SplDoublyLinkedList();
Fatal error: Class 'SplDoublyLinkedList' not found
お?
spl_classes()で自分の環境に入っているSPL関連クラスを調べることが出来ます。
試してみたところ、何故かデータ構造関連のクラスが一切入っていませんでした。
何処にも書かれていませんが、どうやら現時点では開発中のPHP5.3以降でないと使用できないようです。
それ以外は入っていたのですが、SplDoublyLinkedListのかわりなのかなんなのかSplObjectStorageとかいうのが入っている。
見慣れないというかマニュアルに載っていません。
どうやらPHP6で実装されるクラスのようですが、何故か5.2.5の時点で既に存在しています。
とりあえず触ってみます。
splobjectstorage.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
//適当なクラスを作成
class testClass1{}
class testClass2{}
$a=new testClass1(0);
$b=new testClass1(5);
$c=new testClass2('x');
//SplObjectStorageに代入
$st=new SplObjectStorage();
$st->attach($a);
$st->attach($b);
$st->attach($c);
//SplObjectStorageの挙動
$st->count();//オブジェクト数=3
$st->contains($b); //$bが含まれているのでtrue
$st->contains(new testClass1(5)); //$bと違うインスタンスなのでfalse
$st->current(); //null
$st->rewind(); //$stのポインタ初期化
$st->key(); //$aの順番=0
$st->current(); //$a
$st->next();
$st->key(); //$bの順番=1
$st->current(); //$b
$st->next();
$st->valid(); //3番目に$cが存在するのでtrue
$st->next();
$st->valid(); //4番目は存在しないのでfalse
$st->detach($b); //$bを削除
$st->count(); //オブジェクト数=2
|
前回行った配列に関するイテレータと同じように、SplObjectStorageはオブジェクトに対してイテレータを適用するためのクラスのようです。
newの部分以外は、配列に関するイテレータとまったく同じように扱うことが出来ます。
素晴らしいですね。
最大の問題点は、使い道がよくわからないというところでしょうか。
ソースはこちらに載っているようですが、
http://www.php.net/~helly/php/ext/spl/splobjectstorage_8inc-source.html
これってPHPで実装されてるのか?
privateなインスタンス変数にアクセスした際、普通はCannot access private propertyエラーになるのだが、
$st->indexはUndefined propertyになるので、少なくともそのままの実装ではないような気がする。
「配列やそれに類似するデータ構造の各要素に対する繰返し処理の抽象化である」
といわれても何のことだかさっぱりです。
PHPでは、配列にアクセスする際はforeachなりnextなりを使用し、
ディレクトリにアクセスする際はopendirやreaddirを使用し、
XMLを解析する場合はxml_set_element_handlerなりxml_set_character_data_handlerを使用したりするわけですが、これでは処理する相手が増えるたびに新しい関数を覚えなくてはいけなかったりして恐ろしく面倒です。
というわけでIteratorパターンなるものが編み出されました。
データ構造からデータを取得する手段を切り離すということです。
これを使用することによって、$iterator_array->next()で配列の次の値を取得し、$iterator_dir->next()で次のファイル名を取得し、$iterator_xml->next()でXMLの次の要素を取得できるようになるわけです。
とりあえず配列について簡単にIteratorを作成してみます。
例によってエラー処理は放置。
iterator_array.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
class Iterator_array{
public $array=array(
'<',
'<',
'<',
'!"#%&',
'\''
);
function __construct(){
$this->position=0;
}
public function current(){
return $this->array[$this->position];
}
public function key(){
return $this->position;
}
public function next(){
$this->position++;
}
public function rewind(){
$this->position=0;
}
public function seek($position){
$this->position=$position;
}
public function valid(){
return isset($this->array[$this->position]) ? true : false;
}
public function count(){
return count($this->array);
}
#↓クラスのおわり
}
|
index.php
1
2
3
4
5
6
7
8
9
10
|
require_once('iterator.php');
$iter=new Iterator_array();
print($iter->current());
$iter->next();
print($iter->current());
$iter->seek(4);
print($iter->current());
|
さて、PHP5においてクラス関連機能が強化されたことに伴い、このように統一した操作を行うIteratorインターフェイスが用意されました。
せっかく自力でIterator_arrayクラスを作成したのに、内容のほぼ同じArrayIterator が実装されて俺涙目。
jQuery 1.2.6
http://jquery.com/
jQueryとは、JavaScriptのライブラリ集のようなものです。
で、これが尋常ではなく便利極まりない。
動的メニューやテキストの動的変更といった、ちょっとした演出が非常に簡単に行えます。
まず公式サイトからファイルをダウンロード。
何種類かありますが全部同じもので、Uncompressedがソース閲覧用、Minifiedが使用するためにコメントやインデントを全て取り払ったものです。
jquery.jsこれひとつがjQueryの全てです。
適当なフォルダに突っ込んだらHTMLから呼び出してみます。
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script src="jquery.js" type="text/javascript"></script>
</head>
<body>
<div class="Class1">DIVでClass1です</div>
<div class="Class2">DIVでClass2です</div>
<p class="Class1">PでtestClassです</p>
<div id="TestID">DIVでtestIDです</div>
<script type="text/javascript">
$("DIV.Class1")
.css("color","red")
.html("DIVのClass1を発見しました")
.animate({fontSize: "2em"},2000 );
$("#TestID")
.css("background","blue")
.css("width","120px")
.css("height","120px")
.css("padding","10px")
.mouseover(function () {$(this).slideUp();});
</script>
</body>
</html>
|
基本は、$の後にタグ.class、もしくはタグ#idで対象を見つけ出し、それに対して行いたいことを.で繋げて書いていくだけです。
色々なメソッドがあるのですがとりあえずスクリプトを別ファイルにしましょう。
ちょっとだけ特殊な書き方が必要で、
index.html
<script src="index.js" type="text/javascript"></script>
index.js
$(document).ready(function(){
//ここにメソッド
});
これで分離できます。
あとは適当にidなりclassなりの単位でメソッドに突っ込めばいいのですが、中にはAjaxでファイルを動的に取得するメソッドも存在します。
かつてこんなに長々とソースを書いていたものが、
$("#id").click(function(){$(this).load("sample.txt");});
だけで終わっちゃうとかもうねどうなのよこれ。
再帰的処理とは、一言でいうと自分自身を呼び出す関数です。
何が便利って連想配列全部に処理を行いたい場合などですかね。
連想配列に入ってきた文字列全てにhtmlspecialcharsを行う関数htmlspecialchars_arrayを作ってみましょう。
index.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
//連想配列
$input=array(
'1'=>'abc',
'2'=>'<>',
'3'=>'#$%'
);
$output=htmlspecialchars_array($input);
var_dump($output);
//配列にhtmlspecialcharsする関数
function htmlspecialchars_array($array){
foreach($array as $key=>$val){
$ret[$key]=htmlspecialchars($val);
}
return $ret;
}
|
簡単にできました。
が、この関数、たとえば
$input=array(array('1'=>'abc'));
なんてものが入ってくるとあっさりエラーになってしまいます。
こういう場合に威力を発揮するのが再帰的処理です。
たとえばXMLは入れ子が凄いことになっているわけですが、再帰的処理を使わずにあれを解析しようとするととんでもない労力が必要になります。
とりあえずhtmlspecialchars_arrayをパワーアップさせてみましょう。
recursive.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
//連想配列
$input=array(
'1'=>'abc',
'2'=>'<>',
'3'=>array(
'01'=>'aaa',
'02'=>'><><'
)
);
$output=htmlspecialchars_array($input);
var_dump($output);
//配列にhtmlspecialcharsする関数
function htmlspecialchars_array($input){
if(is_array($input)){
foreach($input as $key=>$val){
$ret[$key]=htmlspecialchars_array($val);
}
return $ret;
}
$ret=htmlspecialchars($input);
return $ret;
}
|
関数htmlspecialchars_arrayでは、まず引数が配列かどうかチェックします。
配列であれば配列の中の各値についてさらにhtmlspecialchars_arrayを呼び出します。
引数が配列でなければ、htmlspecialcharsして返します。
これを繰り返した結果、$input内の全ての値に対してhtmlspecialcharsを適用することができます。
このように深さが何段階あるかわからないが、そういうものに対してチェックを行いたい場合に役立ちます。
ディレクトリを順にチェックしたり、XMLを解析したり。
あるいは迷路を解くようなプログラムも作れてしまうかもしれません。
まあPHPでそこまでやる意味はわかりませんが。
array_walk_recursive?
細かいことは気にするな。
ちなみにarray_walk_recursiveを使った場合はこうなります。
array_recursive.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
//連想配列
$input=array(
'1'=>'abc',
'2'=>'<>',
'3'=>array(
'01'=>'aaa',
'02'=>'><><'
)
);
$output=htmlspecialchars_array($input);
var_dump($output);
function htmlspecialchars_array($input){
array_walk_recursive(
$input,
create_function('&$val, $key', '$val=htmlspecialchars($val);')
);
return $input;
}
|
create_functionわかりにくいよcreate_function。
クラスというのはあくまで設計図であり、インスタンス化することによって初めて実体化します。
一旦実体化されたクラスのインスタンスは、当然元のクラスとは別のものです。
一つのクラスから複数のインスタンスを作成しても、それぞれお互いには無関係になります。
一枚の設計図から複数の家を建てても、それぞれの家が無関係なのと同じことです。
例によって例外があります。
今回は例外のひとつ、スタティック変数について扱ってみます。
通常のインスタンス変数がインスタンスに属するのに対し、スタティック変数はクラスに属しています。
設計図の例でいうならば、インスタンス変数は実際に建てられた各家の間取りや床面積等に値します。
ひとつの家の間取りを変更しても、他の家の間取りにはまったく影響しません。
それに対し、スタティック変数は家の設計図そのものをいじくることに値します。
普通の設計図と違うのは、家を建てた後に設計図を変更してもその後に建てる家しか変更されませんが、スタティック変数の変更は全ての家に及ぶことです。
まあ例で説明したところでわかりづらいだけなので、実際に使ってみましょう。
index.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
class test{
//static変数
static $point=0;
function getPoint(){
return self::$point;
}
#↓クラスのおわり
}
$class1=new test();
$val[]=$class1->getPoint();
test::$point=10;
$class2=new test();
$val[]=$class2->getPoint();
$val[]=$class1->getPoint();
print("<pre>");var_dump($val);die();
|
変数宣言の時にstaticを付けることで、その変数はインスタンス変数ではなくスタティック変数になります。
スタティック変数はクラスに属しているため、自分自身のインスタンスを示す$thisではアクセスすることができません。
クラス名::$スタティック変数名という形で直接アクセスします。
最初に$class1->getPointした時点では、$pointは最初のままの0です。
その後self::$point=10と直接スタティック変数$pointを書き換えると、全てのクラスにこの変更が適用されます。
その後newした$class2はもちろん、$class1の$pointも10に変更されてしまいます。
まあ簡単に言うと、クラス内のグローバル変数です。
スタティック変数を使うと何が便利かってSingletonパターンが簡単に作成できます。
singleton.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
//Singletonクラス
class Singleton{
static $single=0;
private function __construct(){}
function getInstance(){
if(!self::$single){
self::$single=new Singleton();
}
return self::$single;
}
#↓クラスのおわり
}
//作成
$a=Singleton::getInstance();
$b=Singleton::getInstance();
//比較
if($a===$b){
print('true');
}else{
print('false');
}
|
普通にnewするとコンストラクタで必ず新しいインスタンスが作成されてしまうので、コンストラクタをprivateにしてnewできないようにしてしまいます。
getInstanceメソッドは、もし既に自分自身が存在していれば自分自身を返します。
その結果何度getInstanceを参照しても、その中身は同じものとなります。
$aと$bという二軒の家を建てたつもりが、実は名前が違うだけで同じ家だったようなものです。
同じ家なので、$a->var=10;したあとに$b->varを参照すると10になっていたりします。
プログラム全体でインスタンスを一つしか作らせたくないようなクラスは、これをextendsして作りましょう。
まあリクエスト一回で終了するPHPにSingletonとかあんまり意味無いような気もしますが。
ログを読み返したら今までずっとインスタンス変数をクラス変数と呼んでいました。
過去のクラス変数はインスタンス変数に脳内変換してください。
前回のカプレカ操作は、各引数を数値として計算しました。
マイナスするだけなので非常に簡単ではあるのですが、PHPの整数演算は大きな桁数を扱うことが出来ません。
その大きさはOSに依存するのですが、一般的な32ビットOSでわずか10桁程度です。
引数を文字列や配列として与えた場合、ほぼ無制限と言えるほど大量のデータでも扱うことが出来ます。
カプレカ操作を文字列や配列を使用して行ってみます。
kaprekar2.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
//配列でカプレカ
//検証する数
if(isset($_REQUEST['k'])){
$seed=$_REQUEST['k'];
}else{
$seed='32799999999999999999999999968';
}
//準備
$i=0;
$result_array=array();
$result_array[0]=$seed;
while(1){
//カプレカ
$tmp=kaprekar($result_array[$i]);
//返り値が既に存在すれば終了
if($a=array_search($tmp,$result_array)){
$result_array[$i+1]=$tmp.'='.$a;
break;
}
$result_array[$i+1]=$tmp;
$i++;
}
print("<pre>");print_r($result_array);die();
//一回のカプレカ
function kaprekar($seed){
//繰下がり用の変数
$borrow=0;
//配列に分割
$seed_array=str_split($seed);
//小さい順に並べる
rsort($seed_array);
$seed_max=count($seed_array)-1;
//差を計算
for($i=$seed_max;$i>=0;$i--){
//各桁の値
$ret_array[$i]=$seed_array[$i]-$seed_array[$seed_max-$i]-$borrow;
$borrow=0;
//繰下がり
if($ret_array[$i]<0){
$ret_array[$i]+=10;
$borrow=1;
}
}
//文字列として返却
ksort($ret_array);
return implode('',$ret_array);
}
|
前回の引数は数値でしたが、今回は文字列です。
それ以外はfunction kaprekar()以外は全く同じです。
今回のkaprekar()は少々分かり難いですが、やってることは昔懐かし繰下がりのある引き算です。
実際引き算を行うときに、一気に計算できる人なんてまず居ないでしょう。
下の位から順番に、一桁ずつ計算しているはずです。
今回はその計算方法を使用しています。
引数として32768が与えられたとして、まず以下のように一文字ずつに分解します。
Array
(
[0] => 8
[1] => 7
[2] => 6
[3] => 3
[4] => 2
)
for内では下の位から順に引き算を実行します。
最初は$i=4、$borrow=0なので
$ret_array[4]=Array[4]-Array[0]-0=2-8=-6となります。
マイナスの場合は上の位から1を借りてこないといけないので$borrow=1をセットします。
そして借りてきた1を使って現在の位を+10します。
これで$i=4の処理が完了となります。
次に$i=3の場合、$ret_array[3]を同様に計算します。
今度は$i=3、$borrow=1なので
$ret_array[3]=Array[3]-Array[1]-1=3-7-1=-5となります。
$borrowは使ったので0に戻します。
もっとも、答えがまたマイナスなのですぐに1に戻りますが。
同様に、$i=2、$i=1、$i=0と最後の桁まで繰り返して終了。
今度のkaprekar()は、せいぜい2桁の計算しかしていないにもかかわらず、メモリが許す限り大きな桁の計算が出来てしまいます。
出力例
Array
(
[0] => 32799999999999999999999999968
[1] => 76320999999999999999999987633
[2] => 97666332209999999998776633321
[3] => 98777666662110988873333322211
[4] => 98777655543332966665444322211
[5] => 88655533332110988876666444312
[6] => 97776643333210988766665332221
[7] => 98765554333331976666654443211
[8] => 88754333332110988876666654212
[9] => 97776654333320987666654332221
[10] => 98655543333320987666665444311
[11] => 98764333332110988876666653211
[12] => 98777654333331976666654322211
[13] => 88755543333320987666665444212
[14] => 97665433332110988876666543321
[15] => 98776543333320987666665432211
[16] => 98765543333320987666665443211
[17] => 98765433333210988766666543211
[18] => 98776543333320987666665432211=15
)
前回は計算できなかったこのくらい大きな桁の計算もできてしまいました。
このようにアルゴリズムを工夫することで、いろいろな場合において制限を回避することが出来るのですが、何故か工夫すればするほど人間にはわかりづらい表記になってしまうという不思議。
Wikipediaでいうところの定義2のほうのカプレカ操作を行ってみる。
各桁の数値を大きい順に並べた数から、各桁の数値を小さい順に並べた数を引く、という操作を繰り返すと最後は6174のような決まった数、もしくはループになるというものだ。
というわけでとりあえずさくっと作ってみましょう。
kaprekar.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
//カプレカ操作をしてみる
//検証する数
if(isset($_REQUEST['k'])){
$seed=(int)$_REQUEST['k'];
}else{
$seed=12345679;
}
//準備
$i=0;
$result_array=array();
$result_array[0]=$seed;
while(1){
//カプレカ
$tmp=kaprekar($result_array[$i]);
//返り値が既に存在すれば終了
if($a=array_search($tmp,$result_array)){
$result_array[$i+1]=$tmp.'='.$a;
break;
}
$result_array[$i+1]=$tmp;
$i++;
}
print("<pre>");print_r($result_array);die();
//一回のカプレカを計算する関数
function kaprekar($int){
is_int($int) or die('Not integer');
//配列に入れる
$int_array=str_split($int);
//小さい順
sort($int_array);
$int_d=implode('',$int_array);
//大きい順
rsort($int_array);
$int_u=implode('',$int_array);
//差
return $int_u-$int_d;
}
|
引数にk=123というふうに適当な数値を与えると引数のカプレカを操作します。
引数は必ず文字列としてやってきますが、is_intはPHPの関数としては珍しく型を厳格に評価するので、(int)で整数型に変換する必要があります。
出力例
Array
(
[0] => 12345679
[1] => 85308642
[2] => 86308632
[3] => 86326632
[4] => 64326654
[5] => 43208766
[6] => 85317642
[7] => 75308643
[8] => 84308652
[9] => 86308632=2
)
まあなんの問題もなく作れてしまいました。
しかし$seedを大きくしてみると、ほんの10桁程度で計算不能になってしまいます。
これは各要素を数値として扱っているからです。
次回はもっと大きな数のカプレカ計算を行えるようにしてみます。