FizzBuzz

FizzBuzz のルール

  • 3 の倍数のときには fizz と表示する
  • 5 の倍数のときには buzz と表示する
  • 3 と 5 の倍数(15の倍数)のときには fizz buzz と表示する
  • それ以外はその数を表示する

ループ

すぐにいろいろ思いつかない場合はまずシンプルなものから考える

#define	is_multiple_of( n, m )	((n)&&!((n)%(m)))
#define	is_fizz( n )	is_multiple_of((n),3)
#define	is_buzz( n )	is_multiple_of((n),5)

void	fizz_buzz( int	max_value ){
	int	n = 1;
	while( n <= max_value ){
		if( n > 1 ){
			debug_printf( "," );
		}
		bool	fizz = is_fizz( n );
		bool	buzz = is_buzz( n );
		if( fizz && buzz ){
			debug_printf( "fizz buzz" );
		}
		else if( buzz ){
			debug_printf( "buzz" );
		}
		else if( fizz ){
			debug_printf( "fizz" );
		}
		else{
			debug_printf( "%d", n );
		}
		++n;
	}
	debug_printf( "\n" );
}
int	main(){
	fizz_buzz( 20 );
	return	0;
}

再帰

ループができたら再帰にもできる

#define	is_multiple_of( n, m )	((n)&&!((n)%(m)))
#define	is_fizz( n )	is_multiple_of((n),3)
#define	is_buzz( n )	is_multiple_of((n),5)

void	fizz_buzz( int	max_value ){
	struct detail {
		static void	callback( int	n ){
			if( n > 1 ){
				debug_printf( "," );
			}
			bool	fizz = is_fizz( n );
			bool	buzz = is_buzz( n );
			if( fizz && buzz ){
				debug_printf( "fizz buzz" );
			}
			else if( buzz ){
				debug_printf( "buzz" );
			}
			else if( fizz ){
				debug_printf( "fizz" );
			}
			else{
				debug_printf( "%d", n );
			}
		}
		static void	fizz_buzz( int	max_value, int	n, void	(*callback)( int )){
			callback( n );
			if( n+1 <= max_value ){
				fizz_buzz( max_value, n+1, callback );
			}
		}
	};
	detail::fizz_buzz( max_value, 1, &detail::callback );
	debug_printf( "\n" );
}
int	main(){
	fizz_buzz( 20 );
	return	0;
}

テンプレート:その1

再帰条件がコンパイル時に決定できる再帰はテンプレートにできる

#define	is_multiple_of( n, m )	((n)&&!((n)%(m)))
#define	is_fizz( n )	is_multiple_of((n),3)
#define	is_buzz( n )	is_multiple_of((n),5)

template<int N,bool = is_fizz(N),bool = is_buzz(N)> struct fizz_buzz_helper_t {
	static void	fizz_buzz(){
		debug_printf( ",%d", N );
	}
};
template<> struct fizz_buzz_helper_t<1,false,false> {
	static void	fizz_buzz(){
		debug_printf( "1" );
	}
};
template<int N> struct fizz_buzz_helper_t<N,true,false> {
	static void	fizz_buzz(){
		debug_printf( ",fizz" );
	}
};
template<int N> struct fizz_buzz_helper_t<N,false,true> {
	static void	fizz_buzz(){
		debug_printf( ",buzz" );
	}
};
template<int N> struct fizz_buzz_helper_t<N,true,true> {
	static void	fizz_buzz(){
		debug_printf( ",fizz buzz" );
	}
};
template<int lhs,int rhs> struct less_than_or_equal_to_t {
	static const bool	value = ( lhs <= rhs );
};
template<int max_value,int n = 1,bool = less_than_or_equal_to_t<n,max_value>::value> struct loop_helper_t {
	static void	each(){
		debug_printf( "\n" );
	}
};
template<int max_value,int n> struct loop_helper_t<max_value,n,true> {
	static void	each(){
		fizz_buzz_helper_t<n>::fizz_buzz();
		loop_helper_t<max_value,n+1>::each();
	}
};
template<int max_value> void	fizz_buzz(){
	loop_helper_t<max_value>::each();
}
int	main(){
	fizz_buzz<20>();
	return	0;
}

