アプリ版:「スタンプのみでお礼する」機能のリリースについて

C言語で画面のFという文字を一回転させていますが、ほぼ同じ For文が4つあり、違うのはprintfの中の変数だけなので、これを、関数化したいのですが、どうもうまくいきませんので教えてください。よろしくお願いします。

#include<stdio.h>

int main(void)
{

int dt[5][5]={
{88,■,■,■,88},
{88,■,88,88,88},
{88,■,■,■,88},
{88,■,88,88,88},
{88,■,88,88,88}
};

;
int x;
int y;



for( x=0; x<=4; x++){
printf("\n");
for( y=0; y<=4; y++){
printf("%d", dt[x][y]);
}
}

for( x=0; x<=4; x++){
printf("\n");
for( y=0; y<=4; y++){
printf("%d", dt[4-y][x]);
}
}

for( x=0; x<=4; x++){
printf("\n");
for( y=0; y<=4; y++){
printf("%d", dt[4-x][4-y]);
}
}

for( x=0; x<=4; x++){
printf("\n");
for( y=0; y<=4; y++){
printf("%d", dt[y][4-x]);
}
}

return 0;
}

質問者からの補足コメント

  • つらい・・・

    返信ありがとうございます。 ■の部分は、11 だったのですが、質問の際に、見た目がみにくすぎたので■にしましたが、よく考えてみるとint型なので、無理がありました。

    分からない点は、引数にどんな形で値を設定すればよいかがわかりません。 今考えていることは、printfの部分の変数だけ変えてなんとかならないかということです。
    間違えてはいますが、下記のように書きました。今のままでは、
    関数の引数が0の時でも、xとyの値が引かれてしまいますし、x と Yを入れ替えることもできません。なかんいい方法がないか考えています。

    続く

    No.1の回答に寄せられた補足コメントです。 補足日時:2016/07/29 11:01
  • うーん・・・

    #include<stdio.h>

    void FunctionXY(int a, int b)
    {

    int x = 0;
    int y = 0;

    int dt[5][5]={
    {88,11,11,11,88},
    {88,11,88,88,88},
    {88,11,11,11,88},
    {88,11,88,88,88},
    {88,11,88,88,88}
    };

    for( x=0; x<=4; x++){
    printf("\n");
    for( y=0; y<=4; y++){
    printf("%-d", dt[a-x][b-y]);
    }
    }

    }

    int main(void)
    {
    FunctionXY( 0,0 );
    FunctionXY( 4,4 ); //180℃右回転

    return 0;
    }

      補足日時:2016/07/29 11:03

A 回答 (9件)

#7です。


パラメータを右回転数を与えるようにしました。
右回転数=0 そのまま
右回転数=1 右へ90度
右回転数=2 右へ180度
右回転数=3 右へ270度
---------------------------------------------
#include<stdio.h>
int GetConst(int X,int Y)
{
if (X == -1 || Y== -1) return 4;
return 0;
}
int GetIndex(int base,int rot)
{
if (rot < 0) rot = 3;
if ((base+rot%2)%2 == 0){
return 0;
}else{
if ((base+rot/2)%2 == 0){
return -1;
}else{
return 1;
}
}
}
void FunctionROT(int rot)
{

int x = 0;
int y = 0;
int X1,X2,C1,Y1,Y2,C2;
int dt[5][5]={
{88,11,11,11,88},
{88,11,88,88,88},
{88,11,11,11,88},
{88,11,88,88,88},
{88,11,88,88,88}
};
X1 = GetIndex(1,rot);
Y1 = GetIndex(0,rot);
C1 = GetConst(X1,Y1);
X2 = GetIndex(1,rot-1);
Y2 = GetIndex(0,rot-1);
C2 = GetConst(X2,Y2);

//printf("\nX1=%d Y1=%d C1=%d X2=%d Y2=%d C2=%d\n",X1,Y1,C1,X2,Y2,C2);
for( x=0; x<=4; x++){
printf("\n");
for( y=0; y<=4; y++){
printf("%-d", dt[X1*x+Y1*y+C1][X2*x+Y2*y+C2]);
}
}

}

int main(void)
{
FunctionROT( 0 );
FunctionROT( 1 );
FunctionROT( 2 );
FunctionROT( 3 );

return 0;
}
---------------------------------------------
以下、実行結果です。
8811111188
8811888888
8811111188
8811888888
8811888888

