FizzBuzz
FizzBuzz のルール
ループ
すぐにいろいろ思いつかない場合はまずシンプルなものから考える
#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; }