テンプレート:その2

template<int n,int m> struct is_multiple_of_t {
	static const bool	value = !( n % m );
};
template<int n> struct is_multiple_of_t<n,0> {
	static const bool	value = false;
};
template<int m> struct is_multiple_of_t<0,m> {
	static const bool	value = false;
};
template<> struct is_multiple_of_t<0,0> {
	static const bool	value = false;
};
template<int n> struct is_fizz_t : is_multiple_of_t<n,3> {};
template<int n> struct is_buzz_t : is_multiple_of_t<n,5> {};

template<int lhs,int rhs> struct less_than_or_equal_to_t {
	static const bool	value = ( lhs <= rhs );
};
template<int max_value,int n = 1,bool = is_fizz_t<n>::value,bool = is_buzz_t<n>::value,bool = less_than_or_equal_to_t<n,max_value>::value> struct fizz_buzz_helper_t {
	static void	fizz_buzz(){
		debug_printf( "\n" );
	}
};
template<int max_value,int n> struct fizz_buzz_helper_t<max_value,n,false,false,true> {
	static void	fizz_buzz(){
		debug_printf( ",%d"), n );
		fizz_buzz_helper_t<max_value,n+1>::fizz_buzz();
	}
};
template<int max_value> struct fizz_buzz_helper_t<max_value,1,false,false,true> {
	static void	fizz_buzz(){
		debug_printf( "1" );
		fizz_buzz_helper_t<max_value,1+1>::fizz_buzz();
	}
};
template<int max_value,int n> struct fizz_buzz_helper_t<max_value,n,true,false,true> {
	static void	fizz_buzz(){
		debug_printf( ",fizz" );
		fizz_buzz_helper_t<max_value,n+1>::fizz_buzz();
	}
};
template<int max_value,int n> struct fizz_buzz_helper_t<max_value,n,false,true,true> {
	static void	fizz_buzz(){
		debug_printf( ",buzz" );
		fizz_buzz_helper_t<max_value,n+1>::fizz_buzz();
	}
};
template<int max_value,int n> struct fizz_buzz_helper_t<max_value,n,true,true,true> {
	static void	fizz_buzz(){
		debug_printf( ",fizz buzz" );
		fizz_buzz_helper_t<max_value,n+1>::fizz_buzz();
	}
};
template<int N> void	fizz_buzz(){
	fizz_buzz_helper_t<N>::fizz_buzz();
}
int	main(){
	fizz_buzz<20>();
	return	0;
}

テンプレート:その3

倍数の判定もテンプレートにやらせる

template<int _value> struct fizz_t {
	static const int	next = _value+1;
};
template<> struct fizz_t<3> {
	static const int	next = 1;
};
template<int _value> struct buzz_t {
	static const int	next = _value+1;
};
template<> struct buzz_t<5> {
	static const int	next = 1;
};
template<int n,int,int> struct fizz_buzz_print_t {
	static void	print(){
		debug_printf( ",%d", n );
	}
};
template<int fizz,int buzz> struct fizz_buzz_print_t<1,fizz,buzz> {
	static void	print(){
		debug_printf( "1" );
	}
};
template<int n,int buzz> struct fizz_buzz_print_t<n,3,buzz> {
	static void	print(){
		debug_printf( ",fizz" );
	}
};
template<int n,int fizz> struct fizz_buzz_print_t<n,fizz,5> {
	static void	print(){
		debug_printf( ",buzz" );
	}
};
template<int n> struct fizz_buzz_print_t<n,3,5> {
	static void	print(){
		debug_printf( ",fizz buzz" );
	}
};
template<int lhs,int rhs> struct less_than_or_equal_to_t {
	static const bool	value = ( lhs <= rhs );
};
template<int max_value,int n = 1,int fizz = 1,int buzz = 1,bool = less_than_or_equal_to_t<n,max_value>::value> struct fizz_buzz_helper_t {
	static void	fizz_buzz(){
		debug_printf( "\n" );
	}
};
template<int max_value,int n,int fizz,int buzz> struct fizz_buzz_helper_t<max_value,n,fizz,buzz,true> {
	static void	fizz_buzz(){
		fizz_buzz_print_t<n,fizz,buzz>::print();
		fizz_buzz_helper_t<max_value,n+1,fizz_t<fizz>::next,buzz_t<buzz>::next>::fizz_buzz();
	}
};
template<int N> void	fizz_buzz(){
	fizz_buzz_helper_t<N>::fizz_buzz();
}
int	main(){
	fizz_buzz<20>();
	return	0;
}