8888888888
1111111111
8888118811
8888118811
8888888888

8888881188
8888881188
8811111188
8888881188
8811111188

8888888888
1188118888
1188118888
1111111111
8888888888
ーーーーーーーーーーーーーー
    • good
    • 0

回転処理をアフィン変換で計算すれば、分岐のない計算式だけで処理できます。



アフィン変換による回転処理
計算後ベクトル座標 = ベクトル座標 * 平行移動(-2,-2) * 回転(deg) * 平行移動(+2,+2)

アフィン変換は行列の記述が面倒なので、計算途中は省略しますが、
最終的には以下の計算式が求まります。

c = cos(deg)
s = sin(deg)
xx = x*c + y*s + (1-c-s)*2
yy = y*c - x*s + (1-c+s)*2

コンソール画面の座標 (x,y) と、回転角度 deg から、変数 dt の座標 (xx,yy) の計算式が決まりましたので、
以下のサンプルのような、条件分岐のない関数を作ることができます。

void displaySquareImage(int dt[5][5], int deg) {
_ int x, y, s, c, xx, yy;
_ s = (deg==90)? 1: (deg==270)? -1: 0;
_ c = (deg==0)? 1: (deg==180)? -1: 0;
_ printf("-- rotate %d deg\n", deg);
_ for (y=0; y<5; y++) {
_ _ for (x=0; x<5; x++) {
_ _ _ xx = x*c + y*s + (1-c-s)*2;
_ _ _ yy = y*c - x*s + (1-c+s)*2;
_ _ _ printf("%02d", dt[yy][xx]);
_ _ }
_ _ puts("");
_ }
}

使い方
displaySquareImage(dt, 0);
displaySquareImage(dt, 90);

さらに汎用化を求めるなら、
* 画像 5x5 以外にも対応
* 90度以外の回転 (計算を double 型にしたり、範囲外判定も増える)
などもご検討ください。
    • good
    • 0

#6です。

関数が望みでしたら、以下ではどうでしょうか。力ずくです。
--------------------------------------------------------------------
#include<stdio.h>
void FunctionXY(int lx,int ly,int lc, int rx,int ry,int rc)
{

int x = 0;
int y = 0;

int dt[5][5]={
{88,11,11,11,88},
{88,11,88,88,88},
{88,11,11,11,88},
{88,11,88,88,88},
{88,11,88,88,88}
};

for( x=0; x<=4; x++){
printf("\n");
for( y=0; y<=4; y++){
printf("%-d", dt[lx*x+ly*y+lc][rx*x+ry*y+rc]);
}
}

}

int main(void)
{
FunctionXY( 1,0,0,0,1,0 );
FunctionXY( 0,-1,4,1,0,0);
FunctionXY( -1,0,4,0,-1,4);

return 0;
}
------------------------------------------------------
以下、実行結果です。
8811111188
8811888888
8811111188
8811888888
8811888888
8888888888
1111111111
8888118811
8888118811
8888888888
8888881188
8888881188
8811111188
8888881188
8811111188
    • good
    • 0

力ずくですが、以下ではどうでしょうか。


-----------------------------------------------------
#include<stdio.h>

int main(void)
{

int dt[5][5]={
{88,11,11,11,88},
{88,11,88,88,88},
{88,11,11,11,88},
{88,11,88,88,88},
{88,11,88,88,88}
};

;
int x;
int y;

#define PRINT(P1,P2){\
for( x=0; x<=4; x++){\
printf("\n");\
for( y=0; y<=4; y++){\
printf("%d", dt[(P1)][(P2)]);\
}\
}\
}


PRINT(x,y)
PRINT(4-y,x)
PRINT(4-x,4-y)

}
-------------------------------------------------------------
以下、実行結果です
8811111188
8811888888
8811111188
8811888888
8811888888
8888888888
1111111111
8888118811
8888118811
8888888888
8888881188
8888881188
8811111188
8888881188
8811111188
    • good
    • 0

Cの関数には「式」は渡せません。

式の計算した値が渡されます。
なので、「式を渡そう」という風に考えてしまうと正解に辿りつけません
/* 関数ポインタを使って渡せないことも無いですが、他言語のラムダ式のような仕組が無いので、面倒なことになります */

そうすると
・呼び出し側から「角度」を受けとる
・角度に合せて、変換式を使い分ける
というのが現実的な方法となります。


