質問: c#の質問 入力した数字を英単語に変換するというプログラムなのですが、 仕様は 110進数値を入力 a,0から99までの数値とする b,範囲外の場合はエラー表示する 2英単語に変換 3出力する 1は組めたのですが2が全く分かりません。とんでもなく長くなりそうな気がしますし、そもそも「1」と入力したら「one」で「2」と入力したら「two」と表示させることは出来るのですか? c#はまだまだ初心者なので分かりやすいヒントが知りたいです。
回答: > とんでもなく長くなりそうな気がします 長くなりますね。フツーだと。 その「長くなりそうだ」と言うのが「勘」で、大概そういう勘って正しいんですよ。 プログラミングに向いてる人ってのは、結局、 「その長くなりそうなブツを如何に短く書くか」 腐心出来る人なんです。短く書ければ成功、そうじゃなきゃ失敗、で、その短く書く事に挑戦する事を楽しめる人なんです。 このケースだと変換ルーティンをマトモに考えるとクソ長くなるのは一発で分かる。そして文字列を準備しておかなきゃならないのは自明です。 後者はしょうがないとしても、数値はどう英語で表現してるのか・・・・・・。実は、仮にこれが「日本語」だったら割に簡単なんだけど、「英語」ってのがクセモノなんですよ(笑)。英語の知識でルールらしきモノをでっち上げなきゃならない。 結局、キモは数値と英名の対応ルールです。これをif文だけでデッチ上げるのは大変です。 そこで、配列を使います。 全然c#は分からないんで、例によってc#に一番似てると思われるjavaを使ってみます(多分c言語よかjavaに近いでしょう)。 // ここから import java.util.scanner; public class main { public static void main(string[] args) { // 一の位の配列 string[] digits = {"", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"}; // 十の位の配列 string[] tensplace = {"", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"}; // 十代 string[] teens = {"", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"}; int n = new scanner(system.in).nextint(); // 整数の入力 if (n < 0 || n > 99) { // 範囲外 system.out.println("エラー"); // 例外を投げても良いかも } else if (n > 10 && n < 20) { // 十代 system.out.println(teens[n-10]); } else if (n == 0) { // 入力が0だった場合 system.out.println("zero"); } else { int r = n % 10; // 一の位 int q = (int)(n - r)/10; // 十の位 system.out.println(tensplace[q] + " " + digits[r]); } } } // ここまで 結局、何がメンド臭いか、と言うと、英語での数値表現の場合、微妙に十二進法が混ざってる事なんですよね。 幸いな事にそこが絡んでるのは十代のトコロだけ。だからそこの英語表現だけをまずは括りだす、と言うのが一つ目の戦略(フランス語だったら多分僕もぶん投げてる・笑)。 あとは、十の位をどう表現してるのか、の配列と一の位の配列を準備します。 ・・・まぁ、もっと細かくしてもいいけど、例えばteenが付く数値と接頭辞の関係を記述・・・とか言うのも考えられますが、現実的じゃないでしょう。我々は言語学者じゃないですし、そもそもc系の言語は元々そういうのが苦手。そういうのはシンボルを相手に自在にいろいろやれる言語(lisp)とか、性器もとい正規表現が得意な言語(perl)なんかに任せておけば良い。少なくとも、ルールが 1. 10 < n < 20 のnの表現 2. それ以外 で済むのなら現実的に充分実装出来る範疇です。 じゃあ、あとはどう配列を設計するか、ってだけになる。 ここでは例えば string[] digits = {"", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"}; みたいに設計してます。 何で配列の先頭を""としてるのか、と言うと 1. 配列の要素番号と、単純には一桁の入力の数値を対応させる為 2. ゼロ項目が""だったら一桁の数値と二桁の数値で場合分けする必要がなくなる からです。 例えば入力が3だった場合、単に配列digitsから3番目の要素を探してくれば良い。例: digits[3] ==> "three" お忘れかもしれませんが、配列は0から数えたりするんで、一個ズレてるんですよね。 もちろん string[] digits = {"one", "two", "three", "four", "five", "six", "seven", "eight", "nine"}; してn = 3の時、digits[n-1] => "three"でも良いんですが、コード書いてる最中に間違えるかもしれんですし、いずれにせよ、c言語由来(いや、pascalか?)の「良くある手」です。そしてより重要なのは2番目、です。 例えば94と入力された時、我々はそれを9と4に分解せんとイケない。 そして十桁ではtensplace[9] => "ninty"、一桁ではtensplace[4] => "four"、合わせてninty fourと言う文字列を得られます。 でもこの入力システムを汎用的に使うとすると、一桁の数値が入力されると、例えば 3が入力される => 3は0と3に分解される となります。十桁は0、一桁は3、と。 そうすると、十桁の配列の0番目は""なんで結果表示に影響を与えないんですよ。残りの3はdigit[3]に適用されて"three"を返してくる。つまり、単純に"three"だけになるんですね。 データ型の方にこの仕組み、つまり、0番目を""にする、と言う設計をしておかないと、場合によっては数値が10以下の場合、数値が20以上の場合、とif節を増やしてメンド臭い事になる。 その辺の影響を踏まえると各配列の第0要素は""にしておいた方が都合が良い、のです。仮に0が入力されたら、その時だけは例外として"zero"を表示させた方が結果紛れがないんですよね。 と言うわけで・・・宿題だとしたら、目的は「データの上手な設計の仕方・入門編」ってトコかな。上手く配列を設計出来たりすれば、のちのプログラムを書くのが滅茶苦茶ラクになりますよ、と。そう言った題意の問題だと思います。