ある数字があるかどうか:ループ

Fizz Buzz じゃないけど、発展問題できっとすぐに思い浮かぶでしょう
ある数字があるかどうかは、10進数の各桁がその数かどうかをチェックすればいいので

inline bool	has_digit( int	n, int	m ){
	bool	result = ( !n && !m );
	while( n ){
		if(( n % 10 ) == m ){
			result = true;
			break;
		}
		n /= 10;
	}
	return	result;
}

ある数字があるかどうか:再帰

ループは再帰にもできる

inline bool	has_digit( int	n, int	m ){
	struct detail {
		static bool	callback( int	n, int	m ){
			return	( n &&((( n % 10 ) == m ) || callback( n / 10, m )));
		}
	};
	return	( !n && !m ) || detail::callback( n, m );
}

ある数字があるかどうか:テンプレート

再帰条件がコンパイル時に決定できる再帰はテンプレートにできる

template<int N, int m, int n = N> struct has_digit_t {
	static const bool	value = (( n % 10 ) == m ) || has_digit_t<N,m,n/10>::value;
};
template<int N,int m> struct has_digit_t<N,m,0> {
	static const bool	value = false;
};
template<> struct has_digit_t<0,0,0> {
	static const bool	value = true;
};

ルールの共通化

ルールにバリエーションが出てくると共通する部分をまとめたくなりますね

#define	is_multiple_of( n, m )	((n)&&!((n)%(m)))
#define	is_fizz( n )	is_multiple_of((n),3)
#define	is_buzz( n )	is_multiple_of((n),5)

bool	has_digit( int	n, int	m ){
	bool	result = ( !n && !m );
	while( n ){
		if(( n % 10 ) == m ){
			result = true;
			break;
		}
		n /= 10;
	}
	return	result;
}
bool	is_funny3( int	n ){
	return	is_multiple_of( n, 3 ) || has_digit( n, 3 );
}
const char*	fizz_buzz_rule_standard( int	n ){
	const char*	result = NULL;
	bool	fizz = is_fizz( n );
	bool	buzz = is_buzz( n );
	if( fizz && buzz ){
		result = "fizz buzz";
	}
	else if( buzz ){
		result = "buzz";
	}
	else if( fizz ){
		result = "fizz";
	}
	return	result;
}
const char*	fizz_buzz_rule_variation1( int	n ){
	const char*	result = NULL;
	if( is_funny3( n )){
		result = "funny";
	}
	return	result;
}
void	fizz_buzz( int	max_value, const char*	(*rule)(int)){
	int	n = 1;
	while( n <= max_value ){
		if( n > 1 ){
			debug_printf( "," );
		}
		const char*	text = rule( n );
		if( text ){
			debug_printf( "%s", text );
		}
		else{
			debug_printf( "%d", n );
		}
		++n;
	}
	debug_printf( "\n" );
}
int	main(){
	fizz_buzz( 20, fizz_buzz_rule_standard );
	fizz_buzz( 20, fizz_buzz_rule_variation1 );
	return	0;
}