真面目に全角度に対応させるなら、回転行列が必要となってきますが、
今回のように4種類しかないのなら、それに0,1,2,3とでも番号を付けて
for( x=0; x<=4; x++){
printf("\n");
for( y=0; y<=4; y++){
/* 渡された「角度」によって、式を使い分けて
x,y→r,c を計算する */
printf("%-d", dt[r][c]);
}
}
とでもすればいいでしょう。
    • good
    • 0

まずは #2 の疑問に対する回答.



「Cの仕様上どうなってんだろ」については「何も決まっていない」となります. そもそも「double」が何なのか仕様で決まっていないんだから, もうどうしようもありません. あと
pi = atan(1.0)*4;
でえられる pi の値は「真の π の値」とはちょっとだけ違います (そして「どっち向きに違うか」はやっぱり仕様上何も決まっていません). だから sin(pi/2) は 1 よりちょっと小さいと思わなきゃならないし, cos(pi/2) は 0 とちょっと違うことを覚悟しなきゃならない. なお悪いことに
・cos(pi/2) は正かもしれないし負かもしれない
・1-sin(pi/2) より abs(cos(pi/2)) のほうが大きい
ので cos(pi/2) + sin(pi/2) は 1 より大きいかもしれないし小さいかもしれないので, 単純に floor で切り捨ててしまうと 0 になっちゃうかもしれない.

つまりこの場合に限定していえば「double を導入する」という判断のせいで「全然ロジックと関係無いトコで苦労」していることになる. ある意味「盛大な自爆」ともいえる.

ということで「正しい」方法:
sin や cos の引数は 4種類しかないんだから, それを int の配列で持つ. 例えば
int sinkpi2[] = { 0, 1, 0, -1 }, coskpi2[] = { 1, 0, -1, 0 };
としておけば sin(kπ/2) や cos(kπ/2) はそれぞれ
sinkpi2[k%4], coskpi2[k%4]
で求まる. これくらいのメモリの余裕はあるでしょ.
    • good
    • 1

あ、座標記述が間違ってますね。

すいません。

dtの座標を次のように考える、って事です。

(-2, -2) (-2, -1) (-2, 0) (-2, 1) (-2, 2)
(-1, -2) (-1, -1) (-1, 0) (-1, 1) (-1, 2)
( 0, -2) ( 0, -1) ( 0, 0) ( 0, 1) ( 0, 2)
( 1, -2) ( 1, -1) ( 1, 0) ( 1, 1) ( 1, 2)
( 2, -2) ( 2, -1) ( 2, 0) ( 2, 1) ( 2, 2)
    • good
    • 0

#1 氏の仰る通りですね。


最初に定義してるデータ型

int dt[5][5]={
      {88,■,■,■,88},
      {88,■,88,88,88},
      {88,■,■,■,88},
      {88,■,88,88,88},
      {88,■,88,88,88}
};

ってのはマズいでしょう。
これは恐らく

char* dt[5][5]={
       {"□","■","■","■","□"},
       {"□","■","□","□","□"},
       {"□","■","■","■","□"},
       {"□","■","□","□","□"},
       {"□","■","□","□","□"}
};

みたいにしないと通らないでしょうね。

それはともかく。

> ほぼ同じ For文が4つあり、違うのはprintfの中の変数だけなので、これを、関数化したい

ってのは良いアイディアですよね。
なるたけ共通機構を纏めちゃって、関数化した方が一般に見通しは良くなります。

そこで。

回転行列を使います。

回転行列:
https://ja.wikipedia.org/wiki/回転行列

恐らく高校数学で・・・習ってると思うんですが・・・・・。

例えば、

□■■■□   □□□□□
□■□□□   ■■■■■
□■■■□ -> □□■□■
□■□□□   □□■□■
□■□□□   □□□□□

となる場合は90度の回転ですよね。
このケースの場合、行列dtの[0][0]である□は回転された行列dt'の[0][4]へ移動してるわけです。
1列目を考えると次のような移動が行われてますね。

dt[0][0]:□ -> dt'[0][4]
dt[1][0]:□ -> dt'[0][3]
dt[2][0]:□ -> dt'[0][2]
dt[3][0]:□ -> dt'[0][1]
dt[4][0]:□ -> dt'[0][0]

