制限付 PHP による FizzBuzz 解答編

【設問】

C#とか。 - Grim Saga Project 別館:れびんの勉強部屋 - 制限付 PHP による FizzBuzz 設問編:
 http://d.hatena.ne.jp/levin_gsp/20080904/1220533204

 
【解答】

<?php
for($i=1;$i<=100;$i++){
  // () をつけずに無理やり左結合に従って三項演算子で FizzBuzz を処理すると…こうなる。ぱねぇキモさ。
  echo $i % 15 != 0 ? $i % 5 != 0 ? $i % 3 != 0 ? $i . ' ' : 'Fizz ' : 'Buzz ' : 'FizzBuzz ';
}

 
コメント末尾が全てを物語っていると思っていただければ幸い。
今後 PHP三項演算子を重ねて使用する際は素直に () で優先順位を明示しようと決めた(笑
PHP三項演算子も使うな、ってのはご勘弁を。。。
 

        • -

 
ご回答くださった id:uskz さん、ありがとうございました。

制限付 PHP による FizzBuzz 設問編

経緯(ヒント)をまず提示する。
 
まずは昨年末に書いた以下の記事。

C#とか。 - Grim Saga Project 別館:れびんの勉強部屋 - FizzBuzz問題・解答編:
 http://d.hatena.ne.jp/levin_gsp/20071109/1194579778

 
この中で深く考えずに三項演算子を使って撃沈した。
 

<?php
for($i=1;$i<=100;$i++){
  echo $i%15==0?'FizzBuzz ':$i%5==0?'Buzz ':$i%3==0?'Fizz ':$i.' '; /* 整数値かFizzしか表示しない… */
}
?>

 
その原因が昨日判明した。
 

梶本裕介の日記 - 2008年09月03日:
 http://d.hatena.ne.jp/uskz/20080903/p6

 
左結合だったのだ。
キモい。
キモすぎる。
 
昨年末の記事で書いたコードがどう解釈されたか、詳解してみる。
 

<?php
for($i=1;$i<=100;$i++){
  echo ((( $i % 15 == 0 ) ? 'FizzBuzz ' : $i % 5 == 0 ) ? 'Buzz ' : $i % 3 == 0 ) ? 'Fizz ' : $i . ' ' ; /* 整数値かFizzしか表示しない… */
}
?>

 
これじゃあ意図通り動くわけがない。
そこで昨日修正記事を書いた。
 

C#とか。 - Grim Saga Project 別館:れびんの勉強部屋 - 三項演算子の解釈要注意:
 http://d.hatena.ne.jp/levin_gsp/20080903/1220445755

 
単純に()で条件判断処理を優先付けした。
 

<?php
for($i=1;$i<=100;$i++){
  echo ($i%15==0?'FizzBuzz ':($i%5==0?'Buzz ':($i%3==0?'Fizz ':$i.' ')));
}
?>

 
これで一応の解決をみた。
が、なんか悔しい。
 
ここまでが経緯(ヒント)。
次が設問。
 
 
 

【問題】
1から100までの数を表示するプログラムを書け。
ただし3の倍数のときは数の代わりに「Fizz」と表示。
5の倍数のときは「Buzz」と表示。
3と5両方の倍数の場合には「FizzBuzz」と表示すること。
 
解答言語は PHP とする。
また、以下のテンプレートを使用すること。
 
for($i=1;$i<=100;$i++){
echo ### FreeCode ###;
}
 
### FreeCode ### 部分を三項演算子を用い、()を用いずにコーディングして解を示せ。
 

                  • -

つまり PHP三項演算子を使って、()で優先順位付せずに解いてネ、ってこと。

 
もちろん私自身は解いてみたわけなので、PHPキモい嫌い、などと言わずに考えてみていただきたい。
頭の体操、頭の体操。
 
解答は一週間後をメドに掲載する。

三項演算子の解釈要注意

昨年末頃にこんな記事を書いた。

C#とか。 - Grim Saga Project 別館:れびんの勉強部屋 - FizzBuzz問題・解答編:
 http://d.hatena.ne.jp/levin_gsp/20071109/1194579778

 
この中に

        • -

驚愕の事実が判明しちゃいました。
php 5.2.2 Win だけかどうかわかりませんが、一処理に三項演算子を三つ以上重ねると最後尾の二つしか効かない。

        • -

こんなことを書いた。
これ、ずっと気になってた。
今も PHP の開発を続けている身でもあるので。
本日たまたまわんくまで三項演算子が取り沙汰されているのに便乗して、R・田中一郎さんの記事にコメントをつけた。
 

R.Tanaka.Ichiro's Blog - 三項演算子は上から評価される(その2):
 http://blogs.wankuma.com/rti/archive/2008/09/03/155325.aspx

 
そこで id:uskz さんからご指摘を受ける。
 

梶本裕介の日記 - 2008年09月03日:
 http://d.hatena.ne.jp/uskz/20080903/p6

 
げげ、マジか。
 
ってのが、まず第一印象。
時間の都合上(またか)、詳細な説明は割愛させていただく。
 
冒頭で引用した私の記事の表現は正しくないことがわかった。
左結合だったのだ。
そこで(意図した)正しい処理順序を()で明示することで解決。
 

<?php
for($i=1;$i<=100;$i++){
  echo ($i%15==0?'FizzBuzz ':($i%5==0?'Buzz ':($i%3==0?'Fizz ':$i.' ')));
}
?>

 
これで PHP 三行プログラミングで FizzBuzz を倒すことが出来た。
勉強になった。

PHP で XML を扱う

久々の技術記事を書く時間をようやく捻出。
が、残念ながら .NET(C#)/XNA ではなくて、タイトル通り PHP/XML である。
実際にいじっているのだから仕方がない。
 
ということで、PHPXML を扱う技術について今回は書いてみようと思う。
まず、使うのは SimpleXML.
 
DOM や DOM/XML(やや古) というのもあるし、そちらの方が詳細に XML を扱うことが出来るが些かコーディングが面倒である。
わかりにくいし。
いや、XML の基礎知識が足りてないのかもしれないが、その追求は今回割愛する。都合良く。
 
SimpleXML の良さはなんと言ってもその名の通り、シンプルさ。
概念がとても簡単で理解しやすい。
 
 
 
例として、次のような XML を扱うとしよう。
 

<?xml version="1.0" encoding="UTF-8"?>
<database>
  <redcarpet>
    <items>
      <item>
        <name>世界のナベアツ</name>
        <gag>オモロー</gag>
      </item>
    </items>
  </redcarpet>
</database>

 
これまで大きな勘違いをしておりまして。
SimpleXML は XML Read 用のライブラリだと思っていたんですね。
 
上記のデータファイル名が「data.xml」だとすると。
 

$xml = simplexml_load_file('data.xml');
echo $xml->redcarpet->items->item->name;
echo ' のギャグは ';
echo $xml->redcarpet->items->item->gag;
// 世界のナベアツ のギャグは オモロー

 
とか書いて「オモロー」と表示させるだけのものだと思っていました。
事前調査が劇的に不足していました。
 

simplexml_import_dom -- DOM ノードから SimpleXMLElement オブジェクトを取得する
simplexml_load_file -- XMLファイルをパースし、オブジェクトに代入する
simplexml_load_string -- XML 文字列をオブジェクトに代入する

 
基本、XML ファイルからデータを読むから、simple_import_dom 使わないと SimpleXMLElement のメソッドは使えないと思っていたのが大きな間違い。
simplexml_load_file/simple_load_string も戻り値は SimpleXMLElement 型だった。
 
今まで PHP + XML で自サイトを構築した中で、XML 更新部分は DOM を交えてひーこらやっていたのは単なるオマヌケだったと最近気づいた。
 

SimpleXMLElement->addAttribute() -- SimpleXML 要素に属性を追加する
SimpleXMLElement->addChild() -- XML ノードに子要素を追加する
SimpleXMLElement->asXML() -- SimpleXML 要素に基づき整形式の XML 文字列を返す
SimpleXMLElement->attributes() -- 要素の属性を定義する
SimpleXMLElement->children() -- 指定したノードの子ノードを見付ける
SimpleXMLElement->__construct() -- 新しい SimpleXMLElement オブジェクトを作成する
SimpleXMLElement->getDocNamespaces() -- ドキュメントで宣言されている名前空間を返す
SimpleXMLElement->getName() -- XML 要素の名前を取得する
SimpleXMLElement->getNamespaces() -- ドキュメントで使用している名前空間を返す
SimpleXMLElement->registerXPathNamespace() -- 次の XPath クエリ用の prefix/ns コンテキストを作成する
SimpleXMLElement->xpath() -- XML データに Xpath クエリを実行する

 
こんなに使えるメソッドがいやがった。。。
先ほどの例で使った XML にデータを追加したいとします。

      <item>
        <name>ザ・パンチ</name>
        <gag>砂漠でラクダに逃げられてぇー!</gag>
      </item>

 

$xml = simplexml_load_file('data.xml');
$item = $xml->redcarpet->items->addChild( 'item' ); // XMLノードに子要素追加
$item->addChild( 'name', 'ザ・パンチ' ); // XMLノードに子要素追加
$item->addChild( 'gag', '砂漠でラクダに逃げられてぇー!' ); // XMLノードに子要素追加
$xml->asXml('data.xml'); // 引数を指定することでファイルへの書き込みができる

 
と、こんな感じで割とお手軽に PHPXML を扱えてしまう。
もっと早くに気づいていれば良かった。。。
 
ちなみに SimpleXML は PHP 5 系では標準モジュール扱いなので、特別何もせず使える。
良いことづくめ。
ちょっとだけ不満なのは、最終的に出来上がる XML ファイルの中身の改行を意識出来ないこと。
 
最終的に出来る XML(data.xml)。

<?xml version="1.0" encoding="UTF-8"?>
<database>
  <redcarpet>
    <items>
      <item>
        <name>世界のナベアツ</name>
        <gag>オモロー</gag>
      </item>
      <item><name>ザ・パンチ</name><gag>砂漠でラクダに逃げられてぇー!</gag></item>
    </items>
  </redcarpet>
</database>

 
ホントはこうなって欲しい。

<?xml version="1.0" encoding="UTF-8"?>
<database>
  <redcarpet>
    <items>
      <item>
        <name>世界のナベアツ</name>
        <gag>オモロー</gag>
      </item>
      <item>
        <name>ザ・パンチ</name>
        <gag>砂漠でラクダに逃げられてぇー!</gag>
      </item>
    </items>
  </redcarpet>
</database>

 
ギャグの一覧を表示するページを創る。オマケ。

<html>
<head><title>ギャグ一覧</title></head>
<body>
<?php
$xml = simplexml_load_file('data.xml');
foreach( $xml->redcarpet->items->item as $itemid => $item ){
  echo $item->name . "" . $item->gag . "<br>\n";
}
/*
世界のナベアツ → オモロー<br>
ザ・パンチ → 砂漠でラクダに逃げられてぇー!<br>
*/
?>
</body>
</html>

 
 
 
※ここに記載したコードは動作試験をしていないので注意

ラムダその3

ラムダ:
 http://d.hatena.ne.jp/levin_gsp/20071127/1196143533
ラムダその2:
 http://d.hatena.ne.jp/levin_gsp/20080415/1208260855
 
前回、ラムダ式を実践してみようと思って挫折した。
Visual Studio 2008 Express Edition をインストールしてみたため、再びチャレンジ。
経緯、その他見解等は過去のエントリを参照してほしい。
 

using System;
 
namespace sample02
{
    /*
     * ラムダ式 (lambda expression) 入門用クラス
     */
    class Program
    {
        /*
         * x と y を渡すためのデリゲートを定義
         */
        delegate int SampleDelegate(int x, int y);
        
        /*
         * デリゲートを引数指定して内部実行後、標準出力するメソッド
         */
        private static void Calculate(int x, int y, SampleDelegate calculator)
        {
            Console.WriteLine(calculator(x,y));
        }
        
        /*
         * コールしてみる。
         * 第三引数はデリゲートなので、ここにラムダを記述
         * 
         * 下記の場合は結果が「11」となる。
         */
        static void Main(string[] args)
        {
            Calculate(3, 8, (x, y) => x+y );
            
            /* デリゲートで書くとこうなる。出力は同じく「11」 */
            Calculate(3, 8, delegate(int x, int y) { return x + y; }); 
        }
    }
}

 
@IT記事に倣って、デリゲートでも同じ結果となる処理を書いてみた。
確かにこれは慣れれば強力な武器になりそうだ。

三角形くるくる

Visual Studio 2008 Express Editions - XNA Game Studio 入門 : XNA Game Studio で作るマインスイーパ (コラム):
 http://www.microsoft.com/japan/msdn/vstudio/express/learn/xna/column_04.aspx

 
上記を参考にRGBグラデーションの三角形が自動的にくるくる回るだけのコードを書いてみた。
参考に、というかほぼ全パクり。
とにかく動くものが作りたかったので、お勉強の意味からすれば劇的に手抜きだけど刺激になったので良しとする。
アルゴリズムとかテクニックの部分は上記ページに譲る。
 
また実行ファイルを自レンタルサーバに設置しておいたので、くるくるを体感したい方はダウンロード&実行してみて欲しい。
 

三角形くるくる:
 http://www.grimsagaproject.com/project/xnasample/mssample01.zip
(追記-----)
三角形くるくる動画:
 http://www.grimsagaproject.com/project/movie/mssample01.html
関連記事 - れびさんのおうち - wink してみた:
 http://myhome.cururu.jp/aquacity/blog/article/91002016813
(---------)

 
XNA Game Studio 2.0 で作成した実行ファイルを正常に動作させるために、いくつか必要になるものがあるので注意。
 

ひにけにXNA - XNA GSで作ったゲームを遊ぶ:
 http://blogs.msdn.com/ito/archive/2008/05/07/distribute-xna-games.aspx

 
必要なモノに関しては上記を参照。
XNA GS 2.0 で作られたゲームの場合」をご覧あれ。
但し、5 で言う GamerServiceComponent は使っていないので XNA GS 2.0 そのものをインストールする必要はない。

構造体・クラス、ボックス化

@ITのC#連載記事を元に勉強を進める企画。
XNA の勉強と並行している時点で矛盾がある気もするが、そこは気にしない。
やりたいようにやるのが自分にとって一番やりやすい、つまりはかどるからである。
 
さて、少しC#そのものの勉強記事から遠ざかっていたので、まずは参照している@ITの連載を紹介しておく。
忙しさを理由にしたり、XNAをいじったりしている間に、C#3.0の連載も始まっていることだし。
 

@IT - 連載 改訂版 C#入門:
 http://www.atmarkit.co.jp/fdotnet/csharp_abc2/index/index.html
@IT - 連載 C# 2.0 入門:
 http://www.atmarkit.co.jp/fdotnet/csharp20/index/index.html
@IT - 連載 C# 3.0 入門:
 http://www.atmarkit.co.jp/fdotnet/csharp30/index/index.html

 
ちょっと過去に遡って、私がC#の勉強をしたつもりの記事も挙げておく。

2007/10/19 interface:
 http://d.hatena.ne.jp/levin_gsp/20071019/1192799664
2007/10/24 interface 2:
 http://d.hatena.ne.jp/levin_gsp/20071024/1193215910
2007/10/29 ジャグ配列:
 http://d.hatena.ne.jp/levin_gsp/20071029/1193654852
2007/10/31 不慣れでもMSDN検索:
 http://d.hatena.ne.jp/levin_gsp/20071031/1193825179
2007/11/06 匿名メソッド:
 http://d.hatena.ne.jp/levin_gsp/20071106/1194354133
2007/11/09 FizzBuzz問題・解答編:
 http://d.hatena.ne.jp/levin_gsp/20071109/1194579778
2007/11/19 ジェネリック
 http://d.hatena.ne.jp/levin_gsp/20071119/1195467451
2007/11/27 ラムダ:
 http://d.hatena.ne.jp/levin_gsp/20071127/1196143533
2007/11/30 サンプル時計:
 http://d.hatena.ne.jp/levin_gsp/20071130/1196407261
2008/04/15 ラムダその2:
 http://d.hatena.ne.jp/levin_gsp/20080415/1208260855

 
ん。
結構サボった。…どんまい。
 
気を取り直して。
実は@IT記事に沿って進めることすら出来ていない。
読んではいるけど、一つ一つサンプルを自力で捻出していない、という意味で。
 
そういう決めごとを設けるのも一つだが、この辺りも冒頭に書いたとおり、やりやすいようにやるのが性分のようで。
今回は、間も空いてしまったので、基礎に立ち返る。
C#2.0/3.0 とかのたまう前に私はまだ C# そのものの基本がわかっちゃいないんだから。
 
ということで、タイトル通り。
 

@IT - 連載 改訂版 C#入門:
 第5章 C#のデータ型:
 http://www.atmarkit.co.jp/fdotnet/csharp_abc2/csabc2_005/cs2_005_01.html

 
ざっと読んでスルーしようと思ったらボックス化のところで試してみたくなった。
プリミティブな型(char, int, etc...)は構造体で出来ている。
クラスとの大きな違いは値型か参照型だ、というのだ。
全てのクラスのスーパークラスである object 型に = で代入することが、基本的には出来る。
その際に自動的に行っているのが、ボックス化。
 
値型を object 型に代入する際には、値型を包むクラスを内部的に自動生成し、一時的なインスタンスまでも作成してくれているというのだ。
 
さて、その解説を読んで気になった。
ボックス化は万能ではないという。
なぜなら、値型ではインスタンスのコピーを(今回の例では) object 型に代入するからだ。
クラスのデータを代入する場合は、参照型を参照型に代入するので、コピーではない。
その挙動の違いを理解しておかないと痛い目を見そうだ。
 
今のうちに見ておこう。痛い目を。
ということでサンプルコードを書いてみた。
 

	class Program
	{
		public struct 構造体 { public int i; }
		public class クラス { public int i; }

		static void Main(string[] args)
		{
			/*
			 * 色々なボックス化
			 */
			object[] test = new object[5];
			test[0] = (int)1;
			test[1] = (float)0.1;
			test[2] = (string)"TEST";
			test[3] = new 構造体();
			test[4] = new クラス();
			for (int i = 0; i < 5; i++)
			{
				Console.WriteLine("Class={0}, Value={1}", test[i].GetType().FullName, test[i].ToString());
			}

			Console.WriteLine(); /* 出力にブランクラインを挿入 */

			/*
			 * 構造体をobject型にボックス化
			 */
			構造体 structTest = new 構造体();
			structTest.i = 100;
			Console.WriteLine("structTest.i={0}", structTest.i);
			object objectStructTest = structTest;
			Console.WriteLine("structTest.i={0}, objectStructTest.i={1}", structTest.i, ((構造体)objectStructTest).i);
			structTest.i = 200;
			Console.WriteLine("structTest.i={0}, objectStructTest.i={1}", structTest.i, ((構造体)objectStructTest).i);

			Console.WriteLine(); /* 出力にブランクラインを挿入 */

			/*
			 * クラスをobject型にボックス化
			 */
			クラス classTest = new クラス();
			classTest.i = 100;
			Console.WriteLine("classTest.i={0}", classTest.i);
			object objectClassTest = classTest;
			Console.WriteLine("classTest.i={0}, objectClassTest.i={1}", classTest.i, ((クラス)objectClassTest).i);
			classTest.i = 200;
			Console.WriteLine("classTest.i={0}, objectClassTest.i={1}", classTest.i, ((クラス)objectClassTest).i);
		}
	}

 
どうだろう。
「構造体をobject型にボックス化」と「クラスをobject型にボックス化」の処理。
やっていることはまったく一緒。
結果が異なることを証明してみた。
 
ちょっと昔、C言語をやっていた頃のポインタ勉強時代を思い出した♪
あの頃があって、値と参照の違いがすんなり受け入れられるのだろうと思ったりする。
結果は以下。
 

Class=System.Int32, Value=1
Class=System.Single, Value=0.1
Class=System.String, Value=TEST
Class=Program+構造体, Value=Program+構造体
Class=Program+クラス, Value=Program+クラス
 
structTest.i=100
structTest.i=100, objectStructTest.i=100
structTest.i=200, objectStructTest.i=100
 
classTest.i=100
classTest.i=100, objectClassTest.i=100
classTest.i=200, objectClassTest.i=200

 
若干編集したけど。
構造体とクラスの出力にはProgramの前にnamespaceがついていたが、何となく恥ずかしくて消しておいた。
 
ふーむ、しかしこれは勉強になった。