大学の課題です。ヒントだけでもいいのでください 再帰関数です
大学の課題です。ヒントだけでもいいのでください 再帰関数です
問題4. キーボードから(空白類文字を含むかもしれない)1行を入力して,先頭のK文字 と最後の K 文字が順に同じであるかを判定するプログラムを書きたいとする.ただ し,K はマクロで定めなければならず,1 行の長さに上限を勝手に設けてはいけな く,メモリを動的に確保してはいけないとする.また,(入力のために打ち込んだ) 改行文字を判定に含めず,2K 文字未満の 1 行が入力された場合は「undecidable」 を表示すること.
入力例(K=5 のとき) 出力
abcdef_g_abcde same
1234567890123456 different
123456789012345 same
abcdef undecidable
繰り返し文や goto 文を使ってはいけない場合のプログラムを書け.
問題5. 負の整数が入力されるまで、繰り返しキーボードから整数を入力する。入力した 各整数 v について、v より後ろに入力された整数の中に v より大きいものがある ときのみ、v を画面に表示する関数 (配列・ポインタなどは用いない)。
たとえば、キーボードから
5 7 8 10 2 4 3 -1
を入力したときに画面に表示したいのは下記の通り:
2875
問題6. 文字’L’がl個、’R’がr個で構成される文字列をすべて表示する関数。ここで、 l と r は非負整数でキーボードから入力し、l + r ≤ 8 としてよい (高々8 文字が入 る配列を 1 つ用意すると良い)。(参考: ’L’ を選べれば選んで、残り l − 1 個と r 個の文字列作成。同様に’R’ を選べれば選んで · · ·。と両方行えばすべて行ったこ とになる。)
入力例(K=5 のとき) 出力
abcdef_g_abcde same
1234567890123456 different
123456789012345 same
abcdef undecidable
繰り返し文や goto 文を使ってはいけない場合のプログラムを書け.
問題5. 負の整数が入力されるまで、繰り返しキーボードから整数を入力する。入力した 各整数 v について、v より後ろに入力された整数の中に v より大きいものがある ときのみ、v を画面に表示する関数 (配列・ポインタなどは用いない)。
たとえば、キーボードから
5 7 8 10 2 4 3 -1
を入力したときに画面に表示したいのは下記の通り:
2875
問題6. 文字’L’がl個、’R’がr個で構成される文字列をすべて表示する関数。ここで、 l と r は非負整数でキーボードから入力し、l + r ≤ 8 としてよい (高々8 文字が入 る配列を 1 つ用意すると良い)。(参考: ’L’ を選べれば選んで、残り l − 1 個と r 個の文字列作成。同様に’R’ を選べれば選んで · · ·。と両方行えばすべて行ったこ とになる。)
Re: 大学の課題です。ヒントだけでもいいのでください 再帰関数です
問題6 は、問題自体にヒントが書かれているので、その通り書くだけです。
step の中で step を呼び出すときの引数を適当なものにしましょう。
なお、step の第3引数は、s の中で、次に 'L' または 'R' を入れる
場所であり、確定した 'L' と 'R' の個数でもあります。
フォーラムルールに従って、質問してもらえれば、
他の問題もヒントを差し上げましょう。
・使用するプログラミング言語は何ですか?
C ですか、C++ ですか?
・OS とコンパイラは何ですか?
gcc なら -Wall オプションを付けていますか?
Visual C++ なら
Visual Studio でデバッグしていますか?
それとも、コマンドプロンプトで cl コマンドを使っていますか?
少しでもコードを書いて、エラーが出たら、
そのエラーをそのまま書いて質問してください。
#include <stdio.h>
char s[8]
void step(int l, int r, int n)
{
l も r もゼロなら、 // これが再帰の終了条件
s を出力。
そうでないなら、
l がゼロでなければ、
s[n] に 'L' を入れて、step を呼び出す。
r がゼロでなければ
s[n] に 'R' を入れて、step を呼び出す。
}
int main(void)
{
int l, r;
scanf("%d%d", &l, &r);
step(l, r, 0);
}
なお、step の第3引数は、s の中で、次に 'L' または 'R' を入れる
場所であり、確定した 'L' と 'R' の個数でもあります。
フォーラムルールに従って、質問してもらえれば、
他の問題もヒントを差し上げましょう。
・使用するプログラミング言語は何ですか?
C ですか、C++ ですか?
・OS とコンパイラは何ですか?
gcc なら -Wall オプションを付けていますか?
Visual C++ なら
Visual Studio でデバッグしていますか?
それとも、コマンドプロンプトで cl コマンドを使っていますか?
少しでもコードを書いて、エラーが出たら、
そのエラーをそのまま書いて質問してください。
Re: 大学の課題です。ヒントだけでもいいのでください 再帰関数です
これらの問題が難しいのは、様々な制約があるからですよね。
・繰り返し文(while や for)を使ってはいけない
・配列を使ってはいけない
・行の長さやデータの個数に上限があってはいけない
など。
これらの制約がなければ、易しいはずです。
そのプログラムを書いてもらえれば、
制約を満たすプログラムを書くヒントを差し上げましょう。
なお、C++ を使えば、問題6 は、
次のように簡単に書けたりします。
実行例
・繰り返し文(while や for)を使ってはいけない
・配列を使ってはいけない
・行の長さやデータの個数に上限があってはいけない
など。
これらの制約がなければ、易しいはずです。
そのプログラムを書いてもらえれば、
制約を満たすプログラムを書くヒントを差し上げましょう。
なお、C++ を使えば、問題6 は、
次のように簡単に書けたりします。
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
string s;
void step()
{
cout << s << endl;
if (next_permutation(s.begin(), s.end())) step();
}
int main()
{
int l, r;
cin >> l >> r;
s = string(l, 'L') + string(r, 'R');
step();
}
Re: 大学の課題です。ヒントだけでもいいのでください 再帰関数です
問題4かずま さんが書きました: ↑6年前これらの問題が難しいのは、様々な制約があるからですよね。
・繰り返し文(while や for)を使ってはいけない
・配列を使ってはいけない
・行の長さやデータの個数に上限があってはいけない
など。
これらの制約がなければ、易しいはずです。
#include <stdio.h> // fgets, puts
#include <string.h> // strchr, strlen
#define K 5
int main(void)
{
char s[256], *p;
if (!fgets(s, sizeof s, stdin)) return 1; // 1行読み込む
if (p = strchr(s, '\n')) *p = '\0'; // 改行文字を削除
int i = K, j = strlen(s); // 比較すべき文字列の末尾
if (j < 2*K) puts("undecidable"); // 文字数が少ない場合
else {
while (i && s[--i] == s[--j]) ; // 比較すべき文字数分、比較する
puts(i ? "different" : "same"); // 比較すべき文字が残っているか?
}
}
#include <stdio.h> // scanf, printf, putchar
int main(void)
{
int i = 0, m = 0, v, a[100];
while (i < 100 && scanf("%d", &v) == 1 && v >= 0) // 非負数の読み込み
a[i++] = v; // 読み込んだ数の保存
while (--i >= 0) // 後ろから処理
if (a[i] < m) printf("%d ", a[i]); // 後ろの最大値より小さいと表示
else m = a[i]; // a[i] が最大値なら、最大値の更新
putchar('\n');
}
#include <stdio.h> // scanf, puts
#include <string.h> // memset
int main(void)
{
char s[8 + 1]; int i, j, k, n, t;
scanf("%d%d", &i, &j);
memset(s, 'L', i), memset(s + i, 'R', j);
n = i + j;
s[n] = '\0';
while (1) {
puts(s);
for (i = n; --i > 0 && s[i-1] >= s[i]; ) ; // 右端から "LR" を探す
if (i == 0) return 0; // なければ終了
for (j = i, k = n; ++j < --k; ) // "LR" の右側を反転
t = s[j], s[j] = s[k], s[k] = t;
s[i-1] = 'R', s[i] = 'L'; // "LR" を "RL" に置き換える
}
}
「ヒントだけでもいいのでください」というので
ヒントを与えているのに、何の応答もありません。
完全な丸投げですね。
問題6 のヒントなんて、ほとんど解答そのものだったのに。
Re: 大学の課題です。ヒントだけでもいいのでください 再帰関数です
問題4で、繰り返し文(while)を使ったコードを示しましたが、
strncmp を使うと、while をなくせます。
しかし、行の長さに上限があるので、もちろんこれは正解ではありません。
C++ なら上限をなくせます。
問題5 も C++ だと、繰り返し文やデータの個数の上限をなくせます。
C では、こんなことができないので、再帰関数を使うんですね。
質問者が何か言ってきたら、再帰版の C のプログラムをお見せできるんですが。
strncmp を使うと、while をなくせます。
#include <stdio.h> // fgets, puts
#include <string.h> // strchr, strlen, strncmp
#define K 5
int main(void)
{
char s[256], *p;
if (!fgets(s, sizeof s, stdin)) return 1; // 1行読み込む
if (p = strchr(s, '\n')) *p = '\0'; // 改行文字を削除
int i = strlen(s);
if (i < 2*K) // 文字数が少ない場合
puts("undecidable");
else if (strncmp(s, s + i - K, K)) // 先頭と末尾の K文字を比較
puts("different");
else
puts("same");
}
C++ なら上限をなくせます。
#include <iostream> // cin, cout
#include <string> // getline
using namespace std;
#define K 5 // または const int K = 5;
int main()
{
string s;
if (!getline(cin, s)) return 1;
if (s.size() < 2*K)
cout << "undecidable\n";
else if (s.substr(0, K) != s.substr(s.size() - K))
cout << "different\n";
else
cout << "same\n";
}
#include <iostream> // cin, cout, endl
#include <iterator> // istream_iterator, ostream_iterator, back_inserter
#include <vector> // vector
#include <algorithm> // copy
using namespace std;
struct In : istream_iterator<int> {
In() {}
In(istream& i) : istream_iterator<int>(i) {}
bool operator!=(const istream_iterator<int>& x) const {
return **this >= 0 && istream_iterator<int>(*this) != x;
}
};
struct Out : ostream_iterator<int> {
int m;
Out(ostream& o, const char *s) : ostream_iterator<int>(o, s), m(0) { }
Out& operator=(const int& x) {
if (x < m) this->ostream_iterator<int>::operator=(x);
else m = x;
return *this;
}
Out& operator*() { return *this; }
};
int main()
{
vector<int> a;
copy(In(cin), In(), back_inserter(a));
copy(a.rbegin(), a.rend(), Out(cout, " "));
endl(cout);
}
質問者が何か言ってきたら、再帰版の C のプログラムをお見せできるんですが。
Re: 大学の課題です。ヒントだけでもいいのでください 再帰関数です
問題4で、行の長さの制限をなくしてみましょう。
先頭 K文字と末尾 K文字さえあれば、比較結果を表示できます。
すなわち、行全体を保存する必要はないということです。
しかし、繰り返し文を使っているので、もちろんこれは正解ではありません。
やはり再帰関数の使用が必要です。
質問者が何も応答しないのに、私が返信を繰り返すのは、
これらの問題をとても面白いと思うからです。
K が 5 の場合、"abcdef_g_abcde" という 14文字の入力があると、
s1 には "abcde" が入り、s2 には "bcdea" が入り、
n = 14, m = 4 となります。
"bcde" と "a" を交換するため、s3 という別の配列を使用していますが、
これを s2 だけでできないかと考えてみるのも面白いでしょう。
次のプログラムで、n文字の文字列の先頭 m文字と残りの (n-m)文字を交換
するには、関数 swap をどう書けばいいかという問題です。
先頭 K文字と末尾 K文字さえあれば、比較結果を表示できます。
すなわち、行全体を保存する必要はないということです。
#include <stdio.h> // getchar, puts
#define K 5
int main(void)
{
char s1[K + 1], s2[K], s3[K + 1];
int c, n = 0, m = 0;
for (; (c = getchar()) != '\n' && c != EOF; n++) {
if (n < K) s1[n] = c; // 先頭 K文字だけを保存
s2[m] = c; // 末尾 K文字を循環的に保存
if (++m == K) m = 0;
}
if (n < 2*K) puts("decidable");
else {
for (n = 0; n < K; n++) {
s3[n] = s2[m]; // 循環文字列を通常の文字列に変換
if (++m == K) m = 0;
}
s1[K] = s3[K] = '\0';
if (strcmp(s1, s3)) puts("different");
else puts("same");
}
}
やはり再帰関数の使用が必要です。
質問者が何も応答しないのに、私が返信を繰り返すのは、
これらの問題をとても面白いと思うからです。
K が 5 の場合、"abcdef_g_abcde" という 14文字の入力があると、
s1 には "abcde" が入り、s2 には "bcdea" が入り、
n = 14, m = 4 となります。
"bcde" と "a" を交換するため、s3 という別の配列を使用していますが、
これを s2 だけでできないかと考えてみるのも面白いでしょう。
次のプログラムで、n文字の文字列の先頭 m文字と残りの (n-m)文字を交換
するには、関数 swap をどう書けばいいかという問題です。
-
- 記事: 48
- 登録日時: 8年前
Re: 大学の課題です。ヒントだけでもいいのでください 再帰関数です
問題4を私も挑戦しましたが、惨敗でした。
不要や無駄のあるプログラミングをしてしまいました。
コマンドラインの出力結果:
hentaitaihenhentai
same
不要や無駄のあるプログラミングをしてしまいました。
#include<stdio.h>
#include<string.h>
#define K 6
char *GetFirstNStr(int N,char str[])
{
str[N]='\0';
//メモリを破壊しているのでmain関数のstrからstr2にstrcpyする
return str;
}
char *GetLastNStr(int N,char str[])
{
char str2[100];
int LastKPos=strlen(str)-N;
//strncpy(str2,str,K);
//これだとstrの先頭からN個コピーされるので駄目
int i=0;
while(i<N)//i<3
{
str2[i]=str[LastKPos+i];
//printf("str2[%d]=%c\n",i,str2[i]); //for debug
i++;
}
str2[i]='\0';
return str2;
}
int main()
{
char str[100];
char str2[100];
fgets(str,sizeof(str),stdin);
int len=strlen(str);
str[len-1]='\0';
if(strlen(str)<2*K)
{
printf("undecidable\n");
return 0;
}
strcpy(str2,str);//メモリが破壊されるので別の変数にコピー
char *FStr;
char *LStr;
FStr=GetFirstNStr(K,str);//始めN
LStr=GetLastNStr(K,str2);//ラストN
if(strcmp(FStr,LStr)==0) //文字列の比較
printf("same\n");//同じなら
else
printf("different\n");
return 0;
}
hentaitaihenhentai
same
-
- 記事: 48
- 登録日時: 8年前
Re: 大学の課題です。ヒントだけでもいいのでください 再帰関数です
問題5をやってみましたが、これもあまり出来なかったです。
まずfgetsで一行の文字列を取得しようとして、その文字列の中の空白を,に変えて
sscanfで値を得ようとしたのですが、あまりにも多い数字の場合、sscanfのポインタを
受け取る引数の変数が場合によって変わってしまう為、難しかったので、ここはscanfで
誤魔化しました。
どうすれば場合によって引数の数が変わる関数を作るのかが分からなかったのです。
1つの解決策は関数のC++の関数のオーバーロードだと思ったのですが、今回は見送りました。
コマンドプロンプト出力結果
5
7
8
10
2
4
3
-1
2875
まずfgetsで一行の文字列を取得しようとして、その文字列の中の空白を,に変えて
sscanfで値を得ようとしたのですが、あまりにも多い数字の場合、sscanfのポインタを
受け取る引数の変数が場合によって変わってしまう為、難しかったので、ここはscanfで
誤魔化しました。
どうすれば場合によって引数の数が変わる関数を作るのかが分からなかったのです。
1つの解決策は関数のC++の関数のオーバーロードだと思ったのですが、今回は見送りました。
#include<stdio.h>
#define MAX 20
int main()
{
int tbl[MAX];
int cnt=0;
int v=0;
char s[MAX];
while((v & 0x80000000)!=0x80000000)
//負数(最上位ビットが1)じゃなければループを続ける
//負数でint型が4バイトなら8桁の16進数があるはず
//処理系に依存するやり方なのでオススメは出来ないです
{
scanf("%d",&v);
tbl[cnt]=v;
cnt++;
}
int X=1,Y=0;
while(cnt>=0)
//減算カウンタが0未満にならないようにする
{
X=tbl[cnt-1];
Y=tbl[cnt];
if(X<Y && X!=-1)
//前と比べつつ、Xが-1じゃない事を確認して出力
{
printf("%d",X);
}
cnt--;
}
return 0;
}
5
7
8
10
2
4
3
-1
2875
Re: 大学の課題です。ヒントだけでもいいのでください 再帰関数です
コンパイラは何を使っていますか?
警告のメッセージが出ませんか?
VC++ gcc -Wall
a.c: In function 'GetLastNStr':
a.c:32:12: warning: function returns address of local variable [-Wreturn-local-addr]
return str2;
^~~~
Re: 大学の課題です。ヒントだけでもいいのでください 再帰関数です
そのプログラムでは、
v の次の整数が v より大きいときのみ表示、になっていますよ。
問題5 はこのようになっています。
while の判定ですが、
なぜ次のように素直に書かないのですか?
Re: 大学の課題です。ヒントだけでもいいのでください 再帰関数です
まずは、余計な配列を使用する方法。
void swap(char a[], int n, int m)
{
char b[n]; // 可変長配列は C99 でしか使用できない。
memcpy(b, a + m, n - m);
memcpy(b + n - m, a, m);
memcpy(a, b, n);
}
void swap(char a[], int n, int m)
{
char *b = malloc(n);
memcpy(b, a + m, n - m);
memcpy(b + n - m, a, m);
memcpy(a, b, n);
free(b);
}
void swap(char a[], int n, int m)
{
std::string b = std::string(a + m, a + n) + std::string(a, a + m);
std::copy(b.begin(), b.end(), a);
}
#include <stdio.h>
void reverse(char a[], int i, int j) // a[i] から a[j-1] までを反転
{
for (char t; i < --j; i++) t = a[i], a[i] = a[j], a[j] = t;
}
void swap(char a[], int n, int m)
{
reverse(a, 0, m); // 前半 m文字を反転
reverse(a, m, n); // 後半 (n - m)文字を反転
reverse(a, 0, n); // 全体を反転
}
int main(void)
{
char a[] = "defghijklmnopqrstuvwxyzabc";
int n = 26, m = 23;
swap(a, n, m);
puts(a);
}
-
- 記事: 48
- 登録日時: 8年前
Re: 大学の課題です。ヒントだけでもいいのでください 再帰関数です
かずまさんへ
>コンパイラは何を使っていますか?
>警告のメッセージが出ませんか?
今使っているPCがWindows環境ですが、かなり重い(UnityとかVisualStudio2015とか重すぎる)
ので未だにBorlandC++5.5Compilerを使っています。
警告は特に出なかったです。ローカル配列変数を返す関数がまずかったのは
10年前からなんとなく知っていたのですが、何故かは分からなかったです。
>>なぜ次のように素直に書かないのですか? これは、未だにファミコンプログラムのアセンブラの癖が
あって、cとアセンブラ、それからVisualBasicの違いに
影響をそれぞれ受けているせいかもしれないです。
>コンパイラは何を使っていますか?
>警告のメッセージが出ませんか?
今使っているPCがWindows環境ですが、かなり重い(UnityとかVisualStudio2015とか重すぎる)
ので未だにBorlandC++5.5Compilerを使っています。
警告は特に出なかったです。ローカル配列変数を返す関数がまずかったのは
10年前からなんとなく知っていたのですが、何故かは分からなかったです。
>>なぜ次のように素直に書かないのですか? これは、未だにファミコンプログラムのアセンブラの癖が
あって、cとアセンブラ、それからVisualBasicの違いに
影響をそれぞれ受けているせいかもしれないです。
Re: 大学の課題です。ヒントだけでもいいのでください 再帰関数です
問題4
投稿日時が近く、不自然な空白を含めて一字一句同じに近い質問を見つけました。
キーボードから(空白類文字を含むかもしれない)1行を入力して,先頭のK... - Yahoo!知恵袋
ここに答えに近いコードが載っています。
私も解いてみました。
まずはHaskellで。
ただし「キーボードから入力」という所はサボっています。メモリを動的確保しているかもわかりません。
次に、C言語に移植。
投稿日時が近く、不自然な空白を含めて一字一句同じに近い質問を見つけました。
キーボードから(空白類文字を含むかもしれない)1行を入力して,先頭のK... - Yahoo!知恵袋
ここに答えに近いコードが載っています。
私も解いてみました。
まずはHaskellで。
ただし「キーボードから入力」という所はサボっています。メモリを動的確保しているかもわかりません。
judge :: String -> String
judge str = judge_i str "" "" (0 :: Integer) (0 :: Integer)
where
judge_i s s1 s2 l1 l2 =
if l1 < k then -- 入力文字列の最初のk文字を取る(逆順になる)
if null s then "undecidable" -- k文字無かった
else judge_i (tail s) ((head s) : s1) s2 (succ l1) l2
else if null s then -- 入力文字列の残りを(逆順で)取ったら
if l2 < k then "undecidable" -- 文字数が足りているか判定して
else judge_eq s1 s2 -- 最初のk文字と一致しているか判定する
else -- 入力文字列の残りを取っていく
judge_i (tail s) s1 ((head s) : s2) l1 (succ l2)
judge_eq "" _ = "same"
judge_eq _ "" = "undecidable"
judge_eq (s1h:s1t) (s2h:s2t) =
if s1h == s2h then judge_eq s1t s2t
else "different"
k = (5 :: Integer)
main :: IO ()
main = do
putStrLn $ judge "abcdef_g_abcde"
putStrLn $ judge "1234567890123456"
putStrLn $ judge "123456789012345"
putStrLn $ judge "abcdef"
#include <stdio.h>
#ifndef K
#define K 5
#endif
struct node_t {
char c;
struct node_t* next;
};
void judge_eq(struct node_t* s1, struct node_t* s2) {
if (s1 == NULL) {
puts("same");
} else if (s2 == NULL) {
puts("undecidable");
} else {
if (s1->c == s2->c) {
judge_eq(s1->next, s2->next);
} else {
puts("different");
}
}
}
void judge(struct node_t* s1, struct node_t* s2, int l1, int l2) {
struct node_t node;
int c = getchar();
if (l1 < K) { /* 入力文字列の最初のk文字を取る(逆順になる) */
if (c == '\n' || c == EOF) {
puts("undecidable"); /* k文字無かった */
} else {
node.c = c;
node.next = s1;
judge(&node, s2, l1 + 1, l2);
}
} else if (c == '\n' || c == EOF) { /* 入力文字列の残りを(逆順で)取ったら */
if (l2 < K) { /* 文字数が足りているか判定して */
puts("undecidable");
} else {
judge_eq(s1, s2); /* 最初のk文字と一致しているか判定する */
}
} else { /* 入力文字列の残りを取っていく */
node.c = c;
node.next = s2;
judge(s1, &node, l1, l2 < K ? l2 + 1 : l2);
}
}
int main(void) {
judge(NULL, NULL, 0, 0);
return 0;
}
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)
Re: 大学の課題です。ヒントだけでもいいのでください 再帰関数です
問題5
まずはじめに、
まず、「など」の範囲がわかりません。
例えば「配列・ポインタなど(教科書〇〇の△章以降で紹介されている機能)」かもしれないし、
「配列・ポインタなど(この問題の採点者が個人的に嫌いな機能)」かもしれないし、
「配列・ポインタなど(C言語にある言語機能すべて)」かもしれないし…
具体的に何を使ってはいけないのかわからないので、プログラムが書きにくいです。
次に、一般的なパソコン環境においてC言語でポインタを使わずに入出力をするのは難しいと考えられます。
C言語の関数呼び出し演算子()は引数に関数へのポインタを取るので、ポインタを使うことが必要になります。
インラインアセンブラを使ってLinuxのread/writeシステムコールを呼び出す場合、
変数の「アドレス」は使っても「ポインタ」は使わないとみなすことができ、セーフ…なのでしょうか?
しかし、この方法では実行環境が限定されてしまいます。
もちろん、問題文では「C言語」と指定されていないので、他の言語を使うという選択肢もあると考えられます。
さて、ポインタを使って関数を呼び出しているので条件を満たしていないと考えられますが、
とりあえず配列および関数呼び出し以外のポインタを使わずにC言語で実装してみました。
フォーマットの指定も値を読み込む変数の指定もポインタを使うので、printfやscanfは使えないことに注意が必要です。
また、int型の範囲に入らない整数が扱えないだけでなく、
int型の範囲のうちINT_MIN以上-INT_MAX未満の値もうまく扱えない可能性がある不都合があります。
まずはじめに、
この制約は非常に困ります。ぶーすと さんが書きました: ↑6年前配列・ポインタなどは用いない
まず、「など」の範囲がわかりません。
例えば「配列・ポインタなど(教科書〇〇の△章以降で紹介されている機能)」かもしれないし、
「配列・ポインタなど(この問題の採点者が個人的に嫌いな機能)」かもしれないし、
「配列・ポインタなど(C言語にある言語機能すべて)」かもしれないし…
具体的に何を使ってはいけないのかわからないので、プログラムが書きにくいです。
次に、一般的なパソコン環境においてC言語でポインタを使わずに入出力をするのは難しいと考えられます。
C言語の関数呼び出し演算子()は引数に関数へのポインタを取るので、ポインタを使うことが必要になります。
インラインアセンブラを使ってLinuxのread/writeシステムコールを呼び出す場合、
変数の「アドレス」は使っても「ポインタ」は使わないとみなすことができ、セーフ…なのでしょうか?
しかし、この方法では実行環境が限定されてしまいます。
もちろん、問題文では「C言語」と指定されていないので、他の言語を使うという選択肢もあると考えられます。
さて、ポインタを使って関数を呼び出しているので条件を満たしていないと考えられますが、
とりあえず配列および関数呼び出し以外のポインタを使わずにC言語で実装してみました。
フォーマットの指定も値を読み込む変数の指定もポインタを使うので、printfやscanfは使えないことに注意が必要です。
また、int型の範囲に入らない整数が扱えないだけでなく、
int型の範囲のうちINT_MIN以上-INT_MAX未満の値もうまく扱えない可能性がある不都合があります。
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int read_integer(void) {
int negate = 0;
int c;
int result = 0;
/* 符号か対応する数字が出るまで読み飛ばす */
do {
c = getchar();
if (c == EOF) exit(1);
} while (!(c == '-' || c == '+' || isdigit(c)));
/* 符号または最初の一字の処理 */
if (c == '-') {
negate = 1;
} else if (isdigit(c)) {
result = c - '0';
}
/* 残りの文字の処理 */
while (isdigit(c = getchar())) {
result = result * 10 + (c - '0'); /* オーバーフローチェックは省略 */
}
return negate ? -result : result;
}
void print_integer(int n) {
if (n < 0) {
putchar('-');
print_integer(-n);
} else {
if (n > 9) print_integer(n / 10);
putchar(n % 10 + '0');
}
}
int process(void) {
int num = read_integer();
if (num < 0) {
return -1;
} else {
int max = process();
if (max < 0 || num >= max) {
/* 後ろに今の整数より大きい整数は無い */
return num;
} else {
/* 後ろに今の整数より大きい整数がある */
print_integer(num);
return max;
}
}
}
int main(void) {
process();
putchar('\n');
return 0;
}
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)
Re: 大学の課題です。ヒントだけでもいいのでください 再帰関数です
問題4
問題5
問題6 は、#4 の最後のプログラムです。
私は、出題者の意図を次のように解釈しました。
配列を使用すると、データの個数に上限が存在してしまう。
不定長のデータを扱うには、リスト構造を使う方法があるが、
「再帰関数」を使用することで不定長のデータが扱えるので、
ポインタの使用と動的確保を禁止する。
配列やリストの処理には、通常繰り返し文を使用するが、
「再帰関数」を使用することで、繰り返し文が不要になるので、
その使用を禁止する。
繰り返し文と同等の処理を記述できる goto文の使用も禁止する。
"%d" のような文字列リテラルが char の配列という型を持ち、
それが先頭要素へのポインタに変換されるという言語仕様を
持ち出して、配列とポインタの使用を制限するのではなく、
あくまでも「処理するデータの保持」について、配列や
ポインタを使用しないこととする。つまり、コード上で、
配列変数やポインタ変数を使用しなければよい。
関数呼出しで、関数が関数へのポインタに変換されるという
言語仕様もポインタの使用とはみなさない。
さて、問題6 の別解として、こんなものを書いてみました。
#include <stdio.h> // getchar, puts
#define K 5
char s[K];
int step(int n)
{
int c = getchar();
if (c == '\n' || c == EOF) return (n < 2*K) ? -2 : K;
if (n < K) s[n] = c;
n = step(n + 1);
return (n <= 0 || s[--n] == c) ? n : -1;
}
int main(void)
{
switch (step(0)) {
case -2: puts("undecidable"); break;
case -1: puts("different"); break;
default: puts("same");
}
}
#include <stdio.h>
int step(void)
{
int v, m;
scanf("%d", &v);
if (v < 0) return 0;
m = step();
if (m <= v) return v;
printf("%d ", v);
return m;
}
int main(void)
{
step();
putchar('\n');
}
私は、出題者の意図を次のように解釈しました。
配列を使用すると、データの個数に上限が存在してしまう。
不定長のデータを扱うには、リスト構造を使う方法があるが、
「再帰関数」を使用することで不定長のデータが扱えるので、
ポインタの使用と動的確保を禁止する。
配列やリストの処理には、通常繰り返し文を使用するが、
「再帰関数」を使用することで、繰り返し文が不要になるので、
その使用を禁止する。
繰り返し文と同等の処理を記述できる goto文の使用も禁止する。
"%d" のような文字列リテラルが char の配列という型を持ち、
それが先頭要素へのポインタに変換されるという言語仕様を
持ち出して、配列とポインタの使用を制限するのではなく、
あくまでも「処理するデータの保持」について、配列や
ポインタを使用しないこととする。つまり、コード上で、
配列変数やポインタ変数を使用しなければよい。
関数呼出しで、関数が関数へのポインタに変換されるという
言語仕様もポインタの使用とはみなさない。
さて、問題6 の別解として、こんなものを書いてみました。
#include <stdio.h> // scanf, puts
#define B3(x) x,x+1,x+1,x+2
#define B2(x) B3(x),B3(x+1),B3(x+1),B3(x+2)
#define B1(x) B2(x),B2(x+1),B2(x+1),B2(x+2)
#define B B1(0),B1(1),B1(1),B1(2)
#define S3(x) x"RR",x"RL",x"LR",x"LL"
#define S2(x) S3(x"RR"),S3(x"RL"),S3(x"LR"),S3(x"LL")
#define S1(x) S2(x"RR"),S2(x"RL"),S2(x"LR"),S2(x"LL")
#define S S1("RR"),S1("RL"),S1("LR"),S1("LL")
void step(int n, int r, int i)
{
static const char b[] = { B }, *s[] = { S };
if (i < (1 << n)) {
if (b[i] == r) puts(s[i] + 8 - n);
step(n, r, i + 1);
}
}
int main(void)
{
int l, r;
scanf("%d%d", &l, &r);
step(l + r, r, 0);
}
Re: 大学の課題です。ヒントだけでもいいのでください 再帰関数です
すみません。L と R 反対でした。次のように訂正します。かずま さんが書きました: ↑6年前さて、問題6 の別解として、こんなものを書いてみました。