0
2
末尾が改行の二次元配列の書き方
https://atcoder.jp/contests/apg4b/tasks/APG4b_cd?lang=ja
とか
https://paiza.jp/works/mondai/array_utilization_primer/array_utilization_primer__reduse_easy
みたいな二次元配列の中身の後ろに空白を入れるけど末尾だけは空白をつけずに改行を出力する問題ってありますよね
どう書けばいいんでしょうか?
if文を使いたくない
ミスが怖いので末尾かどうかを判定すだけのために条件分岐を書きたくないんです
どう書けばいいかはわかっているが、なにが一番しっくりくるかが知りたいのでQandAではなく意見交換に投稿しました
多分一般的
cpp
1//九九の生成 2#include <iostream> 3using namespace std; 4int main(void) { 5 6 7 for (int i = 1; i <= 9; i++) { 8 9 for (int j = 1; j <= 9; j++) { 10 if (j < 9) 11 std::cout << i * j << " "; //i段目の最後の一回以外空白をつける 12 else 13 std::cout << i * j << std::endl;//最後だけ改行する 14 15 } 16 } 17 18 return 0; 19} 20 21
我流
cpp
1//九九の生成 2#include <iostream> 3#include <string> 4using namespace std; 5int main(void) { 6 7 8 for (int i = 1; i <= 9; i++) { 9 10 string s = "";//sはiループが始まったら空文字列に初期化される 11 for (int j = 1; j <= 9; j++) { 12 13 s += to_string(i * j) + " ";//数字を文字列に変換して全ての数字の後ろに空白をつけたものを文字列sに付け加える 14 15 } 16 s.pop_back();//一文字削除して最後の空白を削る 17 std::cout << s << std::endl; //改行はendlに任せる 18 19 20 } 21 22 return 0; 23}
同じ実行結果
1 2 3 4 5 6 7 8 9 2 4 6 8 10 12 14 16 18 3 6 9 12 15 18 21 24 27 4 8 12 16 20 24 28 32 36 5 10 15 20 25 30 35 40 45 6 12 18 24 30 36 42 48 54 7 14 21 28 35 42 49 56 63 8 16 24 32 40 48 56 64 72 9 18 27 36 45 54 63 72 81
皆さんはこういう二次元配列の列の要素数がn個、空白の数がn-1個の問題はどうやって記述していますか?
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答18件
#1
総合スコア28669
投稿2023/05/07 00:01
for (int j = 1; j <= 9; j++) {
j <= 9 を j < 9 にし、9 の要素は for が終わった後に出力すれば if は無くなりますね。
string.join を実装する言語は多いので、それを参考に自作しても良いのではないかと思います。
#2
総合スコア2
投稿2023/05/07 00:10
編集2023/05/07 00:15解答ありがとうございます
つまりこういう感じですか?
iとjでループの条件が変わるのが少し怖いですけど、文字列sを作る必要がなくなって行数が減らせました
cpp
1#include <iostream> 2 3using namespace std; 4int main(void) { 5 6 7 for (int i = 1; i <= 9; i++) { 8 for (int j = 1; j < 9; j++) { 9 std::cout << i * j << " "; 10 } 11 std::cout << i*9 << std::endl; 12 } 13 14 return 0; 15}
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
#3
総合スコア146050
投稿2023/05/07 00:31
編集2023/05/07 00:36ミスが怖いので末尾かどうかを判定すだけのために条件分岐を書きたくないんです
先頭を判定するほうが楽なので、ループの中身を「先頭でなければ改行」→「列の内容を出力」とすれば末尾を判定する必要はなくなります。
…というより、「ミスが怖い」のならテストをキッチリすればいいだけの話です。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
#4
総合スコア85933
投稿2023/05/07 00:41
「if文を使いたくない」という条件が無ければ、
C++
1 for (int i = 1; i <= 9; i++) { 2 for (int j = 1; j <= 9; j++) { 3 std::cout << i * j << (j<9 ? " " : "\n"); 4 } 5 } 6 return 0;
かと思います。終端判断的なこと j<=9
とj<9
を2回書きたくないという気持ちはわからないでもないです。
まあ、普通は9
のところは定数定義すると思うので、終端判断を2回書いてしまうのでしょうか。
j==9 : "\n" ? " "
と書くか悩むか。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
#5
総合スコア7703
投稿2023/05/07 01:20
ミスが怖いので末尾かどうかを判定すだけのために条件分岐を書きたくない
for (int i = 1; i <= 9; i++) {
ってのは結局i <= 9
で末尾を判定している条件分岐なのですが、これは怖くないのでしょうか。
私は条件分岐はあまり怖くありませんが、マジックナンバーをベタ書きするのは怖いので
C++
1//九九の生成 2#include <iostream> 3const int ROW=9; 4const int COL=9; 5int main(void) { 6 for (int i = 1; i <= ROW; i++) { 7 for (int j = 1; j <= COL; j++) { 8 std::cout << i * j; 9 if (j < COL){ 10 std::cout << ' '; 11 }else{ 12 std::cout << std::endl; 13 } 14 } 15 } 16 return 0; 17}
ですかね。
あるいは、「怖さ」の根っこがループの終了判定と事実上同じ判定を重ねることだったりするならば
C++
1for( int i=1; i<=ROW; i++){ 2 int j=0; 3 for(;;){ 4 std::cout << i * j; 5 if(j>=COL){ 6 std::cout<<std::endl; 7 break; 8 } 9 std::cout<<' '; 10 j++; 11 } 12}
とすることでひとつの判定でループの終了と空白/改行の切り替えができます。
何が「怖い」のかを突き詰めていけばその対策も明らかになるのではないでしょうか。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
#6
総合スコア2
投稿2023/05/07 01:23
編集2023/05/07 01:30九九の表はほとんどの人が知っているので、マジックナンバーの1と9のままのほうが変数をつけるより九九を出力するということがわかりやすいと思ったからそうしました
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
#7
総合スコア20673
投稿2023/05/07 02:53
編集2023/05/07 02:55条件分岐を書きたくないんです
以前、似た様な話がありまして、それを真似てみました。
配列の数を順番に表示していく際に、最後に表示された数だけ","をつけないようにしたい
c++
1#include <iostream> 2#include <array> 3 4int main(void) { 5 std::array<std::string, 2> sep = {" ", ""}; 6 for (int i = 1; i <= 9; i++) { 7 for (int j = 1; j <= 9; j++) { 8 std::cout << i*j << sep[j/9]; 9 } 10 std::cout << std::endl; 11 } 12 return 0; 13}
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
#8
総合スコア8224
投稿2023/05/07 03:52
編集2023/05/07 13:36ループの中で条件判定をしたくないのなら、
C++
1#include <iostream> 2using namespace std; 3 4int main() 5{ 6 for (int i = 1; i <= 9; i++) { 7 const char *s = ""; 8 for (int j = 1; j <= 9; j++) { 9 cout << s << i * j; 10 s = " "; 11 } 12 cout << endl; 13 } 14}
追記
さらに、i と j 以外の変数を使いたくなければ、
C++
1#include <iostream> 2using namespace std; 3 4int main() 5{ 6 for (int i = 1; i <= 9; i++) { 7 cout << i; 8 for (int j = 2; j <= 9; j++) 9 cout << ' ' << i * j; 10 cout << endl; 11 } 12}
追記2
if文が嫌なだけで、条件判定は使ってよいのなら、
C++
1#include <iostream> 2using namespace std; 3 4int main() 5{ 6 for (int i = 1; i <= 9; i++) 7 for (int j = 1; j <= 9; j++) 8 cout << i*j << "\n "[j<9]; 9} 10
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
#10
総合スコア2
投稿2023/05/07 12:35
#9
素直に(?)考えると「N個目の要素を出力する際に,N>=2 であれば既に前の要素が出力されているのだからスペースを入れるべき」
これはすごいわかるんですけど、記述の仕方として最後以外要素の後にスペースを入れる、という書き方のほうが理解されやすいと思ったのでこのタイトルに決めました
if文は嫌だって言っておいてなんですが自分が最初に書いた条件文 if (j < 9) が自分でも理解しにくかったから悩んでいた気がしてきました。
cpp
1if (j < 9)//ぱっと見よくわからない 2 3if(j+1<=9) //隣り合う右側の数字が配列の範囲内なら空白 4 5//もしくは 6bool two_valid_index= 1=<j&&(j+1)<=9; 7 8 if (two_valid_index) 9 std::cout << i * j << " "; //i段目の最後の一回以外空白をつける 10 else 11 std::cout << i * j << std::endl;//最後だけ改行する 12 13
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
#11
総合スコア11996
投稿2023/05/08 01:44
if (j < 9)//ぱっと見よくわからない
とのことですが,この程度のレベルの不安に関しては,コメントでも付しておけば良い話なのではないかな,とか.
(この程度の事柄を理由にしてあえて変に技巧に走った(?)ようなコードに変えてしまうのではなく,実装自体は愚直な形のままにしておくのが良いような所と思えます.)
cpp
1//例えばこんなのを思いついたとして,コレを採用するか?っていったら,自分なら嫌かな. 2int i = 1; 3do{ std::cout << i; }while( ( ++i <= 9 ) && (std::cout<<',') ); 4std::cout << std::endl;
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
#12
総合スコア5686
投稿2023/05/11 03:09
C++20 から range という概念が導入されました。 従来はなんらかの「範囲」を扱うためには開始と終了のイテレータを使っていましたがこれを一体にしたものと考えるとよいでしょう。 range の導入と同時にそれを扱うための機構も整備され、パイプをつなぐように各要素を加工していけるという特徴があります。
C++23 ではそれらの機構に join_with
が追加され、要素の「間」に要素を追加するするという加工が簡単に出来るようになりました。
九九の表を書くならこのように使えます。
cpp
1#include <algorithm> 2#include <iostream> 3#include <ranges> 4 5int main(void) { 6 std::ranges::for_each(std::views::iota(1, 10), [](int y) { std::ranges::for_each(std::views::iota(1, 10) | std::views::transform([y](int x) { return std::to_string(x * y); }) | std::views::join_with(' '), [](auto ch){std::cout << ch;}); std::cout << std::endl; }); 7}
条件分岐が (直接には) 含まれないコードになりました。
が……、これが最初の要素 (または最後の要素) を特別扱いするよりわかりやすいとか間違えにくい書き方になっているとは思えません。 こういった凝った仕組みは複雑なものをマシにするために使うものであって簡単なものを当てはめようとすると迂遠すぎるのはよくあることです。
質問の事例程度の簡単なコードは愚直に条件分岐するのがよいように思います。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
#14
総合スコア1090
投稿2023/05/11 21:41
編集2023/05/12 23:46(j%9 == 0)
c++
1#include <iostream> 2#include <cmath> 3//template <typename T> int sgn(T val) { 4// return (T(0) < val) - (val < T(0)); 5//} 6int main(void) { 7 for (int i = 1; i <= 9; i++) 8 for (int j = 1; j <= 9; j++) 9 //std::cout << i * j << (char)('\n' + (' '-'\n') * sgn(j%9)); 10 //std::cout << i * j << (char)('\n' + (' '-'\n') * std::ceil(j%9/9.0)); 11 //std::cout << i * j << (char)(' ' - (' '-'\n') * std::pow(0,j%9)); 12 std::cout << i * j << (char) (' ' - (' '-'\n') * (j%9 == 0)); 13 return 0; 14}
あれこれやってみたら(j%9 == 0)
でした。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
#16
退会済みユーザー
総合スコア0
投稿2023/05/14 07:05
C++14以降(clangは16~)で動く無理矢理テンプレートのSFINAEとパラメータパックを使って書いてみました。
オススメしてるわけではないです。
cpp
1#include <iostream> 2#include <utility> 3template<typename Out, typename Char, typename Elem> 4void print_content(Out& out, const Char* separator, Elem head) { 5 out << head; 6} 7template<typename Out, typename Char, typename Elem, typename... Elems> 8void print_content(Out& out, const Char* separator, Elem head, Elems... tails) { 9 out << head << separator; 10 print_content(out, separator, tails...); 11} 12template<typename Out, typename Char, typename T, T... SEQ> 13void print(Out& out, const Char* separator, const Char* post, std::integer_sequence<T, SEQ...>) { 14 print_content(out, separator, SEQ...); 15 out << post; 16} 17template<typename T, T NUM> 18constexpr T add(T value) { 19 return value + NUM; 20} 21template<typename T, T NUM> 22constexpr T mul(T value) { 23 return value * NUM; 24} 25template<typename Func, Func FUNC, typename T, T... SEQ> 26constexpr auto make_sequence(std::integer_sequence<T, SEQ...>) { 27 return std::integer_sequence<T, FUNC(SEQ)...>(); 28} 29template<typename T, T... SEQ> 30constexpr auto make_sequence_add1(std::integer_sequence<T, SEQ...> seq) { 31 return make_sequence<T(*)(T), add<T, 1>>(seq); 32} 33template<typename T, T N, T... SEQ> 34constexpr auto make_sequence_mul_n(std::integer_sequence<T, SEQ...> seq) { 35 return make_sequence<T(*)(T), mul<T, N>>(seq); 36} 37template<typename T, T N, T... SEQ, typename Out> 38void print_kuku_n(Out& out, std::integer_sequence<T, SEQ...> seq) { 39 print(out, " ", "\n", make_sequence_mul_n<T, N>(seq)); 40} 41template<typename T, typename Out, T... SEQX, T HEADY> 42void print_kuku(Out& out, std::integer_sequence<T, SEQX...> seqx, std::integer_sequence<T, HEADY>) { 43 print_kuku_n<T, HEADY>(out, seqx); 44} 45template<typename T, typename Out, T... SEQX, T HEADY, T... TAILY> 46void print_kuku(Out& out, std::integer_sequence<T, SEQX...> seqx, std::integer_sequence<T, HEADY, TAILY...>) { 47 print_kuku_n<T, HEADY>(out, seqx); 48 print_kuku<T, Out, SEQX...>(out, seqx, std::integer_sequence<T, TAILY...>()); 49} 50const int ROW = 9; 51const int COL = 9; 52int main() { 53 const auto rowseq = make_sequence_add1(std::make_index_sequence<ROW>()); 54 const auto colseq = make_sequence_add1(std::make_index_sequence<COL>()); 55 using value_type = typename decltype(rowseq)::value_type; 56 print_kuku(std::cout, colseq, rowseq); 57 std::cout.flush(); 58 return 0; 59}
個人的には安全で遅くなければ何でもいいです(提示コードは速度未検証)。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
#17
総合スコア1156
投稿2023/05/15 15:10
C++
1#include <iostream> 2#include <vector> 3#include <string> 4#include <algorithm> 5#include <numeric> 6 7int main() 8{ 9 std::vector<int> vi(9); 10 std::iota(vi.begin(), vi.end(), 1); 11 for (auto n : vi) { 12 std::string s = std::accumulate(vi.begin(), vi.end(), std::string(), 13 [n](std::string acc, int x) {return acc + std::to_string(n * x) + " ";}); 14 std::cout << (s.pop_back(), s) << std::endl; 15 } 16}
C++
1#include <iostream> 2 3int main() 4{ 5 for (int i = 1; i <= 9; i++) { 6 for (int j = 1; j <= 9; j++) { 7 std::cout << i * j << " \n"[j == 9]; 8 } 9 } 10}
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。