質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

1回答

1424閲覧

AtCoderの20210911のA問題のエラー部分の問題点を教えて欲しい

dfher

総合スコア17

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

0グッド

0クリップ

投稿2021/09/16 04:59

編集2021/09/16 05:42

前提・実現したいこと

以下のAtCoderの20210911のA問題を解いております。
https://atcoder.jp/contests/abc218/tasks/abc218_a

REとなったコードの何が問題なのかわからないためご指摘いただきたいです。
(他の方のコードをみて、エラーとならない書き方は分かりました)

発生している問題・エラーメッセージ

RE(Runtime Error)判定がでる ‘プログラムの実行中にエラーが発生しました。コンパイル時に検知できなかったエラーがあります.スタックオーバーフロー、ゼロ除算などが原因です.’

該当のソースコード

java

1import java.util.Scanner; 2 3public class Main{ 4 public static void main(String[] args){ 5 char[] s = new char[7]; 6 7 Scanner scan = new Scanner(System.in); 8 int n = scan.nextInt(); 9 10//以下のfor文でREとなる 11 for(int i=0; i<7; i++){ 12 s[i] = scan.next().charAt(0); 13 } 14 15 if(s[n-1] == 'o'){ 16 System.out.println("Yes"); 17 }else{ 18 System.out.println("No"); 19 } 20 } 21}

試したこと

for文の部分を
char[] s = scan.next().toCharArray();
とするとエラーが出ません。

補足情報(FW/ツールのバージョンなど)

macOS Catalina
Visual Studio Code 1.60.1

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

dodox86

2021/09/16 05:03

ソースコードは、サブミットしたときの全体を示さないと読んだ者に問題点が分かりませんよ。そもそも使っているfor文とやらも見当たりません。
dfher

2021/09/16 05:14

「該当のソースコード」が全体のコードとなります。 また、for文の該当部分は上記「該当のソースコード」の ----------------- //以下のfor文でREとなる for(int i=0; i<7; i++){ s[i] = scan.next().charAt(0); } ----------------- の部分です。書き方まちがっていましたらご指摘ください。
dodox86

2021/09/16 05:19

ご提示のコードですが、コンパイル通りませんよ。まず、ご自身の環境でコンパイル成功~サンプルテストデータで実行してOKなのが前提だと思います。
dodox86

2021/09/16 05:49

コードを書いていきなり実行していませんか。自分の考えるとおり動作しているか、デバッグしていないように見えます。当然ですが、コンパイルが通れば要望通り動くものでもありません。回答は既にいただいているので、以上コメントのみとさせていただきます。
dfher

2021/09/16 05:52

貴重な時間をさいてご回答いただきましてありがとうございました。 大変感謝いたします。
guest

回答1

0

ベストアンサー

とりあえず、デバッグ方法を学んでください。
方法は3種類ほどありますが、ここでは『コードを見る方法』でやってみます。

コードを読むコツは、『一行レベルで、何をしているかを考えながら読む』です。たとえばコメントにするとか。

Java

1import java.util.Scanner; 2 3public class Main{ 4 public static void main(String[] args){ 5  // char型配列sを宣言し、要素数は7とする 6 char[] s = new char[7]; 7 // Scannerクラスのオブジェクトを生成(つまり, コンソールから読み込めるようにする) 8 Scanner scan = new Scanner(System.in); 9 // コンソールから整数一個を取り出す 10 int n = scan.nextInt(); 11 12 // 7回繰り返す 13 for(int i=0; i<7; i++){ 14 // 現在のsの場所に入れていく 15 s[i] = scan.next().charAt(0); 16 } 17 18 // (n-1)番目が 'o'なら 19 if(s[n-1] == 'o'){ 20 // "Yes"と表示 21 System.out.println("Yes"); 22 // それ以外なら 23 }else{ 24 // "No"と表示 25 System.out.println("No"); 26 } 27 } 28}

ですね。

そして、このコメントを書き出す。それを擬似コードとでも呼びましょうか。

1. 配列sを用意し、要素数は7とする 2. Scannerのオブジェクトを生成(= コンソールから読み込めるようにする) 3. コンソールから整数一個分を取り出す 4. 以下を7回繰り返す 4.1. コンソールから読み取って、現在のsの場所に入れていく 5. n-1番目が'o'なら 5.1. "Yes"と表示 6. それ以外( 'o'以外 )なら 6.1. "No"と表示

これを現実世界でシミュレーションしてみましょう、

数学の問題に対する手順とか、手作業でやる手順とかみたいな感覚で。

そう、プログラミングは『こう書けばいい』ではありません。

プログラムは魔法でもなんでもなく、現実世界でのシミュレーションである」です。

で、この疑似コードではなんら問題がなさそうです。
でも明らかに問題がある。

ということで、コードに戻ってみましょうか。

疑似コードでいう(1)~(3)はなんら問題がありませんね。

で、今回は(4)辺りでエラーが起きているようです。
では、その(4)辺りを抜き出してみましょう。

Java

1 for(int i=0; i<7; i++){ 2 s[i] = scan.next().charAt(0); 3 }

ですね。こういう場合は条件式とかが間違っている可能性があります。
なので、とりあえず条件式とその周辺を見てみましょう。

for( int i = 0; i < 7; i++ )...

…これを数直線で考えてみると、そんなに問題はなさそうですね。

では、次は(4.1) にフォーカスしてみましょうか。

Java

1s[i] = scan.next().charAt(0);

これはどういう意味でしょうか。分解してみると、

Java

1String tmp = scan.next(); 2s[i] = tmp.charAt(0);

となるはずです。

で、こういう場合は公式ドキュメント (あるいは公式リファレンスとも) を読んでみましょう。
これは原本に当たるので、正確です。

とりあえず、公式リファレンス(ドキュメント)の「Scannerクラス」を見てみましょう。
バージョンによって異なるようですが、Java Platform SE 8の場合、ここですね。

使っているのは Scannerクラスの nextメソッドですから、ここですね。

上記によると、nextメソッドは一行レベルで読み取るような感じでしょうか。
あー、でもnextLineメソッドもあるので、どう違うのでしょうか。

そういうときは『違い』を付けて検索するといいかもしれません。

例:「Java Scanner next nextLine 違い」

そうすると、私の環境ではここここがヒットしました。

要するに「nextメソッドは『次の空白まで』か『改行まで』」ですね。(空白無しだと改行まで)

完全なトークンの前後には、区切り文字パターンに一致する入力が配置されます

とあるので、「次の区切り文字まで」となりますね。

今回の入力値を見てください。

xoxxoxo のように、『区切り文字を含んでいない』ですね。そうなると、やっぱり『改行までを一つとして読み込む』という感じになってしまいます。

では、この状態で再度、現実世界でシミュレーションしてみてください。

そうすると、

(4)に来た時に、

一行レベルで読み込む( tmp = "xoxxoxo" ) → そのtmpから 0番目を取得する ( s[0] = 'x' ) → また一行レベルで読み込む( ただしもう入力値がないので...? ) →→ 『エラー!!!!!!』

と言う風になっていますね。

ありゃ、なんで複数行読み込もうとしているのでしょうか。

……と考えると、わかるはずです。

こういう場合はとりあえず、プログラミングは置いといて、「現実世界でならどうするか」を考えましょう。

今回の場合、

[依頼] ある人(Aさん)が数字(= n ) と oとxからなる単語(= s )をあなたに言います。 このsは、'o' が 晴れ、'x' が 雨という意味です。 あなたは、Aさんが言った単語sの n番目の文字が 'o' なら「晴れ」、 'x'なら「雨」と言いましょう。

的なものでしょうか。

そうすると、「単純にnとsを受けって s[n] の場所を調べればよくね?」的な感じになりますよね。
(まあ、実際には 配列は0からはじまるので n-1 番目ですが)

で、xoxxoxoとかは一つの文字列と見なせますね。
"o x o x x ..." のように半角スペースとかで区切られている場合はアレですが。

なので、「文字列として受け取る -> s[n-1] を調べる」ってだけでいいはずです。

C言語だと 文字列は 文字の配列版です。ですがこの配列版だと文字列操作が面倒なので、JavaとかではStringクラスとかでくるんでいるだけです
中身はおそらく、charの配列とかでしょうね。(厳密にはワイド文字列とかだろうけど)

投稿2021/09/16 05:39

編集2021/09/16 08:38
BeatStar

総合スコア4962

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

dfher

2021/09/16 05:45

ご回答ありがとうございます。 コードテストで通る状態のコードに修正いたしました。大変失礼いたしました。
dfher

2021/09/16 06:16

お忙しいところ、時間を割いていただきましてありがとうございました。 具体的に流れを書き出して書いてみましたが、解決できませんでした。 引き続きご教授いただいた方法とデバッグ方法について学び、解決させたいと思います。
dodox86

2021/09/16 07:18

デバッグの方法もそうなのですけど、Scannerクラスのメソッドの使い方がただ何となく使っているというかんじなのかな、と。scan.next().charAt(0)は、scan.next()の結果に対してcharAt(0)を行っていますが、それ以前にforで7回ループしています。scan.next()で何が返るかを想定してコード書かないと。
dodox86

2021/09/16 08:28

> 要するに「nextメソッドは『次の空白まで』か『改行まで』」ですね。(空白無しだと改行まで) 細かい話で恐縮ですが、空白までと限らず、それこそ公式では「次の区切り文字パターンまで」です。既定では空白なので、初心の方にはそれで充分だとは思いますが。 https://docs.oracle.com/javase/jp/8/docs/api/java/util/Scanner.html#useDelimiter-java.util.regex.Pattern- 実行例です。(Windows コマンドプロンプト) C> type Main3.java import java.util.Scanner; public class Main{ public static void main(String[] args){ Scanner scan = new Scanner(System.in); System.out.println("delimiter1: " + scan.delimiter().toString()); scan.useDelimiter(","); System.out.println("delimiter2: " + scan.delimiter().toString()); while (scan.hasNext()) { System.out.println(scan.next()); } } } C>java --source 8 Main3.java delimiter1: \p{javaWhitespace}+ delimiter2: , item1,item2,item3, item1 item2 item3 ^Z
BeatStar

2021/09/16 08:34 編集

@ dodox86さん > それこそ公式では「次の区切り文字パターンまで」です. あ、確かにそうですね。修正しておきます。
dfher

2021/09/17 02:36

@BeatStar さん for文のところで、1文字ずつ入力→Enter し、それを7回繰り返すという想定でコードを書いておりましたが、"xoxxoxo"という文字列が入力されてエラーになるということが、実際に入力してみて確認できました。 これを機に、Scannerクラスの他の項やStringクラスなど、じっくり仕様を読み直したいと思います。 知識を深めることができ、うれしいです。 お忙しい中、貴重な時間を使って丁寧にご説明いただきまして、大変感謝いたします。
dfher

2021/09/17 02:45

@dodox86 さん おっしゃる通り、Scannerクラスやそのメソッドについて、なんとなくでしか覚えておりませんでした。 仕様を読み直し、next()メソッドの使い方を理解することができました。 また、文字列の検索はfor文を使うより、toCharArray()を使ったほうが良いということも理解しました。 引き続きコードを見ていただいてありがとうございます。 お忙しいところ貴重な時間を使っていただきまして、ありがとうございました。
dodox86

2021/09/17 02:59

@質問者dfherさん > 文字列の検索はfor文を使うより、toCharArray()を使ったほうが良いということも理解しました。 必ずしもそうでも無くて、Stringのまま指定のインデックス位置の文字を取得することもできます。これを利用すればfor文で検索してもよいし、toCharArray()でchar配列に直す必要もないです。(してもいいけど) C>type Main.java public class Main{ public static void main(String[] args){ String s= "ABCDE"; System.out.printf("[1]=%c, [3]=%c\n", s.charAt(1), s.charAt(3)); } } C>java --source 8 Main.java [1]=B, [3]=D 文字列の取り扱いは競技プログラミングの入出力でも、一般のプログラミングでも基本となる良い題材だと思います。引き続き、ぜひ頑張ってください。(※レス不要です)
dfher

2021/09/17 08:34

@dodox86 さん ありがとうございます。 いろいろな書き方を知ることができ、うれしいです。 より理解を深めて、適切なコードが書けるよう頑張ります。
BeatStar

2021/09/17 09:30

(言いたかった事はdodox86さんの仰ることと一緒です。)
dfher

2021/09/19 21:14

@BeatStar ありがとうございます。よりよいコードの書き方を選択できるよう、知識を深めていきたいです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問