こういう状況になってるんですが、このまま扱うのはちょっと見通しが悪いんで、左上左端の座標を[-2][-2]、右下右端の座標を[2][2]と考えます。
つまり、元のdtが次のような座標になってる、と考えます。

(-2, -2) (-2, -1) (-2, 0) (-2, 1) (-2, 2)
(-1, -2) (-1, -1) (-1, 0) (-1, 1) (-1, 2)
( 0, -2) ( 0, -1) ( 0, 0) ( 0, 1) ( 0, 2)
( 1, -2) ( 1, -2) ( 1, -2) ( 1, -2) ( 0, -2)
( 2, -2) ( 2, -2) ( 2, -2) ( 2, -2) ( 0, -2)

要するに中央の■の座標が(0, 0)と敢えて考えます。
そうすると、x->x'、y->y'の座標変換は、Wikipediaの回転行列での演算結果に従うと、

x' = x*cosθ-y*sinθ
y' = x*sinθ+y*cosθ

になりますね。
ここで、元のdtの添字に合わせれば

x' = x*cosθ-y*sinθ+2
y' = x*sinθ+y*cosθ+2

となって、これで当初の「座標変換」が成り立ちます。
基本的にはこいつをdtの添字に突っ込めば、「回転後」の図形が一丁出来上がり、となる筈です。

// ここから
#include<stdio.h>
#include<math.h>

void print_rotated(double theta, char* dt[][5]) {
 int x; double x_prime;
 int y; double y_prime;
 for(x=-2; x<=2; x++){
  printf("\n");
  for(y=-2; y<=2; y++) {
   /* 座標変換 */
   x_prime = x*cos(theta)-y*sin(theta);
   y_prime = x*sin(theta)+y*cos(theta);
   /* ちとココの double -> int の変換がメンド臭い */
   printf("%s", dt[(int)floor(x_prime+2.5)][(int)floor(y_prime+2.5)]);
  }
 }
}

int main(void)
{

 char* dt[5][5]={
  {"□","■","■","■","□"},
  {"□","■","□","□","□"},
  {"□","■","■","■","□"},
  {"□","■","□","□","□"},
  {"□","■","□","□","□"}
 };

 /* 基本的に C の仕様上、円周率が定義されていないので作ります。 */
 double pi = atan(1.0) * 4.0;
 int n;

 for (n = 0; n < 4; n++) {
  print_rotated(n*pi/2, dt);
  printf("\n");
 }

 return 0;
}
// ここまで

注意点としては、僕も大ハマリにハマってたんですが(笑)、基本的に三角関数関係はdoubleを返すんですが、

int -> double

のキャストならいざ知らず、

double -> int

のキャストは甚だ難しい(笑)。
今回の場合、回転自体は90度、180度、270度なんで、三角関数は0か1か-1のdoubleを返してくる筈なんですが、こいつをそのままintにキャストして配列の添字として使おうとしても結果がおかしくなっちまうんですよね。
どうやら「切り捨て」がこっちの想像の斜め上やってるらしくって・・・(笑)。Cの仕様上どうなってんだろ(笑)?
あと、四捨五入のroundってのがANSI Cには無いんです。C99以降の規格にはあるんですが、万が一Visual C++辺り使われた日にゃあ・・・ねぇ(笑)。
そこで、加算する2を2.5にしてfloor(切り捨て)して無理矢理調整しています。
全然ロジックと関係無いトコで苦労するのがCですよ(笑)。全くもう(笑)。

こんなカンジじゃないんですか?
    • good
    • 1
この回答へのお礼

ありがとうございます。長い文章をかいていただいてありがとうございます。まったく考えていたやり方と違ったので、驚きです。試して理解してみます。そのあとに返信します。しかし、こんなものを普通に返信できる人に凄さを感じます(笑)。

お礼日時:2016/07/29 11:14

具体的にどのように考えて、どうやったのですか?


それがどのように「うまくいかった」のでしょうか?

どこで間違えたかがわからなければ、今「正解」のソース見たところで、次には同じところで失敗します。



あと、このプログラム、ちゃんと動作しますか?
intの初期値に■なんてのが入っているし。

for( x=0; x<=4; x++){
printf("\n");
for( y=0; y<=4; y++){
printf("%d", dt[x][y]);
}
}
は間違いではないですが、x,yが連想させる意味との違いに違和感があります。
この回答への補足あり
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!