ネタつつき163 - 掛算と割り算の本質は同じ
掛算と割り算の分析をしてみて感じたのは、この両演算は一次関数と似ているという事です。例えば、2 * 4 = 8、7 / 3 = 2 ..余り2です。これを一次関数に書き直すとy = 2x + b です。掛け算の余りを0と仮定すると、それぞれが 2x + 0と2x + 1 です。これを微分すれば両方とも2です。この事から、掛け算と割り算は超離散しているだけのグラフを描くように見えます。
その考えを元に、今度は掛け算と割り算について、共通ルーチンを使う形で実装してみました。今度のサンプルは、ちょっと複雑になったので、オブジェクト指向の洗礼(プログラマージョーク)をしています。構造的にちょっと見やすくなっていると思います。
//Main
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
static class Program
{
static void Main()
{
CycleTableTest( );
Console.WriteLine( );
NumberTest( );
}
static void NumberTest()
{
char[ ] table = new char[ ] {
'0', '1', '2',
'3', '4', '5',
'6', '7', '8',
'9'
};
NumberUnit obj = new NumberUnit(
new CycleTable( table ) );
//インクリメントとデクリメントの共通表示処理
Action<string, Func<char, string>> Print =
( string msg, Func<char, string> f ) => {
Console.WriteLine( msg );
foreach ( char value in table ) {
Console.WriteLine( "{0}を代入した結果は? : {1}",
value,
f( value ) );
};
Console.WriteLine( );
};
//インクリメント
Print( "インクリメントします・・・",
( char x ) => obj.Increment( x ).ToString( ) );
//デクリメント
Print( "デクリメントします・・・",
( char x ) => obj.Decrement( x ).ToString( ) );
//加算と減算の共通表示処理
Action<char, char, char, Func<char, char, string> > show =
( char x, char ope, char y, Func<char, char, string> f ) => {
Console.WriteLine( "{0} {1} {2} = {3} ",
x, ope, y, f( x, y ) );
};
Action<string, char, Func<char, char, string>> Print1 =
( string msg, char ope, Func<char, char, string> f ) => {
Console.WriteLine( msg );
show( '0', ope, '0', f );
show( '2', ope, '3', f );
show( '3', ope, '6', f );
show( '4', ope, '2', f );
show( '7', ope, '3', f );
show( '9', ope, '9', f );
Console.WriteLine( );
};
//足し算
Print1(
"加算演算子を試します・・・",
'+',
( char v, char v1 ) => ( obj.Add( v, v1 ) ).ToString( ) );
//引き算
Print1( "減算演算子を試します・・・",
'-',
( char v, char v1 ) => ( obj.Sub( v, v1 ) ).ToString( ) );
//掛け算
Print1(
"乗算演算子を試します・・・",
'×',
( char v, char v1 ) => ( obj.Mul( v, v1 ) ).ToString( ) );
//掛け算
Print1(
"除算演算子を試します・・・",
'÷',
( char v, char v1 ) => ( obj.Div( v, v1 ) ).ToString( ) );
//比較
Console.WriteLine( "値の比較を試します・・・" );
Func<int, string> GetString = ( int v ) => {
switch ( v ) {
case -1:
return "右の値の方が大きい";
case 0:
return "両方の値は同じ";
case 1:
return "左の値の方が大きい";
}
throw new ArgumentException( );
};
Console.WriteLine( "{0}と{1}どちらが大きい?{2}",
'5' , '4',GetString( obj.CompareTo( '5', '4' ) ) );
Console.WriteLine( "{0}と{1}どちらが大きい?{2}",
'5', '5', GetString( obj.CompareTo( '5', '5' ) ) );
Console.WriteLine( "{0}と{1}どちらが大きい?{2}",
'5', '6', GetString( obj.CompareTo( '5', '6' ) ) );
//終了
Console.ReadLine( );
}
static void CycleTableTest()
{
char[ ] table = new char[ ] {
'0', '1', '2',
'3', '4', '5',
'6', '7', '8',
'9'
};
CycleTable obj = new CycleTable( table );
Console.WriteLine( "次の値を表示します・・・" );
foreach ( char value in table ) {
Console.WriteLine( "{0}を代入した結果は? : {1}, " +
"1週回った?:{2}",
value,
obj.Next( value ),
obj.IsLast );
};
Console.WriteLine( );
}
}
//NumberUnit
using System;
using System.Text;
using System.Collections.Generic;
enum Sign
{
Plus,
Minus
}
//1桁の数字を抽象化したもの
class NumberUnit
{
//記号表
CycleTable _table;
CycleTable _rTable;
//記号表が必要となる
public NumberUnit(
CycleTable table )
{
this._table = table;
this._rTable = table.Reverse( );
}
//インクリメント演算
public Value Increment(
char value )
{
char lowValue = this._table.Next( value );
char highValue = this._table.First;
if ( this._table.IsLast ) {
highValue = this._table.Next( highValue );
this._table.Reset( );
}
Sign sign = Sign.Plus;
return new Value(
sign,
highValue,
lowValue,
this._table.First );
}
//デクリメント演算
public Value Decrement(
char value )
{
if ( value == this._table.First ) {
return new Value(
Sign.Minus,
this._table.First,
this._table.Begin,
this._table.First );
}
char lowValue = this._rTable.Next( value );
char highValue = this._table.First;
Sign sign = Sign.Plus;
if ( this._rTable.IsLast ) {
sign = Sign.Minus;
this._rTable.Reset( );
}
return new Value(
sign,
highValue,
lowValue,
this._table.First );
}
//2つの引数を受け取る演算
//記号表と照らし合しながら値を導出
private Tuple<bool, char> Operator(
CycleTable usetTable,
char value,
char value1 )
{
CycleTable workTable = this._table.Copy( );
char temp = workTable.First;
char result = value;
while ( value1 != temp ) {
result = usetTable.Next( result );
temp = workTable.Next( temp );
}
bool flag = usetTable.IsLast;
usetTable.Reset( );
return new Tuple<bool, char>(
flag, result );
}
//加算演算
public Value Add(
char value,
char value1 )
{
Tuple<bool, char> temp = this.Operator(
this._table,
value,
value1 );
Sign sign = Sign.Plus;
char lowValue = temp.Item2;
char highValue;
if ( temp.Item1 ) {
highValue = this._table.Begin;
} else {
highValue = this._table.First;
}
return new Value(
sign,
highValue,
lowValue,
this._table.First);
}
//減算演算
public Value Sub(
char value,
char value1 )
{
Tuple<bool, char> temp;
char v = value;
char v1 = value1;
Sign sign;
if ( this.CompareTo( value, value1 ) >= 0 ) {
temp = this.Operator(
this._rTable,
value,
value1 );
sign = Sign.Plus;
} else {
temp = this.Operator(
this._rTable,
value1,
value );
sign = Sign.Minus;
}
char highValue = this._table.First;
char lowValue = temp.Item2;
return new Value(
sign,
highValue,
lowValue,
this._table.First );
}
//比較
public int CompareTo( char v, char v1 )
{
//エラーチェック
if ( !this._table.Contains( v ) ) {
throw new ArgumentException(
v + "は無効な記号です。" );
}
if ( !this._table.Contains( v1 ) ) {
throw new ArgumentException(
v1 + "は無効な記号です。" );
}
//どちらが先に到達するまでチェック
char temp = this._table.First;
bool vHit = false;
bool vHit1 = false;
char tmp1 = this._table.First;
while ( true ) {
if ( temp == v ) vHit = true;
if ( tmp1 == v1 ) vHit1 = true;
if ( vHit || vHit1 ) break;
temp = this._table.Next( temp );
tmp1 = this._table.Next( tmp1 );
}
//どちらが先に到達したのか
if ( vHit ) {
if ( vHit1 == false ) return -1;
return 0;
} else {
if ( vHit1 ) return 1;
return 0;
}
}
//3つの引数を受け取る演算
private Value Operator(
char addValue,
char subValue,
char remainder )
{
if ( addValue == this._table.First ||
subValue == this._table.First ) {
return new Value( this._table.First );
}
Value temp = new Value(
Sign.Plus,
this._table.First,
this._table.First,
this._table.First,
remainder );
char highValue = this._table.First;
char lowValue = this._table.First;
while ( remainder >= subValue ) {
Value v = this.Add( lowValue, addValue );
lowValue = v.LowValue;
if ( v.IsHighValue )
highValue = this.Increment( highValue ).LowValue;
remainder = this.Sub( remainder, subValue ).LowValue;
temp = new Value(
Sign.Plus,
highValue,
v.LowValue,
this._table.First,
remainder );
}
return temp;
}
//乗算演算
public Value Mul(
char value,
char value1 )
{
return this.Operator(
value,
this._table.Begin,
value1 );
}
//除算演算
public Value Div(
char value,
char value1 )
{
return this.Operator(
this._table.Begin,
value1,
value );
}
}
using System;
using System.Collections.Generic;
using System.Linq;
//記号表を管理するオブジェクト
class CycleTable
{
//記号列
IEnumerable<char> table;
//開始記号
char _first;
public char First
{
get { return this._first; }
}
//終了記号
char _last;
public char Last
{
get { return _last; }
}
//開始記号の次
char _begin;
public char Begin
{
get { return _begin; }
}
//テーブルの終了を過ぎたか?
bool _lastFlag;
public bool IsLast
{
get { return this._lastFlag; }
}
//記号列を指定してインスタンスを生成
public CycleTable( IEnumerable<char> table )
{
this.table = table;
this._first = this.table.First( );
this._last = this.table.Last( );
this._begin = this.Next( this._first );
}
//次の記号を返す
public char Next( char value )
{
//エラーチェック
if ( !table.Contains( value ) ) {
throw new ArgumentException(
value + "は無効な記号です。" );
}
//初期値設定
IEnumerator<char> ie = this.table.GetEnumerator( );
ie.MoveNext( );
//検索
while ( value != ie.Current ) ie.MoveNext( );
if ( ie.Current == _last ) {
//終了まで達したら開始記号を返す
this._lastFlag = true;
return _first;
} else {
ie.MoveNext( );
return ie.Current;
}
}
//オブジェクトの状態をリセットする
public void Reset()
{
this._lastFlag = false;
}
//記号表を逆にしたオブジェクトを返す
public CycleTable Reverse()
{
return new CycleTable( this.table.Reverse( ) );
}
//同じ記号表を持つオブジェクトを返す
public CycleTable Copy()
{
return new CycleTable( this.table );
}
//記号表に存在するかチェック
public bool Contains( char target )
{
return this.table.Contains( target );
}
}
共通性を強くアピールするために、0除算のエラーチェックはしていませんが、両演算はほぼ同じだと分かると思います。違いはエラーチェックと、特殊な状況です。これは、加算と減算にも言えます。減算でマイナス値を算出するときに工夫するのと同じです。情報技術力?を鍛えると、情報技術を学ぶ前に見えなかったものが見えてきます。乗算と除算と言えば、表と裏のように両極端に感じる人が多いと思います。しかし、それらの物事も視点を変え抽象化してみれば、思わぬ共通性が見えてきます。これこそが、情報技術の魅力なのかもしれませんね。