Report 2b

b1910586 2b:○ Mon May 25 23:32:05 2020


提出日時:2020/05/25

[課題]
演習2.a 2つの列を受け取り、両方の列の内容を交互に並べた列を返す
(例: 「1, 2,3」「4, 5, 6」→「1, 4, 2, 5, 3, 6」。長さが異なる場
合の扱いは好きに決めてよい)。
演習3.c 初項と公比を与えて等比数列(実数値)を生成するような機能を
実現してみよ。詳細は好きに決めてよい。

[ソース]
//---------------------- main ----------------------//
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "crseq.h"

void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}

void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}

void ivec_read(int *a) { iarray_read(a+1, a[0]); }

void ivec_print(int *a) { iarray_print(a+1, a[0]); }

int* ivec_concat_alternate(int a[], int b[]) {
  int *c = ivec_new(a[0]+b[0]);
  for(int i = 1, j = 1, k = 1; i <= a[0]+b[0]; ++i) { 
      if(i%2 == 1) {
          c[i] = a[j];
          j++;
      }
      else
      {
          c[i] = b[k];
          k++;
      }
  }
  return c;
}

bool iarray_equal(int a[], int b[], int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}

void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}

void expect_double(double d1, double d2, char *msg) {
  printf("%s %g:%g %s\n", (d1==d2)?"OK":"NG", d1, d2, msg);
}

int main(void) {
  // 演習2.a unit test
  int a[] = {3,1,2,3}, b[] = {3,4,5,6}, c[] = {6,1,4,2,5,3,6};
  int *p = ivec_concat_alternate(a, b);
  expect_iarray(p, c, 7, "[1,2,3]+[4,5]=[1,4,2,5,3,6]");

  // 演習3.c unit test
  struct crseq *s = crseq_new(2, 3);
  expect_double(crseq_get(s), 2, "2*3^0 = 2");
  crseq_fwd(s); expect_double(crseq_get(s), 6, "2*3^1 = 6");
  crseq_fwd(s); expect_double(crseq_get(s), 18, "2*3^2 = 18");
  crseq_reset(s); expect_double(crseq_get(s), 2, "2*3^0 = 2 (reset)");
  return 0;
}

//---------------------- crseq.h ----------------------//
//------------- constant ratio sequence API -----------//
struct crseq *crseq_new(double s, double r);
double crseq_reset(struct crseq *seq);
double crseq_fwd(struct crseq *seq);
double crseq_get(struct crseq *seq);
void crseq_free(struct crseq *seq);

//---------------------- crseq.c ----------------------//
//--------------- crseq implementation ----------------//
#include <stdlib.h>
#include "crseq.h"


struct crseq { double _value, _ratio, _initial; int _num; };
struct crseq *crseq_new(double s, double r) {
  struct crseq *seq = (struct crseq*)malloc(sizeof(struct crseq));
  seq->_value = s; seq->_ratio = r; seq->_initial = s; seq->_num = 0; return seq;
}
double crseq_reset(struct crseq *pseq) {
  pseq->_value = pseq->_initial; pseq->_num = 0; return pseq->_value;
}
double crseq_fwd(struct crseq *pseq) {
  pseq->_value *= pseq->_ratio; return pseq->_value;
}
double crseq_get(struct crseq *pseq) {
  return pseq->_value;
}
void crseq_free(struct crseq *pseq) {
  free(pseq);
}

[実行結果]
OK [1,2,3]+[4,5]=[1,4,2,5,3,6]
  6  1  4  2  5  3  6
  6  1  4  2  5  3  6
OK 2:2 2*3^0 = 2
OK 6:6 2*3^1 = 6
OK 18:18 2*3^2 = 18
OK 2:2 2*3^0 = 2 (reset)

[説明]
演習2.a: 
ivec_concat_alternateの実装:
まず二つの列を受け取り、新しい列をmallocで作った。その列の大きさ
は受け取った列の和である。
次に、変数j,kで受け取った列をループし、iで結果の列をループした。
iの奇遇性によって、一つ目の列か二つ目の列かを選んだ。
それから、作り上げた新しい列のポインタを返した。
最後に、expect_iarrayで単体テストを実行した。

演習3.c:
まずcrseq.hでAPIを決めた。
crseq_new:新しい数列を作る。
crseq_reset: 値を初期化する。
crseq_fwd: 次の項を求める。
crseq_get: 現在の項を返す。
crseq_free: メモリを放す。

次に、crseq.cで実装を書いた。
structの定義:
    value: 現在の値
    ratio: 公比
    initial: 初期値
    num: 位置

crseq_newで与えられた初期値と公比で新しい数列を作る。
crseq_resetで現在値を保存した初期値に戻し、位置も初期化する。
crseq_fwdで現在値に公比をかける。
crseq_getで現在値を返す。
crseq_freeでポインタをfree関数に渡す。
最後にexpect_doubleを使って、単体テストを実行した。

[考察]
演習2.a:
動的メモリを使って、実行時に列の長さが選べるが、長さ自体が分から
なくなるので、それを管理する必要がある。
また、動的メモリを使えば、メモリのよい管理ができる。必要な時だけ
割り当てる形式だから、もし1000~5000個の変数が必要な時に静的メモ
リの場合、5000個の配列を作らなければいけないが、実際に2000個だけ
使われたら、3000個相当のメモリが無駄に割り当てられているというわ
けである。
しかし、長さがよく変わるなら、長さを変える計算量が多くなるから、
場合に応じて適切なデータ構造を考えなければならない。

演習3.c:
構造体を直接変えるのは良くないので、APIを通して、ユーザに公開し
たい機能だけを定義することができる。
ユーザは構造体がどういう風にできているか知らなくても、操作ができ
るので便利なのである。
さらに、構造体のメンバーの名前の最初に「_」をつければ、ユーザが
直接に参照しないメンバーだということがわかる。
.cファイルをバイナリのライブラリーにコンパイルすれば、ユーザが中
身が分からなくても使える。

[アンケート]
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。
メンバーをアクセスする前にポインタがNULLかどうかチェックすることです。
Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に
付いていないと思うことは何ですか。
単体テストを書く習慣です。
Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
特にないです。

f1810567 2b:○ Mon May 25 21:32:00 2020


提出日付:5/25

[課題の再掲]
演習1e 関数へのポインタを受け取って配列の値を集約した結果を返す
関数を作成し、配列の合計値、配列の最大値を求める


[プログラムのソースと説明]
演習1e
#include <stdio.h>
#include <stdbool.h>

int iarray_inject(int *a, int n, int (*fp)(int, int)){
  int result = *a;
  for(int i=1; i<n; ++i){
    result = (*fp)(result, *(a+i));
  }
  return result;
}

void expect_int(int i1, int i2, char *msg) {
  printf("%s %d:%d %s\n", (i1==i2)?"OK":"NG", i1, i2, msg);
}

int main(void){
  int a[] = {8,5,2,4,1};
  int iadd(int x, int y){return x + y;}
  int imax(int x, int y){return (x > y) ? x : y;}
  int (*fp)(int, int);
  fp = iadd;
  expect_int(iarray_inject(a, 5, iadd), 20, "8+5+2+4+1");
  fp = imax;
  expect_int(iarray_inject(a, 5, imax), 8, "max(8,5,2,4,1)");
}

iarray_injectについてのみ説明する。
まず配列aの先頭を整数型の変数resultに代入しておく。
次に、iを1からn-1まで変化させてループを回す。
受け取った関数ポインタfpを間接参照し、その関数にresultと*(a+i)(1
番目以降の配列の要素)を入力する。その結果をresultに代入し、最後
にiarray_injectの結果としてresultを出力する。

[実行例]
OK 20:20 8+5+2+4+1
OK 8:8 max(8,5,2,4,1)

[考察]
関数を後から渡して値を処理することもできると分かった。しかし、ど
のような関数を渡すかによってiarray_injectの書き方が変わってしま
うので、それだったらポインタを使わずに渡す関数をそのまま書いて処
理すればいいのではと思った。

[課題の再掲]
演習2a 1つの配列を受け取り、その内容を逆順にした列を返す

[プログラムのソースと説明]
演習2a
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
int *ivec_concat(int *a) {
  int *c = ivec_new(a[0]);
  for(int i=1; i<a[0]; ++i) {
    c[i] = a[a[0] - i + 1];
    c[c[0] - i + 1] = a[i];
    } 
  return c;
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
  int a[] = {4,1,2,3,4},  c[] = {4,4,3,2,1};
  int *p = ivec_concat(a);
  expect_iarray(p, c, 5, "[1,2,3,4]>[4,3,2,1]");
  return 0;
}

*ivec_concatについてのみ説明する。
まずa[0]個の要素を持った配列cを作成する。
次に、iを1からa[0]まで変化させてループを回す。
配列cのi番目に配列aのa[0]-i+1番目を代入する。
配列cのa[0]-i+1番目に配列aのi番目を代入する。
最後に配列cを返す。


[実行例]
OK [1,2,3,4]>[4,3,2,1]
  4  4  3  2  1
  4  4  3  2  1

[考察]
配列aに添え字を付ける書き方と*(a+i)のような書き方を区別するべき
なのかどうかよく分からない。

[アンケート]
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。
そのプログラムでアドレスを扱う際に関係のないアドレスを書き換えな
いようにすること。
Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に
付いていないと思うことは何ですか。
アルゴリズムを思いつくのに時間がかかってしまうこと。
Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
関数へのポインタを理解するのに時間がかかってしまった。
一見難しそうでも諦めずに取り組めば理解できる内容だった。

f1910560 2b:○ Sun May 24 09:52:54 2020


2020/05/24

演習1c
何らかの整列アルゴリズムで配列を昇順に整列する関数を作成する。

プログラム
#include<stdio.h>
#include<stdbool.h>

void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}

void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

void swap(int *a, int i, int j){
  int x = a[i];
  a[i] = a[j];
  a[j] = x;
}

int iarray_sort(int *a, int n){
  for(int i = 0; i < n-1; ++i){
    for(int j = i + 1; j < n; ++j){
      if(a[i] > a[j]){
	swap(a, i, j);
      }
    }
  }
}

bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}

void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}

int main(void){
  int a[] = {8,5,2,4,1}, b[] = {1,2,4,5,8}, c[] = {2,4,5,8}; 
  iarray_sort(a, 4); expect_iarray(a, c, 4, "8524 -> 2458");
  iarray_sort(a, 5); expect_iarray(a, b, 5, "85241 -> 12458");
  return 0;
}

結果
./a.out
OK 8524 -> 2458
  2  4  5  8
  2  4  5  8
OK 85241 -> 12458
  1  2  4  5  8
  1  2  4  5  8

説明

基礎プログラミングでやったように、swapで配列内のi番目とj番目を入
れ替えられるようにし、iarray_sortではiを配列の最初から最後の一つ
手前まで動かしjをiの次から最後まで動かし、i番目とj番目を比べj番
目の方が小さければswapを用いて入れ替えた。増やしたテストケースは
最後の最小値を入れないことでどのようになるかを見るという形にした。

考察
最初、今一つ目に置いてあるテストケースを二つ目に置いたら返された
配列は1245となっていた。これは、その時一つ目に置いてあったテスト
ケースによってaの中身が変わっていたからではないかと考え、現在の
形にすると正しく返るようになった。


演習2a
二つの列を受け取り、両方の列の内容を交互に並べた列を返す。

プログラム
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>

void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}

void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}

void ivec_read(int *a) { iarray_read(a+1, a[0]); }

void ivec_print(int *a) { iarray_print(a+1, a[0]); }

int *ivec_alter(int *a, int *b) {
  int *c = ivec_new(a[0]+b[0]);
  if(a[0] == b[0]){
    for(int i = 1; i<= a[0]*2; i = i + 2){
      c[i] = a[(i+1)/2]; c[i+1] = b[(i+1)/2];
    }
  }else if(a[0] < b[0]){
    for(int i = 1; i <= a[0]*2; i = i + 2){
      c[i] = a[(i+1)/2]; c[i+1] = b[(i+1)/2];
    }
    for(int i = a[0]*2+1; i <= a[0]+b[0]; ++i){
      c[i] = b[i-a[0]];
    }
  }else if(a[0] > b[0]){
    for(int i = 1; i <= b[0]*2; i = i + 2){
      c[i] = a[(i+1)/2]; c[i+1] = b[(i+1)/2];
    }
    for(int i = b[0]*2+1; i <= a[0]+b[0]; ++i){
      c[i] = a[i-b[0]];
    }
  }
  return c;
}

bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}

void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}

int main(void){
  int a[] = {4,9,7,5,3}, b[] = {3,2,4,6}, c[] = {7,9,2,7,4,5,6,3}, d[] = {7,2,9,4,7,6,5,3}, e[] = {6,2,2,4,4,6,6};
  int *p = ivec_alter(a, b);
  int *q = ivec_alter(b, a);
  int *r = ivec_alter(b, b);
  expect_iarray(p, c, 8, "[9,7,5,3]+[2,4,6]=[9,2,7,4,5,6,3]");
  expect_iarray(q, d, 8, "[2,4,6]+[9,7,5,3]=[2,9,4,7,6,5,3]");
  expect_iarray(r, e, 7, "[2,4,6]+[2,4,6]=[2,2,4,4,6,6]");
  return 0;
}

結果
 ./a.out
OK [9,7,5,3]+[2,4,6]=[9,2,7,4,5,6,3]
  7  9  2  7  4  5  6  3
  7  9  2  7  4  5  6  3
OK [2,4,6]+[9,7,5,3]=[2,9,4,7,6,5,3]
  7  2  9  4  7  6  5  3
  7  2  9  4  7  6  5  3
OK [2,4,6]+[2,4,6]=[2,2,4,4,6,6]
  6  2  2  4  4  6  6
  6  2  2  4  4  6  6

説明
例題を参考にivec_alterを作った。二つの列の長さが異なる場合は長い
方の残った分を最後に付け足す問いう形にした。まず二つの列の長さを
比べ、それぞれの場合で奇数番目には一つ目の列を、偶数番目には二つ
目の列をそれぞれ入れていった。テストケースは、一つ目の列が長い場
合、二つ目の列が長い場合、両方の長さが同じ場合と考えて三つ作った。

考察
forのカウンタ更新をどのようにするかを迷った。i+2では上手くいかな
かったのでi=i+2とすると上手くいった。最初、テストケースには
ivec_newを使わないため列の最初に長さを入れなければいけないことに
気付かず、上手くいかなかった。しかしそれを修正すると上手くいった。
列の最初に長さを入れる手法は便利だが私がやったように入れ忘れると
上手くいかないので気を付けなければいけないことがわかった。

アンケート
Q1.
&と*の意味と使い方。
Q2.
関数の最初につけるintやvoidなどの使い分け。
Q3.
演習2が面白かった。

f1910563 2b:○ Mon May 25 23:04:36 2020


プログラミング通論レポートB課題 #2
提出日付:5月25日

<演習1.d>
[再掲]

2つの配列 (長さは同じ) を受け取り、2番目の配列の各要素の値を 1
番目の配列 の各要素に足し込む関数 void iarray add(int *a, int
*b, int n) を 作成する。単体テストも作る。

[プログラム]
#include <stdio.h>
#include <stdbool.h>

void iarray_add(int *a, int *b, int n){
  for(int i = 0; i < n; ++i){
       a[i] = a[i] + b[i];
  }
}

bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}

void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}

int main(void){
  int a[] = {8,5,2,4,1}, b[] = {1,1,2,2,3}, c[] = {9,6,4,6,4};
  iarray_add(a, b, 5); expect_iarray(a, c, 5, "8,5,2,4,1 + 1,1,2,2,3 -> 9,6,4,6,4");

  int a1[] = {8,5,2,4,1}, b1[] = {1,1,2,2,3}, c1[] = {9,6,4,6,4};
  iarray_add(a1+1, b1+1, 4); expect_iarray(a1+1, c1+1, 4, "5,2,4,1 + 1,2,2,3 -> 6,4,6,4");

  int a2[] = {8,5,2,4,1}, b2[] = {1,1,2,2,3}, g[] = {7,4,7};
  iarray_add(a2+1, b2+2, 3); expect_iarray(a2+1, g, 3, "5,2,4 + 2,2,3 -> 7,4,7");

  int a3[] = {8,5,2,4,1}, b3[] = {1,1,2,2,3}, c3[] = {9,6,4,6,4};
  iarray_add(a3, b3, 1); expect_iarray(a3, c3, 1, "8 + 1 -> 9");

  int d[] = {2,6,8,4,6,4}, e[] = {3,5,7,3,5,8}, f[] = {5,11,15,7,11,12};
  iarray_add(d, e, 6); expect_iarray(d, f, 6, "2,6,8,4,6,4 + 3,5,7,3,5,8 -> 5,11,15,7,11,12");

  int h[] = {12,3,10}, i[] = {11,12,3}, j[] = {23,15,13};
  iarray_add(h, i, 3); expect_iarray(h, j, 3, "12,3,10 + 11,12,3 -> 23,15,13");

  int k[] = {}, l[] = {}, m[] = {};
  iarray_add(k, l, 0); expect_iarray(k, m, 0, " + -> ");
  return 0;
}

[実行例]
$ ./a.out
OK 8,5,2,4,1 + 1,1,2,2,3 -> 9,6,4,6,4
  9  6  4  6  4
  9  6  4  6  4
OK 5,2,4,1 + 1,2,2,3 -> 6,4,6,4
  6  4  6  4
  6  4  6  4
OK 5,2,4 + 2,2,3 -> 7,4,7
  7  4  7
  7  4  7
OK 8 + 1 -> 9
  9
  9
OK 2,6,8,4,6,4 + 3,5,7,3,5,8 -> 5,11,15,7,11,12
  5 11 15  7 11 12
  5 11 15  7 11 12
OK 12,3,10 + 11,12,3 -> 23,15,13
 23 15 13
 23 15 13
OK  + ->



[説明]
2つの配列 (長さは同じ) を受け取り、2番目の配列の各要素の値を 1
番目の配列 の各要素に足し込む関数iarray_addを作成した。長さが同
じ2つの配列に置いて、2番目の配列の各要素の値をそれぞれ、1番目
の配列の各要素に足し合わせるので、for文で2つの配列の長さ分繰り
返し、配列の0番目から1つずつ、配列aに配列bを足し合わせ、配列a
に代入する。

単体テストは、2つの配列(a,b),(d,e),(h,i),(k,l)を用意し、さらに、
実行結果と照らし合わせるために、2つの配列を足し合わせた
c,g,f,j,mを用意した。a1~a3,b1~b3はそれぞれa,bと同じ配列となって
いるが、実行する際にその配列の一部を実行している。足し合わせた配
列gは配列aとbの要素番号をずらして足し合わせているため、新しく用
意した。それぞれ、実行された結果を用意したものと照らし合わせ、同
じであれば、OK、異なれば、NGと出力する。

[考察]
単体テストでは、同じ2つの配列の組を使って、実行する際に入力する
配列のポインタの値や配列の長さを変え、①配列の2番目の要素から足
し合わせる、②配列aとbの要素番号をずらして足し合わせる、③長さ1
の配列を足し合わせる、というように複数のテストケースを試したがど
れも正しく実行されたため、作成した関数が入力した配列のポインタの
値を読み取って、計算が行われているということがよくわかった。その
他にも、長さ0の2つの配列を足し合わせたところ、長さが0の配列が
出力されたが、関数iarray_addより、for文の中身がi = 0; i < 0とな
るため、中身が実行されていないことがわかる。つまりこの場合は、足
し合わせて長さ0なのではなく、関数が通らないで、配列aが出力され
たということがわかった。結果として、正しい値が得られるため、良い
と考えられる。これを配列aに足し合わせるのではなく、配列aとbを足
した新しい配列を作るとするときも、その配列は配列aとbと同じ長さで
宣言されれば良いので、問題ないと考えられる。

<演習2.b>
[再掲]
1つの配列を受け取り、その内容を逆順にした列を返す関数を作成する。
単体テストも作る。


[プログラム]
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
int *ivec_reverse(int *a) {
  int *c = ivec_new(a[0]);
  for(int i = 1; i <= a[0]; ++i) { c[a[0]-i+1] = a[i]; }
  return c;
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}

void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

void ivec_print(int *a) { iarray_print(a+1, a[0]); }

void expect_iarray(int *a, int *b, char *msg) {
  printf("%s %s\n", iarray_equal(a+1, b+1, a[0])?"OK":"NG", msg);
  ivec_print(a); ivec_print(b);
}

int main(void) {
  int a[] = {3,1,2,3}, b[] = {3,3,2,1};
  int *p = ivec_reverse(a);
  expect_iarray(p, b, "[1,2,3]→[3,2,1]");

  int c[] = {4,4,3,2,5}, d[] = {4,5,2,3,4};
  int *q = ivec_reverse(c);
  expect_iarray(q, d, "[4,3,2,5]→[5,2,3,4]");

  int e[] = {2,-1,-3}, f[] = {2,-3,-1};
  int *r = ivec_reverse(e);
  expect_iarray(r, f, "[-1,-3]→[-3,-1]");

  int g[] = {1,3}, h[] = {1,3};
  int *s = ivec_reverse(g);
  expect_iarray(s, h, "[3]→[3]");

  return 0;
}

[実行例]
$ ./a.out
OK [1,2,3]→[3,2,1]
  3  2  1
  3  2  1
OK [4,3,2,5]→[5,2,3,4]
  5  2  3  4
  5  2  3  4
OK [-1,-3]→[-3,-1]
 -3 -1
 -3 -1
OK [3]→[3]
  3
  3

[説明]
配列の先頭にデータの領域の長さが記録してある1つの配列を入力し、
その内容を逆順にする関数ivec_reverseを作成した。配列aのポインタ
を入力し,配列aと同じサイズの配列cを作り、for文で、配列のcの最後
尾から順に、配列aの内容を最初から代入することで、配列cの内容は配
列aと内容と逆順になるようにした。ポインタcを返すので、関数の宣言
はint *ivec_reverseにした。

単体テストにおいて、配列a,c,e,gを用意し、それを逆順にした時に想
定される配列b,d,f,hを用意した。新しく宣言したポインタpに配列aを
逆順にした配列のポインタを代入した。そして、関数expect_iarrayで
は、実行結果の配列のポインタaと想定される配列のポインタbを渡し結
果が正しいか照らし合わせた。この配列の先頭a[0]は長さの値が格納さ
れているため、2つの配列を比較する関数iarray_equalでは、配列の先
頭を飛ばしたa+1,b+1,そして長さnは先述の通りa[0]を入力した。さら
に、可変長配列の内容の部分だけ出力表示されるような関数ivec_print
で、比較する2つの配列の内容を出力した。他のテストケースにおいて
も同様である。それぞれ、実行された結果を用意したものと照らし合わ
せ、同じであれば、OK、異なれば、NGと出力した。

[考察]
可変長配列では、内容の領域の長さが配列の先頭に格納されているため、
今までのようにわざわざ入力値に配列の長さnをいれる必要がないとい
う利点があると考えた。単体テストでは、要素数を変化させ、さらに内
容を数字の順序がバラバラのものや負の数、さらに、逆にしても変わら
ない長さが1のものをテストケースとして試したが、正しく実行されて
おり、要素数や内容の数字に関係なく、実行されることがわかった。

[アンケート]
Q1. アドレス、ポインタを使うプログラムで注意すべきこ
とは何だと思いますか。
使われている変数が領域を示しているのかその領域を指すポインタなの
かを理解して使うことだと思います。

Q2. ここまでのところで、プログラムを作るときに重要だか
゙自分で身に付いていないと思うことは何ですか。
計算処理が早くて正確なプログラムを作成することだと思います。
Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
アドレスとポインタの概念は理解できたつもりでいますが、難しいです。

f1910567 2b:◎ Mon May 25 21:02:32 2020


プログラミング通論 レポート 2b
提出日付: 2020/05/25

[課題の再掲1]
演習1e: 配列と引数を2つもつ関数のポインタを受け取り、配列の要素
全てを引数としたような関数の結果の結果を返す関数 int iarray
inject(int *a, int n, int (*fp)(int, int)) を作成する。

[プログラムのソース1]
#作成した関数
-------------------------------

int iarray_inject(int *a, int n, int(*fp)(int,int)){
    if(n < 1){printf("Error: size of the array must be 1 or more\n"); return 0;}
    int res = a[0];
    for(int i = 1; i < n; ++i){
        res = fp(res, a[i]);
    }
    return res;
}

-------------------------------

#単体テストを含めたプログラム全体
-------------------------------

#include <stdio.h>
#include <stdbool.h>
void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
char *bool2str(bool b) { return b ? "true" : "false"; }
void expect_bool(bool b1, bool b2, char *msg) {
  printf("%s %s:%s %s\n", (b1==b2)?"OK":"NG",
         bool2str(b1), bool2str(b2), msg);
}
void expect_int(int i1, int i2, char *msg) {
  printf("%s %d:%d %s\n", (i1==i2)?"OK":"NG", i1, i2, msg);
}
void expect_double(double d1, double d2, char *msg) {
  printf("%s %g:%g %s\n", (d1==d2)?"OK":"NG", d1, d2, msg);
}
int iarray_max(int *a, int n){
    int max = a[0];
    for(int i = 1; i < n; ++i){
        if(max < a[i]){
            max = a[i];
        }
    }
    return max;
}
void iarray_add(int *a, int *b, int n){
    for(int i = 0; i < n; ++i){
        a[i] += b[i];
    }
}
 
int iadd(int x, int y) { return x + y; }
int imax(int x, int y) { return (x > y) ? x : y; }
int iarray_inject(int *a, int n, int(*fp)(int,int)){
    if(n < 1){printf("Error: size of the array must be 1 or more\n"); return 0;}
    int res = a[0];
    for(int i = 1; i < n; ++i){
        res = fp(res, a[i]);
    }
    return res;
}
int main(void) {
    int a[] = {8,5,2,4,1}, b[] = {1,3,6,9,1}, d[] = {-1,2,-3,-2,1};
    expect_int(iarray_inject(a, 5, iadd), 20, "8+5+2+4+1");
    expect_int(iarray_inject(d, 5, iadd), -3, "-1+2-3-2+1");
    expect_int(iarray_inject(d, 1, iadd), -1, "-1");
    expect_int(iarray_inject(a, 5, imax), 8, "max(8,5,2,4,1)");
    expect_int(iarray_inject(b+1, 3, imax), 9, "max(3,6,9)");
    expect_int(iarray_inject(d, 5, imax), 2, "max(-1,2,-3,-2,1)");
    expect_int(iarray_inject(a, 1, imax), 8, "max(8)");
    expect_int(iarray_inject(a, 0, iadd), 0, "");
    return 0;
}

-------------------------------

[プログラムの説明1]
#作成した関数の説明
作成した関数 iarray_inject  の引数、返り値は全てint型であり、内
容は以下の通りである。

配列のポインタ、配列の長さ、引数を2つもつ関数のポインタを受け取る。
配列の0番目の値を変数に格納する。

以下iを1から1ずつ足し、配列の長さ未満の間行う。
格納した値と配列のi番目の値を引数として、受け取った関数を呼び出
し、結果を格納する。

格納した値を返り値として返す。

ただし、配列の長さが1未満の時はエラーであることを表示し、0を返す。

#単体テストを含む実行例

$ ./a.out
OK 20:20 8+5+2+4+1
OK -3:-3 -1+2-3-2+1
OK -1:-1 -1
OK 8:8 max(8,5,2,4,1)
OK 9:9 max(3,6,9)
OK 2:2 max(-1,2,-3,-2,1)
OK 8:8 max(8)
Error: size of the array must be 1 or more
OK 0:0

[考察1]
関数 iarray_inject について、受け取った配列の長さが1未満の時を例
外として扱ったことは、配列の長さが0の場合、配列の先頭の(なんらか
の)値が返されることを防ぐためのものである。

また、単体テストの作成のついては配列の長さが配列全体、1、0の場合
について留意して作成した。

[課題の再掲2]
演習3d: テキストと同様の情報隠蔽の手法を用い、値を記録して記録し
た値のうち最大値と最小値を返すような機能を作成する。

[プログラムのソース2]
#APIヘッダファイル
----------------------------
// cdseq.h --- constant difference sequence API.
struct cdseq *cdseq_new(int s, int d);
int cdseq_get(struct cdseq *r);
void cdseq_free(struct cdseq *r);
void cdseq_fwd(struct cdseq *r);
void cdseq_reset(struct cdseq *r);
int cdseq_num(struct cdseq *r);
void cdseq_put(struct cdseq *r);
int cdseq_get_max_puted(struct cdseq *r);
int cdseq_get_min_puted(struct cdseq *r);
void cdseq_print_puted(struct cdseq *r);
void cdseq_change_value_diff_num(struct cdseq *r, int value, int diff, int num);

----------------------------

#API関数群ファイル
----------------------------
// cdseq.c -- cdseq implementation.
#include <stdlib.h>
#include <stdio.h>
#include "cdseq.h"
#define SIZE 10000
//#define SIZE 1
int *ivec_new_(int size) {
    int *a = (int*)malloc((size+1) * sizeof(int));
    a[0] = 0; return a;
}
int *ivec_add_(int *a, int b) {
    //printf("resize: %d\n",a[0]+b);
    a = realloc(a, (1+a[0]+b)*sizeof(int));
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
void ivec_print(int *a) { iarray_print(a+1, a[0]); }

struct cdseq { int value, diff, num, *log; };
struct cdseq *cdseq_new(int s, int d) {
    struct cdseq *r = (struct cdseq*)malloc(sizeof(struct cdseq));
    r->value = s; r->diff = d; r->num = 0; 
    r->log = ivec_new_(SIZE);
    return r;
}
int cdseq_get(struct cdseq *r){
    return r->value;
}
void cdseq_fwd(struct cdseq *r){
    r->value += r->diff; r->num += 1;
}
void cdseq_free(struct cdseq *r){
    free(r->log);
    free(r);
}
void cdseq_reset(struct cdseq *r){
    r->value -= r->diff * r->num; r->num = 0;
}
int cdseq_num(struct cdseq *r){
    return r->num;
}
void cdseq_put(struct cdseq *r){
    if(r->log[0] % SIZE == 0 & r->log[0] != 0){ivec_add_(r->log, SIZE);}
    r->log[0] += 1;
    r->log[r->log[0]] = r->value;
}
int cdseq_get_max_puted(struct cdseq *r){
    int a = r->log[1];
    for(int i = 2; i <= r->log[0]; ++i){
        if(a < r->log[i]){
            a = r->log[i];
        }
    }
    return a;
}
int cdseq_get_min_puted(struct cdseq *r){
    int a = r->log[1];
    for(int i = 2; i <= r->log[0]; ++i){
        if(a > r->log[i]){
            a = r->log[i];
        }     
    }
    return a;
}
void cdseq_print_puted(struct cdseq *r){
    ivec_print(r->log);
}
void cdseq_change_value_diff_num(struct cdseq *r, int v, int d, int n){
    r->value = v; r->diff = d; r->num = n;
}

----------------------------

#実行ファイル(例)
----------------------------
#include <stdio.h>
#include "cdseq.h"
int main(void) {
    struct cdseq *s1 = cdseq_new(1, 9);
    int i;
    for(i = 0; i < 8; ++i) {
        printf("%d: %d\n", cdseq_num(s1), cdseq_get(s1));
        printf("%d: %d\n", cdseq_num(s1), cdseq_get(s1));
        cdseq_fwd(s1);
        if(i < 5) { cdseq_put(s1); printf("puted %d\n", cdseq_get(s1)); }
    }
    printf("%d: %d\n", cdseq_num(s1), cdseq_get(s1));
    printf("%d: %d\n", cdseq_num(s1), cdseq_get(s1));
    printf("max: %d\n",cdseq_get_max_puted(s1));
    printf("min: %d\n",cdseq_get_min_puted(s1));
    printf("puted:"); cdseq_print_puted(s1);
    cdseq_free(s1); 
    return 0;
}

----------------------------

#実行ファイル(単体テスト)
----------------------------
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "cdseq.h"
int expect_int(int i1, int i2, char *msg) {
    printf("%s %d:%d %s\n", (i1==i2)?"OK":"NG", i1, i2, msg);
    return (i1==i2)? 0:1;
}
int main(void) {
    int i;
    int NG = 0;
 
    for(int j = 0; j < 8; ++ j){
        struct cdseq *s1 = cdseq_new(20, -3);
        cdseq_put(s1);
        int max = 20;
        int min = 20;
        for(i = 1; i <= j; ++i) {
            cdseq_fwd(s1);
            cdseq_put(s1);
            
            int value = 20-3*i;
            if(max < value){max = value;}
            if(min > value){min = value;}
        }
        NG += expect_int(cdseq_get_max_puted(s1),max,""); NG += expect_int(cdseq_get_min_puted(s1),min,"");
        printf("puted:"); cdseq_print_puted(s1);
        cdseq_free(s1); 
    }
 
    for(int j = 0; j < 8; ++ j){
        struct cdseq *s1 = cdseq_new(0, -0);
        int max = 10;
        int min = 10;
        for(i = 0; i <= j; ++i) {
            cdseq_change_value_diff_num(s1,10*(int)pow(-3,i),0,i);
            cdseq_put(s1);
            
            int value = 10*(int)pow(-3,i);
            if(max < value){max = value;}
            if(min > value){min = value;}
        }
        NG += expect_int(cdseq_get_max_puted(s1),max,""); NG += expect_int(cdseq_get_min_puted(s1),min,"");
        printf("puted:"); cdseq_print_puted(s1);
        cdseq_free(s1); 
    }
 
    for(int j = 0; j < 8; ++ j){
        struct cdseq *s1 = cdseq_new(0, 0);
        int max = 0;
        int min = 0;
        for(i = 0; i <= j; ++i) {
            cdseq_change_value_diff_num(s1,i*i-2*i,0,i);
            cdseq_put(s1);
            cdseq_put(s1);
            int value = i*i-2*i;
            if(max < value){max = value;}
            if(min > value){min = value;}
        }
        NG += expect_int(cdseq_get_max_puted(s1),max,""); NG += expect_int(cdseq_get_min_puted(s1),min,"");
        printf("puted:"); cdseq_print_puted(s1);
        cdseq_free(s1); 
    }
    struct cdseq *s1 = cdseq_new(20, -3);
    printf("puted:"); cdseq_print_puted(s1);
    cdseq_free(s1);
    printf("NG count: %d\n", NG);
    return 0;
}

----------------------------

[プログラムの説明2]
#作成したプログラムの説明
プログラムはテキストの等差数列生成APIを拡張して作成した。

値を記録する機能の説明の前に、等差数列生成APIの主な変更箇所を述べる。

構造体のメンバに項の番号を格納する変数numを追加した。
関数cdseq_getでは次の値に進まないように変更した。
次の値に進む関数cdseq_fwdを追加した。

#値を記録する機能の説明
値を記録する機能は以下の通りである。

記録される値は可変長の配列に保持され、構造体cdseqのメンバ*logに
配列のポインタが格納される。
関数cdseq_putを呼び出すことで現在の値を記録する。
関数cdseq_get_max_putedは記録した値1個以上のうち最大のものを返す。
関数cdseq_get_min_putedは記録した値1個以上のうち最小のものを返す。
関数cdseq_print_putedは記録した値全てを表示する。

#詳細な説明
値を記録する配列の初期の長さは1001であり、0番目に記録されている
値の数を格納する。
関数cdseq_putが呼び出された時、配列の全てに値が格納されていたら、
配列を1000拡張する。

関数cdseq_get_max_puted、cdseq_get_min_putedは等差数列の性質を用
いることなく、記録された値のみを利用して最大値、最小値を計算する。

#実行例
実行ファイルでは項を0番から8番まで進めながら2回ずつ値を出力し、1
番から5番値を記録する。最後に記録した値の最大値、最小値、記録し
た値を出力する。

実行例は以下の通りである。
----------------------------
$ ./a.out
0: 1
0: 1
puted 10
1: 10
1: 10
puted 19
2: 19
2: 19
puted 28
3: 28
3: 28
puted 37
4: 37
4: 37
puted 46
5: 46
5: 46
6: 55
6: 55
7: 64
7: 64
8: 73
8: 73
max: 46
min: 10
puted: 10 19 28 37 46
----------------------------

#単体テストの実行例
単体テストは、配列のオーバーフローが起きていないこと、記録された
値の最大値、最小値の計算が等差数列の性質に依存しないこと、正しく
値が出力されていることに留意して作成した。

単体テストは配列の初期の長さを2、追加する配列の長さを1として行った。

テストの内容は0番目から0から7番目までの以下の値(最後だけ2回ずつ)
を記録した最大値、最小値の正誤である。
初項20、交差-3の等差数列
初項10、公比-3の等比数列
a_n = n^2 - 2n
ただし等比数列と数列{a_n}は、関数cdseq_change_value_diff_numを用
いて値を変更して行った。この関数はvalue、diff、numを任意の値に変
更する。

単体テストの実行例は以下の通りである。

-----------------------------

$ ./a.out
OK 20:20
OK 20:20
puted: 20
OK 20:20
OK 17:17
puted: 20 17
OK 20:20
OK 14:14
puted: 20 17 14
OK 20:20
OK 11:11
puted: 20 17 14 11
....(一部省略)
....
....
OK 8:8
OK -1:-1
puted:  0  0 -1 -1  0  0  3  3  8  8
OK 15:15
OK -1:-1
puted:  0  0 -1 -1  0  0  3  3  8  8 15 15
OK 24:24
OK -1:-1
puted:  0  0 -1 -1  0  0  3  3  8  8 15 15 24 24
OK 35:35
OK -1:-1
puted:  0  0 -1 -1  0  0  3  3  8  8 15 15 24 24 35 35
puted:
NG count: 0

-----------------------------

[考察2]
配列の初期の長さを2、追加する配列の長さを1として行った単体テスト
の結果が正しかったことから、配列のオーバーフローは起きてないと考
えられる。作成したプログラムの仕様である記録された値の最大値、最
小値の計算が等差数列の性質に依存しないことが等比数列などの結果が
正しく出力されていることから、この仕様が満たされていると考えられ
る。このことから、等差数列以外の同様な数列生成APIに同様の機能を
実装する際に流用することが可能であると考えられる。

[アンケート]
#Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。
メモリとCPU上で具体的にどのような操作を行なっているか把握していること。

#Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に付いていないと思うこ
とは何ですか。
予めプログラム全体の構想を考えること。

#Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
動的メモリ管理、情報隠蔽の手法について学ぶことが出来た。

f1910575 2b:○ Sun May 24 18:01:16 2020


プログラミング通論 第2回 レポート2B

提出日付:5月24日(日)

【選択した課題1】演習1b
(課題の要約)
配列の並び順を逆順にする関数void iarray_reverse(int *a, int n)を
作成し、複数のテストケースで単体テストを実行する。

【方針1】
配列の逆順化については要素数が偶数と奇数で場合分けをする。いずれ
も配列の半分までを逆順になるように配列末尾から順次入れ換えていく。
単体テストのための関数はスライド記載のものを使用する。逆順化を施
した配列と想定される配列の要素が1つでも異なればfalseが返される。

【コード1】
#include <stdio.h>
#include <stdbool.h>

void iarray_reverse(int *a, int n) {
  int keep;
  if (n % 2 == 0) {
    for (int i = 0; i < n/2; i++) {
      keep = a[i];
      a[i] = a[n-i-1];
      a[n-i-1] = keep;
    }
  } else {
    for (int i = 0; i < (n-1)/2; i++) {
      keep = a[i];
      a[i] = a[n-i-1];
      a[n-i-1] = keep;
    }
  }
}

void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}

void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}

int main(void) {
  int a[] = {8,5,2,4,1}, b[] = {1,4,2,5,8}, c[] = {8,5,2,4,1}, d[]={4,2,5,8,1};
  int e[] = {1}, f[] = {1}, g[] = {2,4,1,6}, h[] = {2,4,1,6}, i[] = {3,5,9,1}, j[] = {1,9,5,3};
  iarray_reverse(a, 5); expect_iarray(a, b, 5, "85241 -> 14258");
  iarray_reverse(c, 4); expect_iarray(c, d, 4, "85241 -> 42581");
  iarray_reverse(e, 1); expect_iarray(e, f, 1, "1 -> 1");
  iarray_reverse(g, 1); expect_iarray(g, h, 1, "2416 -> 2416");
  iarray_reverse(i, 4); expect_iarray(i, j, 4, "3591 -> 1953");
  return 0;
}

【実行例1】
OK 85241 -> 14258
  1  4  2  5  8
  1  4  2  5  8
OK 85241 -> 42581
  4  2  5  8
  4  2  5  8
OK 1 -> 1
  1
  1
OK 2416 -> 2416
  2
  2
OK 3591 -> 1953
  1  9  5  3
  1  9  5  3

【解説1】
冒頭は与えられた配列を指定した先頭からの要素数分だけ逆順にする関
数である。指定する要素数が偶数の場合、ちょうど前半の部分を配列の
先頭から順に配列末尾と入れ換えていくことで逆順化する。指定する要
素数が奇数の場合、配列中央の要素の1つ手前まで同様に入れ替えてい
くことで逆順化する。iarray_printは与えられた配列を指定した要素数
分だけ表示する関数である。続くiarray_equalは受け取った2つの配列
が指定した要素数まで全て等しいかを判定する関数である。等しい場合
はtrueを、そうでない場合はfalseを返す。その次のexpect_iarrayは単
体テストの結果を表示する関数である。正しければ"OK"、誤っていれば
"NG"を表示する。ここでiarray_equalが呼び出され、後にmainの中で逆
順化した結果が正しいかの判定に使用される。最後のmain関数内では複
数のテストケースを用意し、iarray_reverseを実行したのち、
expect_iarrayで逆順化の結果が正しいか判定している。

【考察1】
単体テストを実行する際、main内最初の部分で配列aとbの2つを宣言し、
その後の部分で指定要素数だけを変えたところ、"OK"と表示されるはず
のテストケースで"NG"が表示された。iarray_reverseは配列の最大値を
求める操作などとは違い、配列の内容自体を編集する操作である。この
ため2回目以降の逆順化が想定される結果と異なる結果になったと分かっ
た。expect_iarrayの最後に逆順化した配列を元に戻すプログラムを入
れるなどすれば、2回目以降のテストでも同じ配列が使用でき、テスト
ケースの準備が多くなりすぎずに済むのではないかと考えた。

【選択した課題2】演習1c
(課題の要約)
何らかの整列アルゴリズムで配列を昇順に整列する関数void
iarray_sort(int *a, int n)を作成し、複数のテストケースで単体テス
トを実行する。

【方針2】
整列アルゴリズムはバブルソートを適用する。テストケースについては
その都度整列前の配列と想定される整列結果を表す配列を宣言していく
方針で進めた。

【コード2】
#include <stdio.h>
#include <stdbool.h>

void iarray_sort(int *a, int n) {
  int keep;
  for (int i = 0; i < n-1; i++) {
    for (int j = 0; j < n-1; j++) {
      if (a[j+1] < a[j]) {
        keep = a[j];
        a[j] = a[j+1];
        a[j+1] = keep;
      }
    }
  }
}

void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}

void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}

int main(void) {
  int a[] = {8,5,2,4,1}, b[] = {1,2,4,5,8};
  int c[] = {1,1,1,1,1}, d[] = {1,1,1,1,1};
  int e[] = {3}, f[] = {3};
  int g[] = {7,2,4,1,5}, h[] = {1,2,4,5,7};
  int i[] = {2,1,4}, j[] = {2,1,4};
  iarray_sort(a, 5); expect_iarray(a, b, 5, "85241 -> 12458");
  iarray_sort(c, 5); expect_iarray(c, d, 5, "11111 -> 11111");
  iarray_sort(e, 1); expect_iarray(e, f, 1, "3 -> 3");
  iarray_sort(g, 5); expect_iarray(g, h, 5, "72415 -> 12475");
  iarray_sort(i, 1); expect_iarray(i, j, 1, "214 -> 214");
  return 0;
}

【実行例2】
OK 85241 -> 12458
  1  2  4  5  8
  1  2  4  5  8
OK 11111 -> 11111
  1  1  1  1  1
  1  1  1  1  1
OK 3 -> 3
  3
  3
OK 72415 -> 12475
  1  2  4  5  7
  1  2  4  5  7
OK 214 -> 214
  2
  2

【解説2】
冒頭は配列を昇順に整列する関数である。配列先頭から順に隣り合う要
素同士の大小を比較していき、先頭に最小の要素を移動させる。残りの
部分で再び同じ操作を行い、これを配列最後の要素の1つ手前まで繰り
返すことでソートしている。その次の3つの関数は単体テストの結果を
表示するための関数である。最後のmain部分では複数のテストケースに
おいて単体テストを実行している。

【考察2】
単体テストを確認している際に、"OK"と"NG"の両方が出ている場合もあ
れば、全て"OK"の場合もあった。テストケースで変える点として今回で
言えば要素数だけに注目すると、いくらテストケースを列挙しても並び
方(プログラムのふるまい)が似たものになり、意味がないことが分かっ
た。このことから、テストケースとしては例外的な場合を考えるのがよ
いという傾向が実感できた。

【アンケート】
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。
扱っているのがアドレスなのかそこに格納されている値なのか区別することだと思います。
Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に
付いていないと思うことは何ですか。
できるだけ簡潔なプログラムを書くことです。
Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
動的メモリ管理の部分が難しく感じました。

f1910576 2b:○ Mon May 25 15:44:08 2020


学籍番号 1910576
氏名 藤松栞 (ペア;個人作業)
提出日時 5/25


1.1つ目の課題について
1-1.課題の再掲
(1b)配列の並び順を逆順にする関数void iarray_reverse(int *a,int n)を作成する

1-2.描いたプログラム
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
void iarray_reverse(int *a,int n){
    for(int i=0;i<n/2;++i){
      int b=a[i];
    a[i]=a[n-1-i];
    a[n-1-i]=b;
    }
}

bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
  int a[] = {8,5,2,4,1};
  int b[] = {1,4,2,5,8};
  int c[]={1,2,3};
  int d[]={3,2,1};
  iarray_reverse(a, 5); 
  expect_iarray(a, b, 5, "85241 -> 14258");
iarray_reverse(c,3);
 expect_iarray(c,d,3,"123->321");
  return 0;
}

1-3.プログラムの説明と実行例
最初に配列の要素を逆にするために、n-1番目から表示するように与え
られた配列の順序を交換していき、ちょうどその動作を真ん中の要素ま
で繰り返す。ここでi<nとやってしまうと変えてしまった部分が元通り
になってしまう。(上の例でいうと14241と表示されてしまう。)

その後テストケースを作成した。例と同様にして実際に得られた配列と
本来の配列が正しいかを比較していき、正しいならtrue,正しくないな
らfalseを表示し、その後void expect_iarrayで成り立つならOK、成り
立たないならNGと表示するようにした。例で記載されているテストケー
スが上手くいったのでテストケースを追加したら成功した。

OK 85241 -> 14258
  1  4  2  5  8
  1  4  2  5  8
OK 123->321
  3  2  1
  3  2  1

1-4. 考察

今回、要素を逆にする関数がvoidだったため逆にするのに苦労した。も
しintの関数だったら新しく配列を準備してそこにn-1番目の要素から順
に代入していくだけだった。けれど今回はvoidだったため、aでもらっ
た配列をaで返さなくてはいけず、繰り返しで入れ替えを行うと失敗し
てしまった。

2.2つ目の課題について
2-1.課題の再掲
(2b)1つの列を受け取り、内容を逆順にした列を返す。

2-2.描いたプログラム
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
int *ivec_new(int size) {
  int *a = (int*)malloc((size) * sizeof(int));
  a[0] = size; return a;
}

int *ivec_concat(int *a,int n) {
  int *c = ivec_new(n);
  for(int i=0;i<n;++i){
     c[i]=a[n-1-i];
  }
  return c;
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
  
  int b[]={8,5,2,4,1},d[]={1,4,2,5,8};
  int a[]={1,2,3};int c[]={3,2,1};
  int e[]={3,4,5,6},f[]={6,5,4,3};
  int g[]={8,7,1,2,3,4},h[]={4,3,2,1,7,8};
  
  int *q=ivec_concat(b,5);
  expect_iarray(q, d, 5, "[8,5,2,4,1] -> [1,4,2,5,8]");
   int *p=ivec_concat(a,3);
  expect_iarray(p, c, 3, "[1,2,3] -> [3,2,1]");
   int *r=ivec_concat(e,4);
  expect_iarray(r, f, 4, "[3,4,5,6] -> [6,5,4,3]");
   int *s=ivec_concat(g,6);
  expect_iarray(s, h, 6, "[8,7,1,2,3,4] -> [4,3,2,1,7,8]");
  
  
  return 0;
}

2-3.プログラムの説明

最初にmallocを用いて領域の割り当てを行う関数を作成した。サイズを
与えてその分とどの大きさで用いるかを計算する。今回は整数の配列な
のでint型のサイズを使用した。その後配列を逆にする関数を作成した。
ここでは与えられる配列とその配列の要素を渡し、配列の領域をその要
素分与え、aの配列を逆向きに代入していった。その後はテストケース
を作成し、上で得られた関数が本来の関数があっているかどうかを確か
めていった。問題部分に記載されていた例が正しく動いたことを確認し、
自分で実行例を作成して動かした結果が以下のようになった。

OK [8,5,2,4,1] -> [1,4,2,5,8]
  1  4  2  5  8
  1  4  2  5  8
OK [1,2,3] -> [3,2,1]
  3  2  1
  3  2  1
OK [3,4,5,6] -> [6,5,4,3]
  6  5  4  3
  6  5  4  3
OK [8,7,1,2,3,4] -> [4,3,2,1,7,8]
  4  3  2  1  7  8
  4  3  2  1  7  8

2-4.考察

今回mallocを用いた場合と用いらなかった場合で配列の要素を逆にする
プログラムを作成した。実際に描いてmallocを用いた方が楽に逆にする
プログラムが再生できた。

しかし、今回ivec_concat(int *a,int n)としているが本当はサイズを
求める関数int size(int *a)というものを作成し、要素数を数えるよう
にしたかつたが実際に実行すると上手くいかなかったため、このように
要素数をすでに与えることにした。エラーが出た原因が自分ではわから
なかった。実行例の1つめと3つめはうまくいき、2つめだけ上手くいか
なかったり、別の時は1つめにエラーが出て2つめ、3つめは上手くいっ
たりした。そもそもの要素を求める関数が悪いのだとは思うが、個数は
その通りに表示されるので原因が分からなかつたため、直す事が出来な
かった。

そのような点も含めてもmallocで領域割り当てはとても便利だと感じた。

3. アンケート
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。 
配列の何番目とポインタの何番目が同じではないこと

Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に
付いていないと思うことは何ですか。
ポインタはまだ分からないことの方が多いので身に着けていきたい

Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
どんどん予習の時間が短くなっているので頑張りたい。

f1910579 2b:○ Mon May 25 21:11:35 2020


学籍番号:1910579
個人作業
提出日時:5/25

<一つ目の課題>
[課題の再掲]
b. 配列の並び順を逆順にする関数 void iarray revese(int *a, int
n) を作成する。

[ソース]
//逆順
#include<stdio.h>
#include<stdbool.h>
void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

void iarray_reverse(int *a, int n){
  int b[n];
  for(int i=0 ; i < n ; ++i){
        b[i] = a[n-i-1];
  }
  for(int i=0 ; i < n ; ++i){
        a[i] = b[i];
  }
}


bool iarray_equal(int *a, int *b, int n) { for(int i = 0; i < n; ++i) {
if(a[i] != b[i]) { return false; } }
  return true;
}
void expect_iarray(int *a, int *b, int n, char *msg) { 
    printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
    iarray_print(a, n); iarray_print(b, n);
}

int main(void){
    int a[] = {8,5,2,4,1}, b[] = {1,4,2,5,8},c[]={1,4,3,8,5,3,7},
    d[]={7,3,5,8,3,4,1},e[]={4,2},f[]={2,4},g[]={1,4,3,8};
    iarray_reverse(a, 5); //1
    expect_iarray(a, b, 5, "85241 -> 14258");
    iarray_reverse(c, 7); //2
    expect_iarray(c, d, 7, "1438537 -> 7358341");
    iarray_reverse(e, 2); //3
    expect_iarray(e, f, 2, "12 -> 21");
    iarray_reverse(b+1, 2); //4
    expect_iarray(b+1, e, 2, "42 -> 24");
    iarray_reverse(d+3, 4); //5
    expect_iarray(d+3,g, 4, "8341 -> 1438");
    return 0;
}

[説明]
私が作った関数であるiarray_reverseとmain以外の説明は省略する。
まず関数iarray_reverseについて説明する。int *a, int nで配列aとそ
のサイズのnを用意する。

次に配列aと同じサイズで空の配列のb[n]を作った。そして変数iをnま
で反復させて、b[i] = a[n-i-1]でbの配列に真ん中を中心とした反対の
位置のaの要素を代入した。最後にaにbの配列を代入させて、aの配列の
逆順を完了させた。次にmain内の説明について、テキストから引用した
配列a,bに加えc,d,r,f,gも加えた。またテストを//(数字)で番号を割
り振った。テキストの通り、aの配列を逆順にして、それをbの配列と比
較して同じであった時OKと出力できる関数expect_iarrayで処理した。
2と3のテストにおいても同じ処理を行った。4のテストはbの配列を前
から2番目を二つ分逆順になるようにした。5のテストも同じくdの配列
を前から4番目から4つ分逆順にした。

[実行例]
OK 85241 -> 14258
  1  4  2  5  8
  1  4  2  5  8
OK 1438537 -> 7358341
  7  3  5  8  3  4  1
  7  3  5  8  3  4  1
OK 42 -> 24
  2  4
  2  4
OK 42 -> 24
  2  4
  2  4
OK 8341 -> 1438
  1  4  3  8
  1  4  3  8
テストが全てOKになったので私の作った逆順にするプログラムは正しい
と考えられる。

[考察]
今回配列bを作って、そこに逆順の配列を入れてaに代入させたが、先に
bにaの配列を代入させてからaに逆順の配列を入れた方が反復を一回で
済ませられるため、より簡単に処理ができたのかなと考えられる。


<二つ目の課題>
[課題の再掲]
d. 2つの配列(長さは同じ)を受け取り、2番目の各要素の値を1番目の
配列の各要素に足し込む関数 void iarray add(int *a, int *b, int
n) を作成する。

[ソース]
//二つの配列をたす
#include<stdio.h>
#include<stdbool.h>
void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

void iarray_add(int *a, int *b, int n){
    int c[n];
    for(int i=0;i<n;++i){
        c[i]=a[i]+b[i];             
    }
    for(int j=0;j<n;++j){
        a[j]=c[j];
    }
}

void expect_int(int i1, int i2, char *msg) {
printf("%s %d:%d %s\n", (i1==i2)?"OK":"NG", i1, i2, msg);
}
bool iarray_equal(int *a, int *b, int n) { for(int i = 0; i < n; ++i) {
if(a[i] != b[i]) { return false; } }
  return true;
}
void expect_iarray(int *a, int *b, int n, char *msg) { 
    printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
    iarray_print(a, n); iarray_print(b, n);
}

int main(void){
    int a[] = {8,5,2,4,1}, 
    b[] = {1,1,2,2,3}, 
    c[] = {9,6,4,6,4}, 
    d[] = {9,4,5,4,6},
    e[] = {18,10,9,10,10},
    f[] = {4,7},
    g[] = {5,9};
    iarray_add(a, b, 5); 
    expect_iarray(a, c, 5, "85241+11223 -> 96464");
    iarray_add(c, d, 5); 
    expect_iarray(c, e, 5, "96464+94546 -> 181091010");
    iarray_add(b+1, f, 2); 
    expect_iarray(b+1, g, 2, "12+47 -> 59");
    return 0;
}

[説明]
これもiarray_addとmainの関数だけの説明をする。
iarray_addについて配列*a,*bと配列のサイズnを用意して、他に配列c[n]を作った。
変数iを0からn未満まで反復して、aとbを足した物をcに代入する。そし
て変数jを同じく0からn未満まで反復して、aにcを代入させた。これでa
を昇順に並び替えられた。mainではint a[] = {8,5,2,4,1}, b[] =
{1,1,2,2,3}, c[] = {9,6,4,6,4}, d[] = {9,4,5,4,6},e[] =
{18,10,9,10,10},f[] = {4,7},g[] = {5,9};の7つの配列を用意して、
ソースにあるように様々なテストを実行した。

[実行例]
OK 85241+11223 -> 96464
  9  6  4  6  4
  9  6  4  6  4
OK 96464+94546 -> 181091010
 18 10  9 10 10
 18 10  9 10 10
OK 12+47 -> 59
  5  9
以上のことから私が作ったコードは正しいと考えられる。

[考察]
先ほどと同じような内容にはなってしまうが、反復を二回やってしまっ
ているが、演習1bと違って配列を一つ増やさず、a[i] = a[i] + b[i]と
やればもっと簡単な処理にできたのではないかと思われる。

<アンケート>
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。
 --今どのような型を使っているのかしっかり把握すること。

Q2. ここまでのところで、プログラムを作るときに重要だか
゙自分で身に付いていないと思うことは何ですか。
 --1つのやり方に拘らずいろんな方法で試してみること。物事を柔軟に考える力。

Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
 --今回様々なところでつまずいてしまったので、Q2で書いたように柔
 軟に考える力を養って、プログラムを作っていきたい。

f1910583 2b:○ Mon May 25 22:47:51 2020


レポート2B


・課題1b「配列の並び順を逆順にする関数 void iarray_revese(int
*a, int n) を作成する」

-ソースコード-
#include <stdio.h>
#include <stdbool.h>

void iarray_print(int *a, int n){
    for(int i = 0; i < n; i++){
        printf(" %2d", a[i]);
    }
    printf("\n");
}

bool iarray_equal(int *a, int *b, int n) {
    for(int i = 0; i < n; ++i) {
        if(a[i] != b[i]) { return false; }
    }
    return true;
}

void expect_iarray(int *a, int *b, int n, char *msg) {
    printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
    iarray_print(a, n); 
    iarray_print(b, n);
}

void iarray_reverse(int *a, int n){
    int i, tmp;
    for(i = 0; i < n/2; i++){
        tmp = a[n-i-1];
        a[n-i-1] = a[i];
        a[i] = tmp;
    }
}

int main(void){
    int a[] = {8,5,2,4,1}; 
    int b[] = {1,4,2,5,8};
    iarray_reverse(a, 5);
    expect_iarray(a, b, 5, "85241 -> 14258");
    int c[] = {-1,-2,-3,-4};
    int d[] = {-4,-3,-2,-1};
    iarray_reverse(c, 4);
    expect_iarray(c, d, 4, "-1-2-3-4 -> -4-3-2-1");
    int e[] = {0};
    int f[] = {0};
    iarray_reverse(e, 1);
    expect_iarray(e, f, 1, "0 -> 0");
    return 0;
}

-実行例-
OK 85241 -> 14258
  1  4  2  5  8
  1  4  2  5  8
OK -1-2-3-4 -> -4-3-2-1
 -4 -3 -2 -1
 -4 -3 -2 -1
OK 0 -> 0
  0
  0

-解説-
iarray_revese関数は、配列aと配列aの要素数nを受け取り、並び順を逆
順にする関数である。
この関数のfor文内では、tmpという一時保存用の変数を使って、配列a
のn-i-1番目の要素とi番目の要素を入れ替えてから、iをインクリメン
トしている。iがnの半分を超えると、要素の入れ替えをやめる。

-考察-
iarray_reverse(int *a, int n)関数は配列aの要素数nを受け取ってい
るが、配列aの要素数nは「n = sizeof(a) / sizeof(a[0])」
と求めることができると考え、下記のように関数を作り直した。
void iarray_reverse(int *a){
    int i, tmp, n;
    n = sizeof(a) / sizeof(a[0]);
    for(i = 0; i < n/2; i++){
       /*省略*/
    }
}
しかし、この関数は思ったように動かなかった。これは、いろいろな要
素数の配列aを渡してもsizeof(a)が常に4となることが原因だとわかっ
た。sizeof(a)が4となる理由は、sizeof(a)が配列aのメモリサイズでは
なくポインタ型のサイズを計算しているからだと考える。

・課題2a「2つの列を受け取り、両方の列の内容を交互に並べた列を返す。」

-ソースコード-
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>

int *ivec_new(int size){
    int *a = (int*)malloc((size+1)* sizeof(int));
    a[0] = size; return a;
}

int *ivec_alternate(int *a, int *b){
    int *c = ivec_new(a[0]+b[0]);

    if (a[0] != b[0]){ 
        for(int i = 1; i <= a[0]+b[0]; i++) {c[i] = 0;}
        return c;
        //配列aとbの長さが違うときは0のみの配列を返す。
    }

    for(int i = 1; i <= a[0]; ++i){
        c[2*i-1] = a[i];
    }
    for(int i = 1; i <= b[0]; ++i){
        c[2*i] = b[i];
    }
    return c;
}

bool iarray_eqaul(int *a, int *b, int n){
    for(int i = 0; i < n; i++){
        if(a[i] != b[i]){ return false;}
    }
    return true;
}

void iarray_print(int *a, int n){
    for(int i = 0; i < n; ++i){
        printf(" %2d", a[i]);
    }
    printf("\n");
}

void expect_iarray(int *a, int *b, int n, char *msg){
    printf("%s %s\n", iarray_eqaul(a, b, n)?"OK":"NG", msg);
    iarray_print(a, n); iarray_print(b, n);
}

int main(void){
    int a[] = {3,1,2,3}, b[] = {3,4,5,6}, c[] = {6,1,4,2,5,3,6};
    int *p1 = ivec_alternate(a, b);
    expect_iarray(p1, c, 7,"[1,2,3]+[4,5,6]=[1,4,2,5,3,6]");

    int d[] = {1,12}, e[] = {1,21}, f[] ={2,12,21};
    int *p2= ivec_alternate(d, e);
    expect_iarray(p2, f, 3,"[12]+[21]=[12,21]");

    //長さが異なるときは0のみの配列にする
    int g[] = {4,1,1,1,1}, h[] = {2,-1,-1}, i[] = {6,0,0,0,0,0,0};
    int *p3 = ivec_alternate(g, h);
    expect_iarray(p3, i, 7,"[1,1,1,1]+[-1,-1]=[0,0,0,0,0,0]");

    int k[] = {1,9}, l[] = {3,8,7,6}, m[] ={4,0,0,0,0};
    int *p4= ivec_alternate(k, l);
    expect_iarray(p4, m, 5,"[9]+[8,7,6]=[0,0,0,0]");

    return 0;
}

-実行例-
OK [1,2,3]+[4,5,6]=[1,4,2,5,3,6]
  6  1  4  2  5  3  6
  6  1  4  2  5  3  6
OK [12]+[21]=[12,21]
  2 12 21
  2 12 21
OK [1,1,1,1]+[-1,-1]=[0,0,0,0,0,0]
  6  0  0  0  0  0  0
  6  0  0  0  0  0  0
OK [9]+[8,7,6]=[0,0,0,0]
  4  0  0  0  0
  4  0  0  0  0

-解説-
ivec_alternate関数は、配列aとbを受け取り、aとbの配列の要素を交互
に並べた新たな列cを返す関数である。まず配列aと配列bの長さを足し
た数と同じ長さの列cをつくる。配列aと配列bの長さが異なる場合は、
列cの0番目の要素以外全てに0を代入する。2つの配列の長さが等しい場
合は、列cの2*i-1(奇数)番目の要素には配列aの要素を、2*i(0以外の偶
数)番目には配列bの要素を代入する。

-考察-
今回は配列aと配列bの長さが異なる場合の扱いは好きに決めてよいとあっ
たので、全て0を代入するようにした。ところで長さが異なる場合を一
切考慮しないで、例えばa[]={4,1,1,1,1}, b[]={2,-1,-1}をくっつける
と、[6 1 -1 1 -1 1 -2084533197]のように、想定外の値が表示される
ことがある。このような事態を防ぐための例外処理は重要である。だが
全て0にしてしまうと味気なく感じるので、余った要素はそのまま後ろ
にくっつける等の工夫はなるべくしたほうが良いと思った。

-アンケート-
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。
    配列名は先頭要素のアドレスになるということ。
Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に
付いていないと思うことは何ですか。
    構造体に関する基礎知識が足りない。
Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ
    難しかった。

h1810533 2b:○ Mon May 25 01:48:56 2020


「個人作業」
5月25日
1、配列の並び順を逆にする関数iarray_reverseとその単体テスト

ソースコード
#include <stdio.h>
#include <stdbool.h>
void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
  printf("%d> ", i+1); scanf("%d", a+i);
  }
  }
  void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
  }
  void iarray_reverse(int *a, int n){
  for(int i = 0; i < n-1-i; i++){
  if(i != n-1-i){
  int x = a[i];
  a[i] = a[n-1-i];
  a[n-1-i] = x;
  }
  }
  }
  bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
  if(a[i] != b[i]) { return false; }
  }
  return true;
  }
  void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
  }
  int main(void) {
  int a[] = {8,5,2,4,1}, b[] = {1,4,2,5,8}, c[] = {3,2,1,7,9,5}, d[] = {5,9,7,1,2,3};
  iarray_reverse(a, 5);
  iarray_reverse(c, 6);
  expect_iarray(a, b, 5, "85241 -> 14258");
  expect_iarray(c, d, 6, "321795 -> 597123");
  return 0;
  }

説明

関数iarray_reverseでは長さnの配列aを受け取り、iを0から増加させ、
i < n-1-iのあいだa[i] とa[n-1-i]の要素を交換する。
単体テストでは、関数の実行結果と目的の結果を出力する。単体テストの結果は
OK 85241 -> 14258
1  4  2  5  8
1  4  2  5  8
OK 321795 -> 597123
5  9  7  1  2  3
5  9  7  1  2  3
	となった。

考察
関数iarray_reverse中のn-1-iをn-iに変化させると単体テストの実行結果は
NG 85241 -> 14258
6422284  1  4  2  5
3  4  2  5  8
NG 321795 -> 597123
1  5  9  7  1  2
5  9  7  1  2  3
となる。この実行結果になった理由は、i=0のとき実行結果の配列の0番
の値に存在しないn番の値が入り、
i=1以降は1番目以降からの配列の並びが逆にされたためと考えられる。
一つ目のテストで目的の結果が変化しているのは、異常な値と
expect_iarrayで比較した結果起こった不具合と考えられる。

2、二つの列を受け取りその内容を交互に並べるプログラム
#include <stdio.h>
#include <stdlib.h>
void iarray_read(int *a, int n) {
for(int i = 0; i < n; ++i) {
printf("%d> ", i+1); scanf("%d", a+i);
}
}
void iarray_print(int *a, int n) {
for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
printf("\n");
}
int *ivec_new(int size) {
int *a = (int*)malloc((size+1) * sizeof(int));
a[0] = size; return a;
}
void ivec_read(int *a) { iarray_read(a+1, a[0]); }
void ivec_print(int *a) { iarray_print(a+1, a[0]); }
int *ivec_alt(int *a, int *b){
int *c = ivec_new(a[0]+b[0]);
for(int i = 1; i <= a[0]; ++i){c[2*i - 1] = a[i];}
for(int i = 1; i <= b[0]; ++i){c[2*i] = b[i];}
return c;
}
int main(void) {
int *a, *b, *c;
a = ivec_new(3); ivec_read(a);
b = ivec_new(3); ivec_read(b);
c = ivec_alt(a, b); ivec_print(c);
free(a); free(b); free(c);
return 0;
}

単体テスト
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
int *ivec_new(int size) {
int *a = (int*)malloc((size+1) * sizeof(int));
a[0] = size; return a;
}
int *ivec_alt(int *a, int *b){
int *c = ivec_new(a[0]+b[0]);
for(int i = 1; i <= a[0]; ++i){c[2*i - 1] = a[i];}
for(int i = 1; i <= b[0]; ++i){c[2*i] = b[i];}
return c;
}
bool iarray_equal(int *a, int *b, int n) {
for(int i = 0; i < n; ++i) {
if(a[i] != b[i]) { return false; }
}
return true;
}
void iarray_print(int *a, int n) {
for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
printf("\n");
}
void expect_iarray(int *a, int *b, int n, char *msg) {
printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
int a[] = {3,1,2,3}, b[] = {3,7,5,6}, c[] = {2,4,5}, d[] = {6,1,7,2,5,3,6}, e[] = {5,1,4,2,5,3};
int *p = ivec_alt(a, b);
int *q = ivec_alt(a, c);
expect_iarray(p, d, 7, "[1,2,3]+[7,5,6]=[1,7,2,5,3,6]");
expect_iarray(q, e, 6, "[1,2,3]+[4,5]=[1,4,2,5,3]");
return 0;
						  }

説明
関数ivec_altでは渡された2つの配列a,bを受け取り、その配列の長さ
の合計分の長さを持つ配列cをつくる。
そして、配列cの1から始まる奇数番目の要素にはaの要素、2から始ま
る偶数番目の要素にはbの要素を番号順に入れていく。
単体テストの結果は以下の様になった。
OK [1,2,3]+[7,5,6]=[1,7,2,5,3,6]
 6  1  7  2  5  3  6
 6  1  7  2  5  3  6
OK [1,2,3]+[4,5]=[1,4,2,5,3]
 5  1  4  2  5  3
 5  1  4  2  5  3

考察
プログラムのメイン関数で配列a,bの長さをそれぞれ2,5にしたところ次
の結果が得られた。

1> 1
2> 2
1> 3
2> 4
3> 5
4> 6
5> 7
  1  3  2  4  0  5  0
  ここで、結果の配列の5番目と7番目に0が入っているが、この部分に
  は本来配列aの3番目と4番目の値が入っているはずであり、今回
  はそれらが存在しなかったため、数列cの初期の値0のまま出力された
  と考えられる。また、配列bの4番目と5番目の値は本来配列cの
  8番目と10番目にそれぞれ入るはずだが、今回はそれらが存在しな
  いため書き込まれなかったと考えられる。

アンケート
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いま
すか。
値が入っている位置を詳細に把握することが大切だと思う。
Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に付いて
いないと思うことは何ですか。
int型など型の種類の詳細な知識。
Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
mallocの機能を復習できてよかった。

h1810542 2b:△ Mon May 25 15:26:30 2020


レポート2B

選択した課題1:
演習1b: 配列の並び順を逆順にする関数 void iarray revese(int *a,
int n) を作成する。

方針1:for文とintがたのrを用いて解いていく

コード1:
#include <stdio.h>
#include <stdbool.h>
void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}


char *bool2str(bool b) { return b ? "true" : "false"; }

void expect_int(int i1, int i2, char *msg) {
   printf("%s %d:%d %s\n", (i1==i2)?"OK":"NG", i1, i2, msg); 
   }

int iarray_max(int *a,int n){
    int max;
    max = a[0];
    for(int i=0;i<n;++i){
        if(max<a[i]){
            max = a[i];
        }
    }
          return max;
}

 void iarray_revese(int *a, int n){
     int r;
     for(int i=0;i<n/2;++i){
         r = a[n-1-i];
        a[n-1-i] = a[i];
        a[i]=r;        
     }
     
 }

bool iarray_equal(int *a, int *b, int n) { 
    for(int i = 0; i < n; ++i) { 
        if(a[i] != b[i]) { return false; } 
        } 
        return true; 
        } 
void expect_iarray(int *a, int *b, int n, char *msg) { 
    printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg); 
    iarray_print(a, n); iarray_print(b, n); 
    }

int main(void) {
  int a[] = {8,5,2,4,1}, b[] = {1,4,2,5,8}; 
  iarray_revese(a, 5);
   expect_iarray(a, b, 5, "85241 -> 14258");



  return 0;
}

実行例1:
./1b.exe
OK 85241 -> 14258
  1  4  2  5  8
  1  4  2  5  8

解説1:for文の中身を1度実行するとまずrにa[n-1]の値が代入され次に
a[n-1]にa[0]が代入され最後にa[0]にrに保存されている初めのa[n-1]
の値が代入されるこれをn/2の間繰り返すことですべての要素を逆順に
並べることができる

考察1:for文とrを用いることで逆順に要素を並べられることが分かった。

選択した課題2:
演習2a: 2つの列を受け取り、両方の列の内容を交互に並べた列を返す

方針2:a[]b[]の要素が交互に代入されるように計算する
コード2:
#include <stdio.h> 
#include <stdlib.h> 
#include<stdbool.h>
void iarray_read(int *a, int n) { 
    for(int i = 0; i < n; ++i) { 
        printf("%d> ", i+1); 
        scanf("%d", a+i);
         } 
} 
void iarray_print(int *a, int n) { 
    for(int i = 0; i < n; ++i) { 
        printf(" %2d", a[i]); 
        } 
        printf("\n"); 
        } 
int *ivec_new(int size) { 
    int *a = (int*)malloc((size+1) * sizeof(int));
     a[0] = size; return a;
    } 

void ivec_read(int *a) { 
    iarray_read(a+1, a[0]);
     } 
     
void ivec_print(int *a) { 
    iarray_print(a+1, a[0]);
} 
     
int *ivec_alter(int *a, int *b) { 
    int *c = ivec_new(a[0]+b[0]); 
    for(int i = 1; i <= a[0]; ++i) { 
        c[2*i-1] = a[i];
         } 
    for(int i = 1; i <= b[0]; ++i) { 
        c[2*i] = b[i];
         } 
         return c; 
    }

int *ivec_concat(int *a, int *b) {
     int *c = ivec_new(a[0]+b[0]);
      for(int i = 1; i <= a[0]; ++i) { 
          c[i] = a[i]; 
        } 
    for(int i = 1; i <= b[0]; ++i) { 
        c[i + a[0]] = b[i];
         }
          return c; 
        }
char *bool2str(bool b) { return b ? "true" : "false"; } 
bool iarray_equal(int *a, int *b, int n) { 
    for(int i = 0; i < n; ++i) { 
        if(a[i] != b[i]) {
             return false;
              } 
              } 
              return true;
               } 
    void expect_iarray(int *a, int *b, int n, char *msg) { 
        printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg); 
        iarray_print(a, n); iarray_print(b, n);
         }

int main(void) { 
    int a[] = {3,1,2,3}, b[] = {2,4,5}, c[] = {5,1,4,2,5,3}; 
    int *p = ivec_alter(a, b); 
    expect_iarray(p, c, 6, "[1,2,3]+[4,5]=[1,4,2,5,3]"); 
    return 0; 
    }

実行例2:
./2a.exe
OK [1,2,3]+[4,5]=[1,4,2,5,3]
  5  1  4  2  5  3
  5  1  4  2  5  3

解説2:*ivec_newで新たに配列cを作りc[0]には配列a,bのサイズを足し
たものが入っている。なのでc[2*i-1]にa[i]の値をまず代入していき次
にc[2*i]をb[i]を代入していくことでcにa,bの要素が交互に代入するよ
うにした。

考察2:配列の各変数の位置を指定することは単純な計算で可能ということがわかった。

アンケート:
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。 
違いを理解する

Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に
付いていないと思うこ とは何ですか。
練習不足

Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
難しいです

h1910525 2b:○ Mon May 25 18:14:46 2020


学籍番号 1910525
ペア 1910616
提出日 5/25(月)

1つ目の課題再掲:演習2a、2つの列の内容を交互に並べた列を返す関数
を作成し、単体テストを動かせ。

ソースコード

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>


void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}

void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}

void ivec_read(int *a) { iarray_read(a+1, a[0]); }

void ivec_print(int *a) { iarray_print(a+1, a[0]); }

bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i <= n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}


void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}


int *ivec_kougo(int *a, int *b){
  int *c = ivec_new(a[0]+b[0]);
  for(int i = 1; i <= c[0]; i+=2){ c[i] = a[(i+1) / 2]; c[i+1] = b[(i+1) / 2]; }
  return c;
}

int main(void) {
  int a[]={3,1,2,3},b[]={3,4,5,6},c[]={6, 1, 4, 2, 5, 3, 6}; 
  int*p=ivec_kougo(a,b); 
  expect_iarray(p,c,6,"[1,2,3]+[4,5,6}]=[ 1, 4, 2, 5, 3, 6]"); 

  int d[]={5,8,2,5,3,2},e[]={5,1,2,3,4,5},f[]={10,8,1,2,2,5,3,3,4,2,5}; 
  int*q=ivec_kougo(d,e); 
  expect_iarray(q,f,10,"[8,2,5,3,2]+[1,2,3,4,5}]=[8,1,2,2,5,3,3,4,2,5]"); 

  int g[]={7,1,2,3,4,5,6,7},h[]={7,8,9,10,11,12,13,14},i[]={14,1,8,2,9,3,10,4,11,5,12,6,13,7,14}; 
  int*r=ivec_kougo(g,h); 
  expect_iarray(r,i,14,"[1,2,3,4,5,6,7]+[8,9,10,11,12,13,14}]=[ 1,8,2,9,3,10,4,11,5,12,6,13,7,14]"); 

  return 0;
}

実行例

OK [1,2,3]+[4,5,6}]=[ 1, 4, 2, 5, 3, 6]
  6  1  4  2  5  3
  6  1  4  2  5  3
OK [8,2,5,3,2]+[1,2,3,4,5}]=[8,1,2,2,5,3,3,4,2,5]
 10  8  1  2  2  5  3  3  4  2
 10  8  1  2  2  5  3  3  4  2
OK [1,2,3,4,5,6,7]+[8,9,10,11,12,13,14}]=[ 1,8,2,9,3,10,4,11,5,12,6,13,7,14]
 14  1  8  2  9  3 10  4 11  5 12  6 13  7
 14  1  8  2  9  3 10  4 11  5 12  6 13  7


説明:可変長配列の例題について、列連結関数ivec_concatの代わりに交
互に並べる関数ivec_kougoを作成した。まず2つの列の大きさ分の可変
長配列*cを作成しする。次にfor文で変数iを2ずつ増やし、c[i]にaの要
素を、c[i+1]にbの要素を入れることで交互に要素が入る。

考察:今回の仕様だと、2列の要素数が必ず一致していないといけない。
2列の要素数が異なる場合は値が入っていないアドレスまで読まれてし
まう。これを「交互に値をいれ、片方の要素が尽きたらもう片方の要素
を並べる」という仕様にする方法を考察する。今回は予め列の要素数が
わかっている状態であるから、どちらが多いかを判定すれば並べること
ができる。よって、2列のうち少ない方の要素数分だけ交互に並べ、残
りを並べるという方法がとれると考えられる。それぞれのfor文の実行
回数は2列の要素数から導ける。

2つ目の課題再掲:演習3a、等差数列生成APIについてcdseq.cに等差数列
を初項に戻す機能cdseq_resetを追加せよ。

ソースコード

// cdseq.c -- cdseq implementation.
#include <stdlib.h>
#include "cdseq.h"

struct cdseq {int ft, value, diff; } ;

struct cdseq *cdseq_new(int s, int d) {
  struct cdseq *r = (struct cdseq*)malloc(sizeof(struct cdseq));
  r->ft = s; r->value = s; r->diff = d; return r;
}

int cdseq_get(struct cdseq *r) {
  int v = r->value; r->value += r->diff; return v;
}

void cdseq_free(struct cdseq *r) {
  free(r);
}

void cdseq_reset(struct cdseq *r){
  r->value = r->ft;
}



// cdseq.h --- constant difference sequence API.
struct cdseq *cdseq_new(int s, int d);
int cdseq_get(struct cdseq *r);
void cdseq_free(struct cdseq *r);
void cdseq_reset(struct cdseq *r);



// cdseq_demo.c -- cdseq demonstration.
#include <stdio.h>
#include "cdseq.h"
int main(void) {

  struct cdseq *s1 = cdseq_new(1, 2);
  struct cdseq *s2 = cdseq_new(0, 3);

  int i;
  for(i = 0; i < 13; ++i) {
    printf(" %2d", cdseq_get(s1));
    printf(" %2d", cdseq_get(s1));
    printf(" %2d", cdseq_get(s2));
    printf("\n");
    if (i == 7){
      cdseq_reset(s1);
      cdseq_reset(s2);
    }
  }

  cdseq_free(s1); cdseq_free(s2);
  return 0;
}

実行例
[h1910525@sol 02]$ gcc8 cdseq.c cdseq_demo.c
[h1910525@sol 02]$ ./a.out
  1  3  0
  5  7  3
  9 11  6
 13 15  9
 17 19 12
 21 23 15
 25 27 18
 29 31 21
  1  3  0
  5  7  3
  9 11  6
 13 15  9
 17 19 12


説明:等差数列生成APIにリセット機能をつけ、関数cdseq_resetで実行
できるようにした。構造体cdseqを改変し、int型のftを追加で構造とし
て持たせた。ここに初項をとっておき、cdseq_resetが呼ばれるとvalue
にftが上書きされ、結果としてリセットされる。

考察:今回、構造体を改変するという方法をとったが、これをせずにリ
セット機能をつくれるかについて考察する。構造体に初項を保存しない
場合、作成した等差数列の初項を直接参照する方法は、等差数列と初項
の関係をリスト化して保存する必要がある。つまり、また別の構造体を
生成することとなる。また、直接参照するのではなく逆算するという方
法を考える。等差数列は現在の値と等差が足された回数が分かれば初項
が導ける。つまり、特定の等差数列において等差が足された回数の情報
を保存できれば可能である。この場合においても、その回数と等差数列
を関連付ける必要があるため、新たな構造体が必要になると考える。こ
れ以外に、新たな構造体を生成せずにリセット機能が作れないか考えた
が思いつかなかった。

アンケート

Q1.アドレス、ポインタを使うプログラムで注意すべきことは何だと思
いま すか。
それらの概念をよく理解し、自分の扱っている数値が値なのか番地なの
かを間違えないこと。

Q2.ここまでのところで、プログラムを作るときに重要だが自分で身に
付いて いないと思うことは何ですか。
まず小さい規模で動く関数を作って、そこから拡張していくという手順。
まだ直接全て書いて動かない、という形になりがち。

Q3.感想をどうぞ。
情報隠蔽をできるようになれば大きなソフトウェアが作れそうと感じた。

h1910527 2b:○ Mon May 25 22:31:03 2020



演習1e
関数ポインタを受け取り、配列の値を間接的に計算する関数
iarray_injectを作成せよ。また、作成した関数について配列の総和、
最大値を求めよ。

ソースコード
#include <stdio.h>
#include <stdbool.h>
void iarray_read(int *a, int n) {
    for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
    }
}
void iarray_print(int *a, int n) {
    for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
    printf("\n");
}

int iadd(int x, int y){
    return x+y;
}

int imax(int x, int y){
    return (x>y)?x:y;
}

int iarray_inject(int *a, int n, int (*fp)(int,int)){
    int i;
    int result = a[0];
    for(i=1;i<n;i++){
        result = fp(result,a[i]);
    }
    return result;
}

void expect_int(int i1, int i2, char *msg) {
    printf("%s %d:%d %s\n", (i1==i2)?"OK":"NG", i1, i2, msg);
}

int main(void) {
    int a[] = {8,5,2,4,1}, b[] = {-1,-2,-3}, c[] = {0,-4,5,7}, d[] = {0};

    //iadd test
    printf("iadd test\n");
    expect_int(iarray_inject(a, 5, iadd), 20, "8+5+2+4+1");
    expect_int(iarray_inject(a, 4, iadd), 19, "8+5+2+4");
    expect_int(iarray_inject(b, 3, iadd), -6, "-1-2-3");
    expect_int(iarray_inject(c, 4, iadd), 8, "0-4+5+7");
    expect_int(iarray_inject(d, 1, iadd), 0, "0");

    //imax test
    printf("imax test\n");
    expect_int(iarray_inject(a, 5, imax), 8, "max(8,5,2,4,1)");
    expect_int(iarray_inject(b, 3, imax), -1, "max(-1,-2,-3)");
    expect_int(iarray_inject(c, 4, imax), 7, "max(0,-4,5,7)");
    expect_int(iarray_inject(c, 3, imax), 5, "max(0,-4,5)");
    expect_int(iarray_inject(d, 1, imax), 0, "max(0)");

    return 0;
}

テストケース
./a.out
iadd test
OK 20:20 8+5+2+4+1
OK 19:19 8+5+2+4
OK -6:-6 -1-2-3
OK 8:8 0-4+5+7
OK 0:0 0
imax test
OK 8:8 max(8,5,2,4,1)
OK -1:-1 max(-1,-2,-3)
OK 7:7 max(0,-4,5,7)
OK 5:5 max(0,-4,5)
OK 0:0 max(0)

解説
関数iarray_inject内で配列の先頭の数を代入し変数resultを初期化し
た後、forループで参照する配列の要素を一つずつずらしながら値を求
めている。今回は関数ポインタの制約上引数が2つに制限されるため、
resultに演算結果を代入することでそれまでの演算結果を一変数に集約
し、関数に渡せるようにしている。テストケースは2関数で記述位置を
分け可読性を上げたうえで、正数のみ、負数のみ、両者混合、要素が一
つだけといった多様なパターンを検証し関数設計に問題がないか確かめ
ている。

考察
関数ポインタを用いた単体テストの利点として、汎用性が上がることが
挙げられる。今回のように配列の総和と最大値について検証したい場合、
配列の総和を求める関数と最大値を求める関数をそれぞれ作成しなけれ
ばならず、不経済である。そこで、今回のiaddやimaxのように必要最低
限の相違点だけを別関数として仕立て、挙動が同一である他の部分は共
用するといった設計にすることでコードの削減が期待できる。この考え
方は、テストをしたい機能が増えるほど相対的にコード記述量が減らせ
ることから、大規模開発を行うときには重要であると感じた。また、テ
ストケースについても、作成方法を工夫することで試験者が結果を誤認
しないようにすることも大事だと考えた。


演習2a
二つの配列の要素を交互に並べた配列を返す関数を作成せよ。

ソースコード
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

int *ivec_new(int size) {
    int *a = (int*)malloc((size+1) * sizeof(int));
    a[0] = size;
    return a;
}

int *ivec_alternation(int *a, int *b) {
    int *c = ivec_new(a[0]+b[0]);
    int n = (a[0]<b[0])?a[0]:b[0], m = (a[0]>b[0])?a[0]:b[0];
    for(int i = 1; i <= n; ++i) { c[2*i-1] = a[i];c[2*i] = b[i]; }
    for(int i = 1; i <= m; ++i) { c[2*n+i] = (a[0]>b[0])?a[n+i]:b[n+i]; }
    return c;
}

bool iarray_equal(int *a, int *b, int n) {
    for(int i = 0; i < n; ++i) {
        if(a[i] != b[i]) { return false; }
    }
    return true;
}

void iarray_print(int *a, int n) {
    for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
    printf("\n");
}

void expect_iarray(int *a, int *b, int n, char *msg) {
    printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
    iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
    int a1[] = {3,1,2,3}, b1[] = {2,4,5}, c1[] = {5,1,4,2,5,3};
    int a2[] = {2,-1,-2}, b2[] = {4,-3,-4,-5,-6}, c2[] = {6,-1,-3,-2,-4,-5,-6};
    int a3[] = {1,0}, b3[] = {1,1}, c3[] = {2,0,1};
    int *p = ivec_alternation(a1, b1);
    int *q = ivec_alternation(a2, b2);
    int *r = ivec_alternation(a3, b3);
    expect_iarray(p, c1, 5, "[1,2,3]+[4,5]=[1,4,2,5,3]");
    expect_iarray(q, c2, 6, "[-1,-2]+[-3,-4,-5,-6]=[-1,-2,-3,-4,-5,-6]");
    expect_iarray(r, c3, 2, "[0]+[1]=[0,1]");
    return 0;
}

テストケース
./a.out
OK [1,2,3]+[4,5]=[1,4,2,5,3]
  5  1  4  2  5
  5  1  4  2  5
OK [-1,-2]+[-3,-4,-5,-6]=[-1,-2,-3,-4,-5,-6]
  6 -1 -3 -2 -4 -5
  6 -1 -3 -2 -4 -5
OK [0]+[1]=[0,1]
  2  0
  2  0

解説
二つの配列の要素数のうち小さいほうをnとし、大きい方をmとする。
まずそれぞれの配列要素を交互に入れていき要素数が2nになったのち、
残りの要素を順次代入している。

考察
テストケースは「aのほうが多い」、「bのほうが多い」、「両方同じ」
の3つを用意し、すべてのパターンで正しく動作するか検証した。試験
をしていてNGが出ることが多々あったので、単体テストの重要性を再認
識した。コード内で数か所三項演算子を用いたが、使いどころを選ばな
いと可読性が低下するので気を付けようと感じた。

アンケート
Q1.ポインタ自体はメモリの住所でしかなく、型にかかわらずアクセス
できてしまうため、誤った代入をしないようにすること。
Q2.計算量の概念、よほど遅くない限り動けばいいやと思ってしまう。
Q3.動的配列が正確に理解できていないので頑張ります。

h1910528 2b:○ Mon May 25 21:54:26 2020


提出日:5/25
作成したプログラム
[課題1b]
#include <stdio.h>
#include <stdbool.h>

void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}

void iarray_reverse(int *a, int n){
       int t;
   for (int i = 0; i < n / 2; i++) {
        t = a[i];
        a[i] = a[n - i - 1];
        a[n - i - 1] = t;
  }
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}

int main(void) {
  int a[] = {8,5,2,4,1}, b[] = {1,4,2,5,8}, c[] = {0,15,-1,0}, d[] = {0,-1,15,0};
  iarray_reverse(a, 5); expect_iarray(a, b, 5, "85241 -> 14258");
  iarray_reverse(c, 4); expect_iarray(c, d, 4, "015-10 -> 0-1150");
  return 0;
}
[結果]
PS C:\programming> .\a.exe
OK 85241 -> 14258
  1  4  2  5  8
  1  4  2  5  8
OK 015-10 -> 0-1150
  0 -1 15  0
  0 -1 15  0
[説明]

iarray_reverseで与えられた配列を逆順にして返すプログラム。for文
で繰り返しを作り、最初から配列の真ん中までをそれぞれ対応する端の
値と入れ替え続ける。
直接代入すると値が消えるので、変数tをかませることで交換している。
テストとして、正数のみの配列と、同じ数および0,負の数が含まれる配
列を作った。

[考察]
ポインタをつかってvoidの関数でなにもかえさずとも中身を直接いじれ
るのは便利だとは思うが、値の管理が分かりづらく難しいと感じた。

作成したプログラム
[課題2c]
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}

void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}

int *shou(int *num, int tot) {
  int tmp, i, j;
  int *c = (int*)malloc((tot) * sizeof(int));
  for (i = 0; i < tot; ++i) {
        c[i] = num[i];
  }
  for (i = 0; i < tot; ++i) {
    for (j= i+1; j < tot; ++j) {
      if (c[i] > c[j]) {
        tmp =  c[i];
        c[i] = c[j];
        c[j] = tmp;
      }
    }
  }
   return c;
}

int main(void) {
  int a[] = {5,3,4,7}, b[] = {3,4,5,7};
  int c[] = {7,3,0,3,-4}, d[] = {-4,0,3,3,7};
  expect_iarray(shou(a, 4), b, 4, "5347 -> 3457");
  expect_iarray(shou(c, 5), d, 5, "730-43 -> -40337");
  
  return 0;
}
[結果]
PS C:\programming> .\a.exe
OK 5347 -> 3457
  3  4  5  7
  3  4  5  7
OK 730-43 -> -40337
 -4  0  3  3  7
 -4  0  3  3  7
[説明]
配列を与えるとそれを昇順に整列させた配列を返す関数shouを含むプロ
グラム。まず配列とその要素数を受け取り、その配列のコピーcをつく
る。それをfor文を二重に使用し、配列の最初から後の要素にかけてそ
れ以降の要素と比較を行い、小さいほうが左に来るように入れ替え続け
る。最後にできた配列cを返す。テストでは、正数のみの要素数4の配列
と、0、負の値、同じ値を含む要素数5の配列を作った。

[考察]
色々悩みながら試行錯誤して作った。ポインタの要素数を取得できない
ために、今回はshouではポインタだけでなく要素数も与えるものを作っ
たが、ポインタの要素数を取得できないのはなにかと不便に感じた。

アンケート
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いま
すか。
アドレス、ポインタの中身の管理。色々アクセスする方法があり、関数
を跨げるので混乱しやすい。
Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に付いて
いないと思うことは何ですか。
ポインタとアドレスの理解が低くまだ配列との扱いの違う点で混乱する。
Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
ポインタの理解度をより高めたい。

h1910532 2b:○ Mon May 25 22:37:39 2020


プログラミング通論 #2 2b

1910532
個人作業
5/25

1c
再掲
何らかの整列アルゴリズムで配列を昇順に整列する関数void
iarray_sort(int *a, int n)の作成

ソース
#include<stdio.h>
#include<stdbool.h>

void iarray_sort(int *a, int n){
  for(int i = 0; i < n; ++i){
    for(int j = 1 + i; j < n; ++j){
      if(a[j] < a[i]){
	int m = a[i];
	a[i] = a[j];
	a[j] = m;
      }
    }
  }
}

bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}

int main(void){
  int a[] = {8,5,2,4,1}, b[] = {1,2,4,5,8};
  int c[] = {3,8,6,1,4,4}, d[] = {1,3,4,4,6,8};
  int e[] = {-3,2,12,5,0}, f[] = {-3,0,2,5,12};
  iarray_sort(a, 5); expect_iarray(a, b, 5, "85241 -> 12458");
  iarray_sort(c, 6); expect_iarray(c, d, 6, "386144 -> 134468");
  iarray_sort(e, 5); expect_iarray(e, f, 5, "-321250 -> -302512");
  return 0;
}

実行例
OK 85241 -> 12458
  1  2  4  5  8
  1  2  4  5  8
OK 386144 -> 134468
  1  3  4  4  6  8
  1  3  4  4  6  8
OK -321250 -> -302512
 -3  0  2  5 12
 -3  0  2  5 12

配列を昇順に整列させることができた。
要素に、同じ数や負の数、2桁の数、0があっても問題なかった。

説明

配列全体で一番小さい数を見つけ、配列の先頭から順に埋めていくよう
にした。先頭を埋めたら2番目から最後の要素までを、2番目を埋めた3
番目から最後の要素までを比較し、小さい数を探すようにした。先頭に
小さい数を埋めるときは、その数が2つ存在しないよう、1つ仮の変数に
入れるようにした。単体テストでは要素数を変えたり、要素に同じ数や
負の数、2桁の数、0がある場合など様々な場合を確かめた。

考察

始め、説明で述べた仮の変数を用意していなかった。したがって、同じ
数が何個も出てきてしまい、入れ替える前の数がなくなってしまった。
それに対応するためにプログラムを色々と変更したがなかなかうまくい
かなかった。その理由はどこに文を書き加えるべきかわからなかったか
らだ。フローチャートのようなものを考えながらプログラムを書く必要
があると分かった。


1d
再掲

2つの配列(長さは同じ)を受け取り、2番目の各要素の値を1番目の配列
の各要素に足しこむ関数void iarray_add(int *a, int *b, int n)の作
成

ソース
#include<stdio.h>
#include<stdbool.h>

void iarray_add(int *a, int *b, int n){
  for(int i = 0; i < n; ++i){
    a[i] = a[i] + b[i];
  }
}

bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void){
  int a[] = {8,5,2,4,1}, b[] = {1,1,2,2,3}, c[] = {9,6,4,6,4};
  int d[] = {6,4,8,0,0}, e[] = {4,2,10,3,0}, f[] = {10,6,18,3,0};
  int g[] = {-3,-5,2,0,6,13}, h[] = {-4,5,4,-1,0,2}, i[] = {-7,0,6,-1,6,15};
  iarray_add(a, b, 5); expect_iarray(a, c, 5, "85241+11223 -> 96464");
  iarray_add(d, e, 5); expect_iarray(d, f, 5, "64800+421030 ->1061830 ");
  iarray_add(g, h, 6); expect_iarray(g, i, 6, "-3-520613+-454-102 ->-706-1615");
  return 0;
}

実行例
OK 85241+11223 -> 96464
  9  6  4  6  4
  9  6  4  6  4
OK 64800+421030 ->1061830
 10  6 18  3  0
 10  6 18  3  0
OK -3-520613+-454-102 ->-706-1615
 -7  0  6 -1  6 15
 -7  0  6 -1  6 15

2番目の各要素の値を1番目の配列の各要素に足しこむことができた。
要素に、同じ数や負の数、2桁の数、0があっても問題なかった。

説明

変数iを使って、同じ添え字の配列の要素を足し、その結果をもとの配
列に入れた。単体テストでは要素数を変えたり、要素に同じ数や負の数、
2桁の数、0がある場合など様々な場合を確かめた。

考察

この関数があれば、配列の数が2つと言わず、n個ある場合も合計を出す
ことができるようになると気が付いた。例えば、配列aと配列bの足した
結果を配列aに入れ、aとcを足してaに入れ・・・と繰り返すことができ
るからだ。したがって、このような単純な関数でも工夫次第ではより難
しいことにしようできると分かった。

アンケート
A1
アドレスかアドレスの中の値のどちらを示しているのか理解すること。

A2
プログラムをより簡潔に短く書く力。

A3
単体テストの内容を考えるのが色々なパターンを想定しなければならず、大変だった。

h1910534 2b:○ Thu May 21 00:49:41 2020


学籍番号: 1910534
ペア: 個人作業
提出日時: 2020年5月20日

・1つめの課題
演習1b
配列の並び順を逆順にする関数 void iarray_reverse(int *a, int n)を作成する

・単体テストを含むプログラム
#include <stdio.h>
#include <stdbool.h>
void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
char *bool2str(bool b) { return b ? "true" : "false"; }
void expect_bool(bool b1, bool b2, char *msg) {
  printf("%s %s:%s %s\n", (b1==b2)?"OK":"NG",
         bool2str(b1), bool2str(b2), msg);
}

void iarray_reverse(int *a, int n){
  int num;
  for (int i = 0; i < n/2; i++) {
    num = a[n-1-i];
    a[n-1-i] = a[i];
    a[i] = num;
  }
}

int main(void) {
  int a[] = {8,5,2,4,1}, b[] = {1,4,2,5,8};
  iarray_reverse(a, 5); expect_iarray(a, b, 5, "85241 -> 14258");
  int c[] = {8}, d[] = {8};
  iarray_reverse(c, 1); expect_iarray(c, d, 1, "8 -> 8");
  int e[] = {5,2,4,1}, f[] = {1,4,2,5};
  iarray_reverse(e, 4); expect_iarray(e, f, 4, "85241 -> 14258");
  int g[] = {}, h[] = {};
  iarray_reverse(g, 0); expect_iarray(g, h, 0, "{} -> {}");
  return 0;
}

・実行例
$ ./a.out
OK 85241 -> 14258
  1  4  2  5  8
  1  4  2  5  8
OK 8 -> 8
  8
  8
OK 85241 -> 14258
  1  4  2  5
  1  4  2  5
OK {} -> {}


・説明
配列の並び順を逆順にする関数 iarray_reverseを作成した。
まずint型整数numを用意した。次にfor文の中で、引数として与えられ
た配列のn-1-i番目の数字をnumに入れ、n-1-i番目にi番目の数字を入れ
た。そしてi番目にnumを代入した。これにより配列のn-1-i番目とi番目
が入れ替わった。これをiが0からn/2の場合それぞれについて行い、配
列を逆順にしている。
単体テストでは互いに逆順の配列を2つ用意し、一方を iarray_reverse
で逆順にした後2つをexpect_iarrayで比較して、一致していればOK、一
致していなければNGが出力されるようになっている。

・考察
main関数内でテスト1つごとに配列を宣言しているが、これは1度
iarray_reverseで同順にした配列を繰り返しなどで逆順に戻すより少な
い行数で済むのでわかりやすいと考えたための工夫である。同じ配列の
中身を部分的に変えてなんども使いたいわけではなければ、繰り返しな
どで元の状態に戻さないでシンプルに新しく宣言する方が完結でわかり
やすいプログラムになるとわかった。
繰り返しや条件分岐などやたら凝ったプログラムの方が良くできている
と思いがちだが、シンプルな記述の方が良い場面も多くあると考えられ
る。

・2つめの課題
演習2a
2つの列を受け取り、両方の列の内容を交互に並べた列を返す関数を
作る。単体テストも作る。

・単体テストを含むプログラム
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}

int *altarray(int *a, int *b) {
  int *c = ivec_new(a[0]+b[0]);
  int sma = a[0];
  bool smaisa = true;
  if (a[0] > b[0]) { sma = b[0]; smaisa = false;}
  for (int i = 1; i <= sma; i++) {
    c[2*i-1] = a[i];
    c[2*i] = b[i];
  }
  if (smaisa && a[0] != b[0]) {
    for (int i = 2*sma+1; i <= a[0]+b[0]; i++) {
      c[i] = b[i-sma];
    }
  }
  if (!smaisa && a[0] != b[0]) {
    for (int i = 2*sma+1; i <= a[0]+b[0]; i++) {
      c[i] = a[i-sma];
    }
  }
  return c;
}

int main(void) {
  int a[] = {3,1,2,3}, b[] = {2,4,5}, c[] = {5,1,4,2,5,3};
  int *p = altarray(a, b);
  expect_iarray(p, c, 6, "[1,2,3]+[4,5]=>[1,4,2,5,3]");
  int d[] = {2,1,2}, e[] = {6,3,4,5,6,7,8}, f[] = {8,1,3,2,4,5,6,7,8};
  int *q = altarray(d, e);
  expect_iarray(q, f, 9, "[1,2]+[3,4,5,6,7,8]=>[1,3,2,4,5,6,7,8]");
  int g[] = {3,1,2,3}, h[] = {3,4,5,6}, i[] = {6,1,4,2,5,3,6};
  int *r = altarray(g, h);
  expect_iarray(r, i, 7, "[1,2,3]+[4,5,6]=>[1,4,2,5,3,6]");
  int j[] = {1}, k[] = {1}, l[] = {2};
  int *s = altarray(j, k);
  expect_iarray(s, l, 1, "[]+[]=>[]");
  return 0;
}


・実行例
$ ./a.out
OK [1,2,3]+[4,5]=>[1,4,2,5,3]
  5  1  4  2  5  3
  5  1  4  2  5  3
OK [1,2]+[3,4,5,6,7,8]=>[1,3,2,4,5,6,7,8]
  8  1  3  2  4  5  6  7  8
  8  1  3  2  4  5  6  7  8
OK [1,2,3]+[4,5,6]=>[1,4,2,5,3,6]
  6  1  4  2  5  3  6
  6  1  4  2  5  3  6
OK []+[]=>[]
  2
  2


・説明
2つの配列の内容を交互に並べた列を返す関数 altarrayを作成した。
長さが異なる場合は、交互に並べて余った長い方の内容を順に並べるこ
とにした。
まず、引数として与えられた2つの配列の内容を合わせた長さをポイン
タ型の*c、すなわちc[0]に代入した。
また、2つの配列a[],b[]のうち短い方を確かめるために、int型変数sma
にとりあえずaの長さであるa[0]を代入しbool型変数smaisaにtrueを代
入した。次にif文でa[0]とb[0]の大小を比較し、b[0]の方が小さければ
smaにb[0]を代入、smaisaにfalseを代入した。
次にfor文で「配列c[]にaのi番目の要素を並べ、bのi番目の要素を並べ
る」ことを短い方の配列の内容を移し終えるまで繰り返した。そして、
if文でsmaとsmaisaから短い方の配列を判定し、余った要素をfor文で順
に習えbている。
単体テストでは2つの配列a、bの長さについて、
aの方が長い場合、bの方が長い場合、長さが同じ場合、どちらの長さも
0の場合に分けてテストした。

・考察
可変長配列の先頭に長さを記録していると、繰り返しなどで長さを調べ
る手間が省け、配列同士の結合や整理がしやすいことがわかった。また、
i番目の内容が配列のi番目(array[i])にくるので、0番目から数える必
要がないという点で扱いやすいと考える。
内容を交互に並べた列を作る関数は作れたが、長さ1で内容がない配列
が引数として与えられると、内容の数が増えないため先頭の長さと内容
の数に不一致が生じてしまうことがテストからわかった。この関数を用
いる場面で長さ1の配列を引数として与えることはあまりないと考えら
れるが与えられる仕様なら改善が必要である。

・アンケート
Q1. アドレス、ポインタを使うプログラムで注意すべきこ
とは何だと思いすか。

ポインタ型が何のアドレスを格納しているか、どのアドレスを処理で取
り出しているかをわからなくならないようにすること。

Q2. ここまでのところで、プログラムを作るときに重要だか
゙自分で身に付いていないと思うことは何ですか。

論理的なつながりなどを考えて設計するのが重要だと思うが、思いつい
たそばから書き始める時がある

Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。

アドレス、ポインタの有用性が基礎プロの時より理解できたので、便利
な側面も実感できた。


h1910542 2b:○ Sun May 24 15:04:25 2020



1.	プログラム
演習1のc

演習1のcは何らかの整列アルゴリズムで配列を昇順に整列する関数
void iarray sort(int *a, int n) 。作成したプログラムは以下のよう
になった。

#include<stdio.h>

void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
void iarray_sort(int *a, int n){
    int x = 1;
    while (x != 0)
    {
        x = 0;
        for ( int i = 0; i < n-1; i++)
        {
            if(a[i]>a[i+1]){
                int tmp = a[i];
                a[i] = a[i+1];
                a[i+1] = tmp;
                x = 1;
            }
        }
        
    }
    
}
int main(void){
    int a[5];
    iarray_read(a, 5); iarray_print(a, 5);
    iarray_sort(a, 5); 
    for(int i = 0; i < 5; ++i){
      printf(" %2d", a[i]);
    }
    printf("\n");
    return 0;
}
【プログラムの説明】

配列の隣り合う数字同士を比べ、左側の数字の方が右側の数字より大き
い時にその2数を入れ替え、昇順になるまで繰り返した。具体的には、
まずx=1としwhile文を使ってx=0でないとき繰り返した。while文では
まずx=0としwhile文のなかでfor文を利用した。隣り合う2数のうち左
の方が右より大きい場合に隣り合う2数を入れ替え、x=1とすることで入
れ替えが起こった時にx=1であるため繰り返され、昇順になるまで配列
の入れ替えが起こる。

・実行例
[h1910542@sol #2]$ ./a.out
1> 2
2> 6
3> 1
4> 7
5> 0
  2  6  1  7  0
  0  1  2  6  7

・単体テスト
// test_iarray_sort.c --- unit test of iarray_sort.c
#include<stdio.h>
#include<stdbool.h>

void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
void iarray_sort(int *a, int n){
    int x = 1;
    while (x != 0)
    {
        x = 0;
        for ( int i = 0; i < n-1; i++)
        {
            if(a[i]>a[i+1]){
                int tmp = a[i];
                a[i] = a[i+1];
                a[i+1] = tmp;
                x = 1;
            }
        }
        
    }
    
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void){
    int a[] = {8,5,2,4,1}, b[] = {1,2,4,5,8}, c[] = {1,2,4,2,1}, d[] = {1,1,2,2,4};
  iarray_sort(a, 5); expect_iarray(a, b, 5, "85241 -> 12458");
  iarray_sort(a+1, 4); expect_iarray(a+1, b+1, 4, "5241 -> 1245");
  iarray_sort(c, 5); expect_iarray(c, d, 5, "12421 -> 11224");
  return 0;
}
・実行例
[h1910542@sol #2]$ ./a.out
OK 85241 -> 12458
  1  2  4  5  8
  1  2  4  5  8
OK 5241 -> 1245
  2  4  5  8
  2  4  5  8
OK 12421 -> 11224
  1  1  2  2  4
  1  1  2  2  4
【考察】

今回の配列を昇順にするプログラムはrubyでやったことがあるものだが、
C言語でも同様配列を入れ替えることで作成できることがわかった。ま
た前回のときのように変数xを用いてxが0か1のときでループの有無を
変えられたので便利だと思った。昇順にする方法は今回の方法以外にも
いくつかあるはずなので試していきたい。

演習1のd

演習1のdは2 つの配列 (長さは同じ) を受け取り、2 番目の各要素の
値を 1 番目の配列の各要素に足し 込む関数 void iarray add(int *a,
int *b, int n) を作成するというものだった。作成したプログラムは
以下のようになった。

#include<stdio.h>

void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
void iarray_add(int *a, int *b, int n){
    for (int i = 0; i < n; i++)
    {
        a[i] = a[i] + b[i];
    }
    
}
int main(void){
    int a[5]; int b[5];
    iarray_read(a, 5); iarray_print(a, 5);
    iarray_read(b, 5); iarray_print(b, 5);
    iarray_add(a, b, 5); 
    for(int i = 0; i < 5; ++i){
      printf(" %2d", a[i]);
    }
    printf("\n");
    return 0;
}

【プログラムの説明】

配列a,bが与えられたとき2番目の各要素の値を1番目の各要素に足して
いくためにfor文を用いた。for文の中では配列aの最初の値に配列bの
最初の値を加えるためにa[i]にa[i]とb[i]の値を足した値を入れた。i
の値を0からn-1まで動かすことで2番目の各要素の値を1番目の各要素
に足し込む関数を作成できた。

・実行例
[h1910542@sol #2]$ ./a.out
1> 2
2> 4
3> 6
4> 8
5> 0
  2  4  6  8  0
1> 1
2> 3
3> 5
4> 7
5> 9
  1  3  5  7  9
  3  7 11 15  9

・単体テスト
// test_iarray_add.c --- unit test of iarray_add.c
#include<stdio.h>
#include<stdbool.h>

void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
void iarray_add(int *a, int *b, int n){
    for (int i = 0; i < n; i++)
    {
        a[i] = a[i] + b[i];
    }
    
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void){
    int a[] = {8,5,2,4,1}, b[] = {1,1,2,2,3}, c[] = {9,6,4,6,4};
    int d[] = {4,5,6,7,8}, e[] = {-4,-5,0,-7,-9}, f[] = {0,0,6,0,-1};
  iarray_add(a, b, 5); expect_iarray(a, c, 5, "85241+11223 -> 96464");
  iarray_add(d, e, 5); expect_iarray(d, f, 5, "45678+(-4)(-5)0(-7)(-9) -> 0060(-1)");
  return 0;
}
・実行例
[h1910542@sol #2]$ ./a.out
OK 85241+11223 -> 96464
  9  6  4  6  4
  9  6  4  6  4
OK 45678+(-4)(-5)0(-7)(-9) -> 0060(-1)
  0  0  6  0 -1
  0  0  6  0 -1

【考察】

1番目の要素に2番目の要素を足し込むことで1番目の配列が足された状
態になっていたため、連続で同じ配列を使って足し込むと同じ配列でも
足された状態の配列に足し込まれることがわかった。また、足し込んだ
結果値が負になっても計算されていることが単体テストの結果からもわ
かった。

2.	アンケート
Q1. 間接的に指定していることを理解しておくこと。
Q2. 客観的発想で視野を広くしてかんがえること。
Q3.Rubyで書いたプログラムをc言語でも同様に作れたのでよかった。

h1910546 2b:○ Mon May 25 19:43:16 2020



1つ目の課題の再掲
配列の並び順を逆順にするプログラムをつくる。また単体テストも行う。

プログラムのソース
#include <stdio.h>
#include<stdbool.h>

void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");

}
int iarray_reverse(int *a, int n){
  int x,i;
    for(i = 0; i < n/2; ++i){
        x = a[i];
	a[i] = a[n-i-1];
	a[n-i-1] = x;
    }
}

bool iarray_equal(int *a, int *b, int n) {
    for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
    }
    return true;
}

void expect_iarray(int *a, int *b, int n, char *msg) {
    printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
    iarray_print(a, n); iarray_print(b, n);
}

int main(void) {
  int a[] = {8,5,2,4,1}, b[] = {1,4,2,5,8}, c[] = {7,0,6,3}, d[] = {3,6,0,7}, e[] = {9,9,9,3,3}, f[] = {3,3,9,9,9};
    iarray_reverse(a, 5); expect_iarray(a, b, 5, "85241 -> 14258");
    iarray_reverse(c, 4); expect_iarray(c, d, 4, "7063 -> 3607");
    iarray_reverse(e, 5); expect_iarray(e, f, 5, "99933 -> 33999");
  return 0;
}

単体テストの実行例
OK 85241 -> 14258
  1  4  2  5  8
  1  4  2  5  8
OK 7063 -> 3607
  3  6  0  7
  3  6  0  7
OK 99933 -> 33999
  3  3  9  9  9
  3  3  9  9  9

プログラムの説明
iarray_printは配列のn個の値を出力する関数であり、繰り返し出力す
るするためにfor文とprintfを組み合わせている。iarray_reverseは配
列の順序を逆転させるもので、まず変数xを用意し、元の配列の先頭の
要素を代入し、次にその先頭の位置に末端の要素を代入、最後に末端の
位置にxを代入する。これらの操作をfor文のカウンタ変数を1ずつ変化
させ繰り返し行うことで正しく出力される。expect iarrayは配列を結
果とするものの単体テストをするためにあり、iarray equalの結果から
OKかNGかを出力する。iarray equalは二つの配列のそれぞれの要素
が等しいか確認する関数である。

考察
今回は単体テストを用い、プログラムが正しく動いているか確認したが、
プログラムが比較的簡単なので、バグを見つけるためだけならそこまで
テストケースを用意する必要もなくできた。しかし、プログラムが複雑
になるにつれバグやミスの量が増えることが予想されるので、すべての
バグを見つけだすには考えられる限り多くのテストケースが必要だと考
えられる。

2つ目の課題の再掲
配列を昇順に整列するプログラムをつくる。また単体テストも行う。

プログラムのソース
#include <stdio.h>
#include <stdbool.h>

void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

int iarray_sort(int *a, int n){
  int x,i,j;
  for(i = 0; i < n; ++i) {
    for(j = i+1; j < n; ++j) {
      if(a[j] < a[i]){
	x = a[i];
	a[i] = a[j];
	a[j] = x;
      }
    }
  }
}

bool iarray_equal(int *a, int *b, int n) {
    for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
    }
    return true;
}

void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}

int main(void) {
  int a[] = {8,5,2,4,1}, b[] = {1,2,4,5,8}, c[] = {3,3,3,-1,4}, d[] = {-1,3,3,3,4}, e[] = {2,4,6,-1,0,0,5}, f[]={-1,0,0,2,4,5,6};
  iarray_sort(a, 5); expect_iarray(a, b, 5, "85241 -> 12458"); 
  iarray_sort(c, 5); expect_iarray(c, d, 5, "333-14 -> -13334");
  iarray_sort(e, 7); expect_iarray(e, f, 7, "246-1005 -> -1002456");
  return 0;
}

単体テストの実行例
OK 85241 -> 12458
  1  2  4  5  8
  1  2  4  5  8
OK 333-14 -> -13334
 -1  3  3  3  4
 -1  3  3  3  4
OK 246-1005 -> -1002456
 -1  0  0  2  4  5  6
 -1  0  0  2  4  5  6

プログラムの説明
iarray_print、iarray_equalとexpect_iarrayに関しては1つ目の課題と
同様のプログラムである。iarray_sortは配列を昇順に並べ替える関数
であり、これを実現させるためにfor文の二重ループを用いている。内
側のfor文で配列の要素同士大きさを比べ、if文の条件を満たせば、x =
a[i];、a[i] = a[j];、a[j] = x;で要素を交換する。外側のfor文で比
べる要素がひとつずれていき、最終的にすべての要素同士、大小関係を
比較し昇順に並び替えができる。

考察
昇順に並び替えるプログラムは、さまざまな整数アルゴリズムで表現す
ることができ、自分は基本的なバブルソートを用いたが、効率よくする
ためには、要素同士を交換する回数の少ない選択ソートなどを使えば良
かったと思う。様々なアルゴリズムを勉強することで、表現できるプロ
グラムの種類を増やすことは重要だと思った。

アンケート
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。
 配列の名前がその配列の先頭要素のアドレスになること。

Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に
付いていないと思うことは何ですか。
ポインタのポインタ。

Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
アドレスとポインタを理解できるようにがんばります。

h1910556 2b:○ Sat May 23 14:54:04 2020


レポート2b
2020/5/23 提出

1 課題1
1.1 課題の再掲

 演習2_b
 1つの動的配列を受け取り、要素を反転したものを返す。


1.2 ソースコード

 #include <stdio.h>
 #include <stdlib.h>
 #include <stdbool.h>
 
 void iarray_read(int *a, int n) {
     for (int i = 0; i < n; ++i) {
         printf("%d> ", i + 1);
         scanf("%d", a + i);
     }
 }
 
 void iarray_print(int *a, int n) {
     for (int i = 0; i < n; ++i) {
         printf(" %2d", a[i]);
     }
     printf("\n");
 }
 
 int *ivec_new(int size) {
     int *a = (int *)malloc((size + 1) * sizeof(int));
     a[0] = size;
     return a;
 }
 
 void ivec_read(int *a) { iarray_read(a + 1, a[0]); }
 
 void ivec_print(int *a) { iarray_print(a + 1, a[0]); }
 
 int *ivec_reverse(int *a) {
     int *c = ivec_new(a[0]);
     for (int i = 1; i <= a[0]; ++i) {
         c[i] = a[a[0] + 1 - i];
     }
     return c;
 }
 
 bool iarray_equal(int *a, int *b, int n) {
     for (int i = 0; i < n; ++i) {
         if (a[i] != b[i]) {
             return false;
         }
     }
     return true;
 }
 
 void expect_iarray(int *a, int *b, int n, char *msg) {
     printf("%s %s\n", iarray_equal(a, b, n) ? "OK" : "NG", msg);
     iarray_print(a, n);
     iarray_print(b, n);
 }
 
 int main(void) {
     int *vec, n;
     printf("n> "); scanf("%d", &n);
     vec = ivec_new(n);
     ivec_read(vec);
     printf("vec ="); ivec_print(vec);
     printf("reversed vec ="); ivec_print(ivec_reverse(vec));
     // テストケース
     printf("testcase\n");
     int a[] = {3, 1, 2, 3}, b[] = {3, 3, 2, 1};
     expect_iarray(ivec_reverse(a), b, 4, "[1,2,3]->[3,2,1]");
     int c[] = {1, -4}, d[] = {1, -4};
     expect_iarray(ivec_reverse(c), d, 2, "[-4]->[-4]");
     int e[] = {0}, f[] = {0};
     expect_iarray(ivec_reverse(e), f, 1, "[]->[]");
     return 0;
 }


1.3 実行例

 in  n> 5
     1> 1
     2> 2
     3> 3
     4> 4
     5> 5
 out vec =  1  2  3  4  5
     reversed vec =  5  4  3  2  1

 in  n> 3
     1> -1
     2> -4
     3> -7
 out vec = -1 -4 -7
     reversed vec = -7 -4 -1


1.4 テストケース

 OK [1,2,3]->[3,2,1]
   3  3  2  1
   3  3  2  1
 OK [-4]->[-4]
   1 -4
   1 -4
 OK []->[]
   0
   0


1.5 説明

 ivec_reverse:
    動的配列aを与えると、それと要素数が等しい動的配列cを作成し、
    forループでaの後ろから要素を見ていき、cの前方から代入していく。
    最終的にcのポインタを返す。

 main:
    1~4行目は入力でnは作成する列の要素数、ivec_readを使って配列
    の要素を入力していく。
    5,6行目では元の列と反転した列を出力する。
    7行目以降はテストケース。
    要素数が1や0の場合などもテストしている。


1.6 考察

 他の言語でも動的配列を扱う場合は配列の0番目の要素で長さを保持し
 ているということ、
 長さを保持しておくと配列に対する操作が簡単になるということがわかった。

2 課題2
2.1 課題の再掲

 演習3_a,b
 等差数列生成に関するcdseq.cを書き換えて以下の関数を作る。

  cdseq_reset:
     等差数列を初項に戻す。

  cdseq_get:
     等差数列の現在の項を取り出す。

  cdseq_fwd:
     等差数列を次の項に進める。

  cdseq_num:
     等差数列において現在何項目か返す。(初項は0)


2.2 ソースコード
2.2.1 cdseq.c

 // cdseq.c -- cdseq implementation.
 #include <stdlib.h>
 #include "cdseq.h"

 struct cdseq { int value, ini, diff, index; };
 struct cdseq *cdseq_new(int s, int d) {
     struct cdseq *r = (struct cdseq*)malloc(sizeof(struct cdseq));
     r->value = s; r->diff = d; 
     r->ini = s;
     r->index = 0;
     return r;
 }
 int cdseq_get(struct cdseq *r) {
     return r->value;
 }
 void cdseq_fwd(struct cdseq *r) {
     r->value += r->diff;
     r->index++;
 }
 int cdseq_num(struct cdseq *r) {
     return r->index;
 }
 void cdseq_reset(struct cdseq *r) {
     r->value = r->ini;
     r->index = 0;
 }
 void cdseq_free(struct cdseq *r) {
     free(r);
 }


2.2.2 3_b.c

 #include <stdio.h>
 #include <stdbool.h>
 #include "cdseq.h"
 
 void expect_int(int i1, int i2, char *msg) {
     printf("%s %d:%d %s\n", (i1==i2)?"OK":"NG", i1, i2, msg);
 }
 int main(void) {
     int n, initial, common_difference;
     printf("n> "); scanf("%d", &n);
     printf("initial> "); scanf("%d", &initial);
     printf("common_difference> "); scanf("%d", &common_difference);
     struct cdseq *s;
     s = cdseq_new(initial, common_difference);
     for (int i = 0; i < n; ++i) {
         cdseq_fwd(s);
     }
     printf("a_n = (index: %d, value: %d)\n", cdseq_num(s), cdseq_get(s));
     cdseq_reset(s);
     printf("a_0 = (index: %d, value: %d)\n", cdseq_num(s), cdseq_get(s));
     // テストケース
     printf("testcase\n");
     struct cdseq *t;
     t = cdseq_new(2, 3);
     expect_int(cdseq_get(t), 2, "2+3*0 = 2");
     expect_int(cdseq_get(t), 2, "2+3*0 = 2");
     cdseq_fwd(t);
     expect_int(cdseq_get(t), 5, "2+3*1 = 5");
     cdseq_fwd(t);
     cdseq_fwd(t);
     expect_int(cdseq_get(t), 11, "2+3*3 = 11");
     cdseq_reset(t);
     expect_int(cdseq_get(t), 2, "2+3*0 = 2");
     cdseq_reset(t);
     expect_int(cdseq_get(t), 2, "2+3*0 = 2");
     struct cdseq *u;
     u = cdseq_new(-5, 0);
     expect_int(cdseq_get(u), -5, "(-5)+0*0 = -5");
     cdseq_fwd(u);
     expect_int(cdseq_get(u), -5, "(-5)+0*0 = -5");
     cdseq_reset(u);
     expect_int(cdseq_get(u), -5, "(-5)+0*0 = -5");
     return 0;
 }


2.3 実行例

 in  n> 6
     initial> 3
     common_difference> 9
 out a_n = (index: 6, value: 57)
     a_0 = (index: 0, value: 3)

 in  n> 0
     initial> 0
     common_difference> 0
 out a_n = (index: 0, value: 0)
     a_0 = (index: 0, value: 0)

 in  n> 4
     initial> -1
     common_difference> -5
 out a_n = (index: 4, value: -21)
     a_0 = (index: 0, value: -1)


2.4 テストケース

 OK 2:2 2+3*0 = 2
 OK 2:2 2+3*0 = 2
 OK 5:5 2+3*1 = 5
 OK 11:11 2+3*3 = 11
 OK 2:2 2+3*0 = 2
 OK 2:2 2+3*0 = 2
 OK -5:-5 (-5)+0*0 = -5
 OK -5:-5 (-5)+0*0 = -5
 OK -5:-5 (-5)+0*0 = -5


2.5 説明

 cdseq_new:
    等差数列の初項と公差を与えて構造体を作成する。
    indexには0を代入している。

 cdseq_reset:
    cdseq_fwdによってstruct cdseq型の変数rのvalueの値自体が変わっ
    てしまっているので、
    構造体に初項を記憶しておくためのフィールドint iniを用意して
    cdseq_resetが呼ばれたときにvalue変数にiniの値を代入するよう
    にしている。
 cdseq_get:
    他に何も操作を行わずrのvalueの値を返すだけにしている。

 cdseq_fwd:
    rのvalueの値に等差数列の差であるdiffの値を足し、cdseq_numで
    使うindexを1足している。

 cdseq_num:
    何番目か記憶するためにフィールドint indexを用意し、それを返すようにしている。

 main:
    1~4行目は入力でnは値を得たい項の番号、initialは初項、
    common_differenceは公差である。
    5,6行目ではinitialとcommon_differenceを与えて構造体を作っている。
    7~9行目ではcdseq_fwdをn回のforループで繰り返している。
    10~12行目は出力で数列のn項目とceseq_resetを行った後の初項に
    関して、何番目かと値を返している。
    13行目からはテストケース。公差が0の場合などもテストしている。


2.6 考察

 構造体定義では初期値を代入した変数をフィールドにすることはでき
 ないということがわかった。
 またJavaなどはクラスのインスタンスに初期値を代入していない場合
 整数型なら0になるようになっているが、Cでは構造体の初期値は決まっ
 ていないことがわかった。

3 アンケート
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。

 float型の変数をポインタを用いてint型に型変換したとき、それらの
 32ビットの使い方が異なることを意識する。

Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に
付いていないと思うことは何ですか。

 ない。


Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
 q演の問題でポインタが理解できてよかった。

h1910589 2b:○ Mon May 25 22:21:02 2020


学籍番号:1910589
提出日時:5/25

______________________

演習1b(配列の並び順を逆順にするプログラム)

[プログラムのソース]
#include <stdio.h>
#include <stdbool.h>



void iarray_read(int *a, int n){
  for(int i=0; i<n; ++i){
    printf("%d>", i+1); scanf("%d", a+i);
  }
}



void iarray_print(int *a, int n){
  for(int i=0; i<n; ++i){
    printf("%2d",a[i]);
  }
  printf("\n");
}



int iarray_reverse(int *a,int n){
  int q[n];
  for(int i=0; i<n; i++){
    q[i]=a[i];
  }
  for(int i=0; i<n; i++){
    a[i]=q[n-1-i];
  }
  return *a;
}




bool iarray_equal(int *a, int *b, int n){
  for(int i=0; i<n; ++i){
    if(a[i]!=b[i]){return false;}
  }
  return true;
}



void expect_iarray(int *a, int *b, int n, char *msg){
  printf("%s %s\n", iarray_equal(a,b,n)?"OK":"NG", msg);
  iarray_print(a,n);
  iarray_print(b,n);
}




int main(void){
  int a[] = {8,5,2,4,1}, b[] = {1,4,2,5,8},c[]={6,1,9,3,2,1},d[]={1,2,3,9,1,6};
  iarray_reverse(a, 5); expect_iarray(a, b, 5, "85241 -> 14258");
  iarray_reverse(c,6); expect_iarray(c,d,6, "619321 -> 123916");
  return 0;
}



[実行例]
[h1910589@sol pt]$ ./w2_1b.out
OK 85241 -> 14258
 1 4 2 5 8
 1 4 2 5 8
OK 619321 -> 123916
 1 2 3 9 1 6
 1 2 3 9 1 6


[プログラムの説明]
iarray_read,iarray_printで配列の入力,出力を行い,iarray_reverseで
配列と配列の要素の数を受け取り,配列の要素を逆順に並べて返す. 与
えられた配列aを入れる要素数nの配列qを用意し,forでaに含まれる要素
を全てqの要素にコピーする.その後は同様にforでqの要素をコピーして
aに代入するのだが,この時要素の番号が逆順になるように工夫する.こ
のような仕組みで,最後に返される配列aには最初に与えられた時の逆順
で要素が入っている.


[考察]
iarray_reverseで配列のコピーを作り,また逆順に並べ替えた数列を作
る際にforで条件を指定した繰り返しを行う.これはC言語の関数では配
列を丸ごとそのまま扱うことができないのでforで条件を指定してひと
つひとつの要素を確認していくのが適切だからである.この仕組みを用
いる時に必要になるのがC言語におけるポインタ機能であり,この機能は
配列の要素に操作を加えることができる.

またこのプログラムではバグを見つけるためのテストが含まれている.プ
ログラム内の関数で導かれる答えと,それと実際に出た答えが一致して
いるかをチェックする関数をあらかじめ予想しておくことでテストを実
行している.コンピュータリテラシーや基礎プログラミングでは何通り
かの実行結果を確認してプログラムに間違いがないか手動で確認してい
たが,手動では確認できる範囲に限界があるので,このようなテストケー
スを用意するのはとても効率が良いし,正確なプログラムが作れると思っ
た.

___________________


演習2b(受け取った配列の内容を逆順にして列を返すプログラム)
[プログラムのソース]
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

void iarray_read(int *a, int n){
  for(int i=0; i<n; ++i){
    printf("%d> ",i+1);
    scanf("%d",a+i);
  }
}


void iarray_print(int *a, int n) {
for(int i = 0; i < n; ++i) {
  printf(" %2d", a[i]);
 }
 printf("\n");
}




bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}



void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}



int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}

void ivec_read(int *a) { iarray_read(a+1, a[0]); }
void ivec_print(int *a) { iarray_print(a+1, a[0]); }






int *ivec_reverse(int *a){
  int *m=ivec_new(a[0]);
  for(int i=0; i<=a[0]; ++i){m[i]=a[i];};
  for(int i=1; i<=a[0]; ++i){a[i]=m[a[0]+1-i];};
  return a;
}


int main(void){
  int a[]={4,6,1,2,9},b[]={4,9,2,1,6},c[]={3,3,5,7},d[]={3,7,5,3};
  int *v=ivec_reverse(a);
  int *w=ivec_reverse(c);
  expect_iarray(v,b,5,"6129 -> 9216");
  expect_iarray(w,d,4,"357 -> 753");
  return 0;
}


[実行例]
[h1910589@sol pt]$ ./w2_2b.out
OK 6129 -> 9216
  4  9  2  1  6
  4  9  2  1  6
OK 357 -> 753
  3  7  5  3
  3  7  5  3


[プログラムの説明]
ivec_newで長さを指定した長さの列を作り,ivec_readで列を読み込み
ivec_printで列を出力する.ivec_reverseが列を逆順に並べて返す.
ivec_read,ivec_printは事前に作ったiarray_readとiarray_printを使っ
ている.この関数では列の先頭にある数字は与えられた列の要素数であ
り,その次の要素から最後の要素までを配列とみなすことにしている.
ivec_reverseでは与えられた配列aと同じ大きさの配列mを用意して,for
を用いてaの要素全てをそのままコピーしmの要素に写す.その後またfor
でaにmの要素を一番後ろから順に写していく.こうして最後に返される
配列aの先頭は要素数,その後ろは与えられた配列が逆順にされたものに
なっている.

[考察]
演習1bとやっていることはほとんど同じであるが,演習2bではmallocを
用いて動的なメモリの確保を行なっている.演習1bでは要素数を引数と
して与える必要があるが,このプログラムでは要素数を与えなくても配
列に対して処理が行われる.演習1b同様にテストが行われるが,テストケー
スの数はもう少し増やせばより確実だったかもしれない.
________________________

[アンケート]
Q1. アドレス、ポインタを使うプログラムで注意すべきこ
とは何だと思いますか。
どのような操作が行われているかちゃんと理解すること
Q2. ここまでのところで、プログラムを作るときに重要だか
゙自分で身に付いていないと思うことは何ですか。
全体をよく見る
Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。 
難しいが頑張る

h1910596 2b:○ Mon May 25 21:21:28 2020


学籍番号: 1910596
ペア: 個人作業
提出日時: 2020/5/25

[1つ目の課題]
演習1b: 配列の並び順を逆順にする関数void iarray_reverse(int *a,
int n)を作成する。

・ソース
#include <stdio.h>
#include <stdbool.h>

void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

void iarray_reverse(int *a, int n) {
  int i;
  int k;
  for (i = 0; i < n / 2; ++i) {
  k = a[i];
  a[i] = a[n-1-i];
  a[n-1-i] = k;
  }
}

bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}

int main(void) {
  int a[] = {8, 5, 2, 4, 1}, b[] = {1, 4, 2, 5, 8};
  int c[] = {4}, d[] = {4};
  int e[] = {4, 9}, f[] = {9, 4};
  int g[] = {14, 64, 34, 44}, h[] = {44, 34, 64, 14};
  int i[] = {5, 8, 3, 1, 9, 0, 4, 6, 9}, j[] = {9, 6, 4, 0, 9, 1, 3, 8, 5};
  iarray_reverse(a, 5); expect_iarray(a, b, 5, "85241 -> 14258");
  iarray_reverse(c, 1); expect_iarray(c, d, 1, "4 -> 4");
  iarray_reverse(e, 2); expect_iarray(e, f, 2, "49 -> 94");
  iarray_reverse(g, 4); expect_iarray(g, h, 4, "14643444 -> 44346414");
  iarray_reverse(i, 9); expect_iarray(i, j, 9, "583190469 -> 964091385");
  return 0;
}

・実行例
OK 85241 -> 14258
  1  4  2  5  8
  1  4  2  5  8
OK 4 -> 4
  4
  4
OK 49 -> 94
  9  4
  9  4
OK 14643444 -> 44346414
 44 34 64 14
 44 34 64 14
OK 583190469 -> 964091385
  9  6  4  0  9  1  3  8  5
  9  6  4  0  9  1  3  8  5

・説明
配列の並び順を逆順にする関数void iarray_reverse(int *a, int n)を
作成した。nは渡された配列の長さである.a[i]を変数kに入れa[i]に
a[n-1-i]を入れる。最後にa[n-1-i]に変数kに入れておいたa[i]を入れ
る。この操作をiが0からnの1/2まで1ずつ増やしながら行うことによっ
て、配列の並びを逆順にすることができる。

・考察
はじめにこの関数を実行したときには、単体テストにおいて14258と表
示されるところが14242と表示され、どのような間違いが生じているの
かすぐにわかることができた。そして、どこに間違いがあるのかもわかっ
たので編集もすぐにできた。このように、単体テストによってプログラ
ムのどの部分がどのように間違っているかをすぐに判断することができ
ると考える。プログラムが複雑になればなるほど効果的だと考える。

[2つ目の課題]
演習1c: 何らかの整列アルゴリズムで配列を昇順に整列する関数void
iarray_sort(int *a, int n)を作成する。

・ソース
#include <stdio.h>
#include <stdbool.h>

void iarray_sort(int *a, int n) {
  int  k;
  for (int i = 0; i < n-1; ++i) {
    for (int j = i + 1; j < n; ++j) {
      if (a[i] > a[j]) {
         k = a[i];
         a[i] = a[j];
         a[j] = k;
      }
    }
  }
}

void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i <= n-1; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}

void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}

int main(void) {
  int a[] = {8,5,2,4,1}, b[] = {1,2,4,5,8};
  int c[] = {4,4,4}, d[] = {4,4,4};
  int e[] = {7}, f[] = {7};
  int g[] = {4,5,4}, h[] = {4,5,4};
  iarray_sort(a, 5); expect_iarray(a, b, 5, "85241 -> 12458");
  iarray_sort(c, 3); expect_iarray(c, d, 3, "444 -> 444");
  iarray_sort(e, 1); expect_iarray(e, f, 1, "7 -> 7");
  iarray_sort(g, 2); expect_iarray(g, h, 2, "454 -> 454");
  return 0;
}

・説明
受け取った配列を昇順に並び替える関数を作成した。まず、配列の最初
の値とそれ以降にある値を比べていき、最初の値よりも小さい値があれ
ばその値と最初の値の場所を入れ替える。この操作を配列の最後の要素
まで行うことにより、昇順に並び替えることができる。

・考察
for文を2つ使う事によって、うまくループさせることができたと思う。
今回用いたようなループは、もっといろんなものに活用できると考える。
このプログラムを最初に実行したところ、数字は同じはずなのにNGと表
示された。そこで、iarray_equalを詳しくみたところ、for文の中のi
<= n-1の部分がi <= nになっていたためであった。このように、プログ
ラムではちょっとしたミスで違う結果になってしまうので、正確さが重
要だと感じた。

[アンケート]
Q1.
ポインタがどこを指しているのかを把握すること。 
Q2.
プログラムの構造を理解しきれていないこと。
Q3.
単体テストの重要性を知ることができた。

i1910089 2b:○ Mon May 25 23:50:31 2020



1d
コード
#include<stdio.h>
#include<stdbool.h>
bool iarray_equal(int *a,int *b,int n){
    for(int i=0;i<n;i++)if(a[i]!=b[i])return false;
    return true;
}
void iarray_print(int *a,int n){
    for(int i=0;i<n;i++)printf(" %2d",a[i]);
    printf("\n");
}
void expect_iarray(int *a,int *b,int n,char *msg) {
    printf("%s %s\n",iarray_equal(a,b,n)?"OK":"NG",msg);
    iarray_print(a,n);iarray_print(b,n);
}
void iarray_add(int *a,int *b,int n){
    for(int i=0;i<n;i++)*(a+i)+=*(b+i);
}
int main(void){
    int a[]={8,5,2,4,1},b[]={1,1,2,2,3},c[]={9,6,4,6,4};
    iarray_add(a,b,5);expect_iarray(a,c,5,"85241+11223 -> 96464");
    int d[]={-5,3,10},e[]={-2,-4,23},f[]={-7,-1,33};
    iarray_add(d,e,3);expect_iarray(d,f,3,"-5,3,10+-2,-4,23 -> -7,-4,23");
}

実行例
OK 85241+11223 -> 96464
  9  6  4  6  4
  9  6  4  6  4
OK -5,3,10+-2,-4,23 -> -7,-4,23
 -7 -1 33
 -7 -1 33

説明
配列のサイズ分だけfor文を回して、配列bの値を配列aに足す。

考察
テストケースに使われた配列は破壊されるので単体テストごとに配列を用意した。
負の数や10以上の数でも正しい値が返ってくることが確認できた。

1e
コード
#include<stdio.h>
void expect_int(int i1,int i2,char *msg){printf("%s %d:%d %s\n",(i1==i2)?"OK":"NG",i1,i2,msg);}
int iadd(int x,int y){return x+y;}
int imax(int x,int y){return (x>y)?x:y;}
int iarray_inject(int *a,int n,int (*fp)(int,int)){
    if(n==1)return a[0];
    int result=fp(a[0],a[1]);
    for(int i=2;i<n;i++)result=fp(result,a[i]);
    return result;
}
int main(void){
    int a[]={8,5,2,4,1};
    expect_int(iarray_inject(a,5,iadd),20,"8+5+2+4+1");
    expect_int(iarray_inject(a,5,imax),8,"max(8,5,2,4,1)");
    int b[]={2,3,2,6};
    expect_int(iarray_inject(b,3,iadd),7,"2+3+2");
    int c[]={1};
    expect_int(iarray_inject(c,1,iadd),1,"1");
    expect_int(iarray_inject(c,1,imax),1,"max{1}");
    return 0;
}

実行例
OK 20:20 8+5+2+4+1
OK 8:8 max(8,5,2,4,1)
OK 7:7 2+3+2
OK 1:1 1
OK 1:1 max{1}

説明
渡された配列の1項目と2項目を関数fpに渡し、その返り値と3項目をfp
に渡す、という風に先頭から順に畳み込む。なお、配列のサイズが1だっ
た場合には次のメモリに何が格納されているのか分からないので配列の
1項目を返すようにした。

考察
テストケースは配列のサイズが1のものが含まれるようにした。
実行例でも正しい値を返すことができている。 

アンケート
Q1. 代入していないアドレスには何が入っているのか分からないので使わない(配列などで)。
Q2. ポインタが2つ以上出てきた時に混乱してしまう。
Q3. 以前よりはポインタを理解できるようになった。

k1710179 2b:○ Mon May 25 23:48:24 2020


1710179 河村 優周

プログラミング通論#2

1.課題
演習1ーb
#include <stdio.h>
#include <stdbool.h>

void iarray_reverse(int *a, int n) {
  int b;
  int x = 0, i;
  for(i = n-1; i >= n/2; --i) {  
    b = a[i]; a[i] = a[x]; a[x++] = b; 
  }
}
char *bool2str(bool b) { return b ? "true" : "false"; }
void expect_iarray(int *b1, int *b2, int x, char *msg, char *msg2) {
  bool judge = true;
  int i;
  for(i = 0; i < x; i++){
      if(b1[i] != b2[i]) judge = false;
  }
  printf("%s %s:%s\n", bool2str(judge), msg, msg2);
}
int main(void) {
  int a[6] = {9, 0, 0, 1, 2, 3}, b[6] = {-1, -3, -2, -4, -1, 0};
  int c[5] = {0, 0, 1, 2, 3}, d[6] = {3, 2, 1, 0, 0, 9};

  iarray_reverse(a, 6); expect_iarray(a, b, 6, "900123->321009", "-1-3-2-4-10");
  iarray_reverse(a, 6); expect_iarray(a, d, 6, "321009->900123", "321009");
  iarray_reverse(a, 6); expect_iarray(a, d, 6, "900123->321009", "321009");
  iarray_reverse(d, 6); expect_iarray(d, a, 6, "321009->900123", "321009");
  iarray_reverse(a, 5); expect_iarray(a, c, 5, "32100->00123", "00123");
  return 0;
}

2.簡単な説明

iarray_reverseの第一引数に入った配列を0項目から第二引数の数字の
項目まで逆順に入れ替える。iarray_reverseの中はswap文のようにした。
なので入れ替える回数は第二引数の二分の一である。expect_iarrayに
て逆順にした配列とそれと同じ形をした配列や違う形の配列を比較して
いる。

3.出力結果
PS C:\pro2> gcc -Wall pro2-2-1b.c
PS C:\pro2> ./a.exe
false 900123->321009:-1-3-2-4-10
false 321009->900123:321009
true 900123->321009:321009
false 321009->900123:321009
true 32100->00123:00123

4.考察

配列を引数として受け取るためにはサブルーチンでの引数をポインタ型
にしなければいけないことが分かった。またポインタ型の配列は普通の
配列のように値を参照できることが分かった。サブルーチンの引数に配
列を入れ形を変えるとメインルーチンでも配列の形が変わっているのが
不思議だった。ポインタを渡し、値を変えるとメイン内でも値が変わる
ことが分かった。

1.課題
演習1ーc
#include <stdio.h>
#include <stdbool.h>

void iarray_sort(int *a, int n) {
  int b;
  int x = 0, i, j;
  for(i = 0; i < n; i++) {  
    x = a[i]; b = i;
    for(j = i; j < n; j++){
      if(x >= a[j]){ 
        b = j;
        x = a[j];
      }
    }
    a[b] = a[i]; a[i] = x; 
  }
}
char *bool2str(bool b) { return b ? "true" : "false"; }
void expect_iarray(int *b1, int *b2, int x, char *msg, char *msg2) {
  bool judge = true;
  int i;
  for(i = 0; i < x; i++){
      if(b1[i] != b2[i]) judge = false;
  }
  printf("%s %s:%s\n", bool2str(judge), msg, msg2);
}
int main(void) {
  int a[6] = {9, 0, 0, 1, 2, 3}, b[6] = {-1, -3, -2, -4, -1, 0};
  int c[5] = {0, 0, 1, 2, 3}, d[6] = {0, 0, 1, 2, 3, 9};
  int e[6] = {1, 0, 2, 3, 0, 9};

  iarray_sort(a, 6); expect_iarray(a, b, 6, "900123->001239", "-1-3-2-4-10");
  iarray_sort(a, 6); expect_iarray(a, d, 6, "001239->001239", "001239");
  iarray_sort(a, 6); expect_iarray(a, e, 6, "001239->001239", "102309");
  iarray_sort(e, 6); expect_iarray(e, a, 6, "321009->900123", "001239");
  iarray_sort(a, 5); expect_iarray(a, c, 5, "001239->001239", "00123");
  return 0;
}

2.簡単な説明
iarray_sortの第一引数で配列で受け取り、第二引数で配列の項目を受け取る。
iarray_sort内では受け取った配列を選択ソートで整列させる。
expect_iarrayでソート済の配列とそれと同じ形または異なる形の配列と比較する。

3.出力結果
PS C:\pro2> gcc -Wall pro2-2-1c.c
PS C:\pro2> ./a.exe
false 900123->001239:-1-3-2-4-10
true 001239->001239:001239
false 001239->001239:102309
true 321009->900123:001239
true 001239->001239:00123

4.考察

サブルーチン内で値を頻繁に変えても文字化け等のエラーが出ないこと
がわかった。ポインタを渡すことに抵抗があったが思っていたより安全
であった。

1.課題
演習1ーd
#include <stdio.h>
#include <stdbool.h>

void iarray_add(int *a, int *b, int n) {
  int i;
  for(i = 0; i < n; i++) {  
    a[i] = a[i] + b[i];
  }
}
char *bool2str(bool b) { return b ? "true" : "false"; }
void expect_iarray(int *b1, int *b2, int x, char *msg) {
  bool judge = true;
  int i;
  for(i = 0; i < x; i++){
      if(b1[i] != b2[i]) judge = false;
  }
  printf("%s %s\n", bool2str(judge), msg);
}
int main(void) {
  int a[5] = {8,5,2,4,1}, b[5] = {1,1,2,2,3}, c[5] = {9,6,4,6,4};
  int d[5] = {10,7,6,8,7}, e[4] = {8,5,2,4};

  iarray_add(a, b, 5); expect_iarray(a, c, 5, "85241+11223->96464");
  iarray_add(a, b, 5); expect_iarray(a, d, 5, "96464+11223->107687");
  iarray_add(b, e, 4); expect_iarray(b, c, 4, "1122+8524->9646");
  iarray_add(a, b, 5); expect_iarray(a, c, 5, "107687+96463->1913101410");
  iarray_add(d, b, 5); expect_iarray(d, a, 5, "107687+96463->1913101410");
  return 0;
}

2.簡単な説明

iarray_addの第一引数と第二引数で配列を受け取り、第三引数で配列の
項目を受け取る。iarray_addでは受け取った二つの配列を第三引数の項
目の数だけそれぞれの項を足し合わせていく。expect_iarrayでは足し
合わされた配列と同じ形かまたは異なる形をした配列を比較する。

3.出力結果
PS C:\pro2> gcc -Wall pro2-2-1d.c
PS C:\pro2> ./a.exe
true 85241+11223->96464
true 96464+11223->107687
true 1122+8524->9646
false 107687+96463->1913101410
true 107687+96463->1913101410v

4.考察

サブルーチン内での引数で受け取ったポインタ配列同士の足し算ができ
ることが分かった。

1.課題
演習1ーe
#include <stdio.h>
#include <stdbool.h>

int iadd(int *a, int n){
    int i, sum = 0;
    for(i = 0; i < n; i++){
        sum += a[i];
    }
    return sum;
}

int imax(int *a, int n){
    int i, x;
    x = a[0];
    for(i = 0; i < n; i++){
        if(x < a[i]) x = a[i];
    }
    return x;
}

int iarray_inject(int *a, int n, int (*fp)(int*, int)) {
  return (*fp)(a, n);
}

char *bool2str(bool b) { return b ? "true" : "false"; }
void expect_int(int b1, int b2, char *msg) {
  printf("%s %s\n", bool2str(b1 == b2), msg);
}
int main(void) {
  int a[5] = {8,5,2,4,1} , b[5] = {-1,4,2,0,7};
  expect_int(iarray_inject(a, 5, iadd), 20, "8+5+2+4+1");
  expect_int(iarray_inject(a, 5, imax), 8, "max(8,5,2,4,1)");
  expect_int(iarray_inject(a, 5, iadd), 17, "8+5+2+4+1");
  expect_int(iarray_inject(a, 5, imax), 5, "max(8,5,2,4,1)");
  expect_int(iarray_inject(b, 5, iadd), 12, "-1+4+2+0+7");
  expect_int(iarray_inject(b, 5, imax), 7, "max(-1,4,2,0,7)");
  return 0;
}

2.簡単な説明

iarray_injectは第一引数で配列、第二引数で配列の項目、第三引数で
サブルーチン名を受け取る。iarray_inject内では引数で受け取ったサ
ブルーチンに配列と配列の項目を入れるのみである。またサブルーチン
は配列のそれぞれの項の総和を求めるiaddと最大値を求めるimaxがある。

3.出力結果
PS C:\pro2> gcc -Wall pro2-2-1e.c
PS C:\pro2> ./a.exe
true 8+5+2+4+1
true max(8,5,2,4,1)
false 8+5+2+4+1
false max(8,5,2,4,1)
true -1+4+2+0+7
true max(-1,4,2,0,7)

4.考察

サブルーチンの引数でサブルーチンの名前が受け取れるということを初
めて知り驚いた。メインルーチン内でいちいちサブルーチンを変えず
iarray_injectだけで事を済ませることができるのはスマートだなと思っ
た。

1.課題
演習2ーa
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
int *ivec_concat(int *a, int *b) {
  int i = 1, j = 1, x = 1;
  int *c = ivec_new(a[0]+b[0]);
  while(j <= b[0] || i <= a[0]){ 
    if(j <= b[0] && i <= a[0]){
      if(i <= a[0] && x%2) c[x++] = a[i++]; 
      else if(j <= b[0])c[x++] = b[j++];
    } 
    if(i > a[0]) c[x++] = b[j++];
    else if(j > b[0]) c[x++] = a[i++];
  }
  return c;
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf("%2d", a[i]); }
  printf("\n");
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
  int a[] = {3,1,2,3}, b[] = {3,4,5,6}, c[] = {6,1,4,2,5,3,6};
  int d[] = {5,0,0,0,0,0}, e[] = {8,1,0,2,0,3,0,0,0};
  int *p = ivec_concat(a, b);
  expect_iarray(p, c, 7, "[1,2,3]+[4,5,6]=[1,4,2,5,3,6]");
  p = ivec_concat(a, d);
  expect_iarray(p, e, 9, "[1,2,3]+[0,0,0,0,0]=[1,0,2,0,3,0,0]");
  return 0;
}

2.簡単な説明
ivec_concatは第一引数と第二引数で配列を受け取る。
ivec_concat内では受け取った二つの配列を交互に一つの配列に入れる。
新しい配列の項数は、引数で受け取った配列の初項には参照したい項数が入っているため、
二つの配列の初項を足し合わせたサイズで作ることができる。また、その初項を項数とする。
片方の配列のほうが項数が多い場合は新しい配列の後ろにつける。

3.出力結果
PS C:\pro2> gcc -Wall pro2-2-2a.c
PS C:\pro2> ./a.exe
OK [1,2,3]+[4,5,6]=[1,4,2,5,3,6]
 6 1 4 2 5 3 6
 6 1 4 2 5 3 6
OK [1,2,3]+[0,0,0,0,0]=[1,0,2,0,3,0,0]
 8 1 0 2 0 3 0 0 0
 8 1 0 2 0 3 0 0 0

4.考察
ポインタ配列のサイズをサブルーチンで定義できることを初めて知った。
そしてこの場合メインルーチンに返り値として配列を渡したときにエラーが出ないので
便利だなと思った。

4.アンケート
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。
ポインタをよく理解しておくこと。
Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に付い
ていないと思うことは何ですか。
配列を引数にいれたり返り値で受け取ったりするとき迷ってしまう。
Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
課題がんばります。

m1810587 2b:○ Mon May 25 12:30:53 2020


1810587
個人作業
2020-05-25

課題1
演習1c 何らかの整列アルゴリズムで配列を昇順に整列する関数を作成する。
(expect_iarrayなどのテキストに登場する関数はtest.hにまとめた。)

ソースコード
#include<stdio.h>
#include<stdbool.h>
#include "test.h"

void iarray_sort(int *a,int n){
        for(int i = 0;i < n - 1;++i){
                int min = *(a+i);
                for(int j = i+1;j < n;j++){
                        if(min > *(a+j)){
                                min = *(a+j);
                                *(a+j) = *(a+i);
                                *(a+i) = min;
                        }
                }


        }
}

int main(void){
        int a[] = {3,6,1,7,2},a_ans[] = {1,2,3,6,7};
        int b[] = {12,5,2,8,23,6},b_ans[] = {2,5,6,8,12,23};
        int c[] = {1,13,1,1,3},c_ans[] = {1,1,1,3,13};
        iarray_sort(a,5);expect_iarray(a,a_ans,5,"36172 -> 12367");
        iarray_sort(b,6);expect_iarray(b,b_ans,6,"12528236 ->25681223");
        iarray_sort(c,5);expect_iarray(c,c_ans,5,"113113 -> 111313");
}

プログラムの説明
配列の最初の値を最小値として変数に格納する。
そのあと最初の値を配列の全ての値と順々に比較していって、もし変数
に格納した値より小さな値があれば、現在変数に格納している値と見つ
かったより小さな値の位置を入れ替える。
配列の最後まで比較が終わったら配列の最初の値の次の値を最小値とし
て変数に格納し、上記で行ったのと同じ動作を行う。
これを配列の最後まで繰り返す。

実行結果
OK 36172 -> 12367
  1  2  3  6  7
  1  2  3  6  7
OK 12528236 ->25681223
  2  5  6  8 12 23
  2  5  6  8 12 23
OK 113113 -> 111313
  1  1  1  3 13
  1  1  1  3 13

考察
最初より小さい変数を見つけた際に交換をする処理をすべきところを代
入操作だけにしてしまったため、帰ってくる配列が全然別物になってし
まうというミスを犯した。
頭では交換しなくてはいけないと分かっていたのだが、実際コードを書
くと代入処理だけで好感が出来ていると思ってしまい、それに気づくこ
とが出来なかった。
このコードによってデータがどうなっているのかを可能な限りトレース
してみるのがプログラムを書く上で重要だと感じた。

課題2
演習2a 二つの列を受け取り両方の列の内容を交互に並べた列を作れ。

ソースコード
#include<stdio.h>
#include<stdlib.h>
#include"test.h"

int *ivec_new(int size){
        int *a = (int*)malloc((size+1) * sizeof(int));
        a[0] = size; return a;
}

void ivec_read(int *a){iarray_read(a+1,a[0]);}
void ivec_print(int *a){iarray_print(a+1,a[0]);}

int *ivec_concat(int *a,int *b){
        int *c = ivec_new(a[0]+b[0]);
        for(int i = 1; i <= a[0]; ++i){c[i] = a[i];}
        for(int i = 1; i <= b[0]; ++i){c[i + a[0]] = b[i];}
        return c;
}

int *ivec_kougo(int *a,int *b){
        int *c = ivec_new(a[0]+b[0]);
        for(int i = 1; i <= a[0]; ++i){c[2*i-1] = a[i];}
        for(int i = 1; i <= b[0]; ++i){c[2*i] = b[i];}
        return c;
}

int main(void){
        int a[] = {3,3,5,6},b[] = {3,10,20,30};
        int c[] = {4,1,2,3,4},d[] = {4,0,0,0,0};
        int e[] = {6,3,10,5,20,6,30},f[] = {8,1,0,2,0,3,0,4,0};
        int *ab = ivec_kougo(a,b);
        int *cd = ivec_kougo(c,d);
        expect_iarray(ab,e,7,"[3,5,6] + [10,20,30] = [3,10,5,20,6,30]");
        expect_iarray(cd,f,9,"[1,2,3,4] + [0,0,0,0] = [1,0,2,0,3,0,4,0]");
        return 0;
}

説明
両方の列を交互に並べた列を入れる配列として、cを作成し領域を確保する。
そのあと配列番号が奇数のところにaの要素を、偶数のところにbの要素
を格納し、cを返す。

実行結果
OK [3,5,6] + [10,20,30] = [3,10,5,20,6,30]
  6  3 10  5 20  6 30
  6  3 10  5 20  6 30
OK [1,2,3,4] + [0,0,0,0] = [1,0,2,0,3,0,4,0]
  8  1  0  2  0  3  0  4  0
  8  1  0  2  0  3  0  4  0

考察
最初は二つあるfor文を一つにまとめ、条件分岐でどちらの要素を入れ
るかを決めて記述しようと思っていたが二つに分けた方が圧倒的に簡単
に記述できることに気が付いた。ループの必要回数が1回で良さそうな
時も、回数を増やすことで楽に出来ないか検討するようにしたい。

Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いま
すか。
segmentation fault 11

Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に付いて
いないと思うことは何ですか。
先に擬似コードを考えること。

Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
ポインタには苦手意識がありましたが、なんとか課題をこなすことが出
来たと思います。

m1810639 2b:△ Tue May 19 17:38:03 2020


学籍番号:1810639
個人作業
提出日時:5/19

課題の再掲1
2つの列を交互に並べた配列をつくる。

ソース1
int *ivec_add(int *a, int *b) {   //演習2−1
  int *c = ivec_new(a[0] + b[0]);
  for(int i = 1;i <= c[0]; i++) {
    c[2*i-1] = a[i];
    c[2*i] = b[i];
  }
  return c;
}

単体テスト1
 int a[] = {3,1,2,3}, b[] = {3,4,5,6}, c[] = {6,1,4,2,5,3,6};
 int *p = ivec_add(a, b);
 expect_iarray(p, c, 7, "[1,2,3]+[4,5,6]=[1,4,2,5,3,6]");

実行例1
OK [1,2,3]+[4,5,6]=[1,4,2,5,3,6]
  6  1  4  2  5  3  6
  6  1  4  2  5  3  6


説明1

新しい配列cをつくり、サイズを決めた。そして、配列cの奇数番号に一
つめの配列の要素、偶数番号に二つめの配列の要素をいれた。

課題の再掲2
配列の要素を昇順に並べる。

ソース2
int find_min(int *a, int b) {
  int min = a[b], min_i = b;
  for(int i = b;i <= a[0];i++) {
    if(min > a[i]) {
      min = a[i];
      min_i = i;
    }
  }
  return min_i;
}
int *ivec_syojun(int *a) {        //演習2ーc
  int min_i, b;
  for(int i = 1;i < a[0];i++) {
    min_i = find_min(a, i);
    b = a[i]; a[i] = a[min_i]; a[min_i] = b;
  }
  return a;
}

単体テスト2
  int a[] = {3,4,1,2}, b[] = {3, 1, 2, 4};
  int c[] = {5,-3,9,-5,4,2}, d[] = {5,-5,-3,2,4,9};
  int *p = ivec_syojun(a);
  expect_iarray(p, b, 4, "[1, 2, 4]");
  expect_iarray(ivec_syojun(c),d,6,"[-5 -3 2 4 9]");

実行例2
OK [1, 2, 4]
  3  1  2  4
  3  1  2  4
OK [-5 -3 2 4 9]
  5 -5 -3  2  4  9
  5 -5 -3  2  4  9


説明2

昇順に並べる関数の他に配列の要素の中で一番小さい要素の番号を見つ
ける関数を作り、その番号を昇順に並べる関数で使って、配列を並べ直
した。

アンケート
Q1
アドレスとポインタの型を合わせることが大事だと思った。
Q2
関数を作るときに、その関数の働きや何の引数かを書いておくこと。
Q3
一つの関数をつくるときに補助にする関数を作って完成させることができたのでよかった。

m1910598 2b:○ Mon May 25 23:40:47 2020


1. 課題の再掲
1-b 配列の並び順を逆順にする関数void iarrayrevese(int *a, int n)を作成する。
2-a 2つの列を受け取り、両方の列の内容を交互に並べた列を返す。

2. プログラムのソースとその説明及び考察
1-b
(単体テストを含めたプログラム)
#include <stdio.h>
#include <stdbool.h>
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
void iarray_reverse(int *a,int n){
    int rev;
    for(int i=0;i<n/2;++i){
        rev=a[i];
        a[i]=a[n-i-1];
        a[n-i-1]=rev;
    }
}
int main(void){
    int a[]={4,3,2,1},b[]={1,2,3,4},c[]={0,2,4,6,8},d[]={8,6,4,2,0},e[]={1},f[]={};
    iarray_reverse(a,4);expect_iarray(a,b,4,"4321->1234");
    iarray_reverse(c,5);expect_iarray(c,d,5,"02468->86420");
    iarray_reverse(e,1);expect_iarray(e,e,1,"1->1");
    iarray_reverse(f,0);expect_iarray(f,f,0,"[]->[]");
    return 0;
}
(実行例)
OK 4321->1234
  1  2  3  4
  1  2  3  4
OK 02468->86420
  8  6  4  2  0
  8  6  4  2  0
OK 1->1
  1
  1
OK []->[]


(説明)
int型の変数revを用意し、配列の長さが奇数なら長さの半分を切り捨て
したものまで、偶数なら長さの半分までint型の変数iを変化させ、for
文を利用して端から順に内容を入れ替えていくプログラムである。

(考察)
iの範囲設定を工夫したことで簡潔にプログラムが書けたと思う。今回
は変数を用意して直接値を入れ替えていったが、間接参照演算子を使え
ばまた違ったプログラムの書き方ができそうだと思った。

2-a
(単体テストを含めたプログラム)
#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>
void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
void ivec_read(int *a) { iarray_read(a+1, a[0]); }
void ivec_print(int *a) { iarray_print(a+1, a[0]); }
int *ivec_alter(int *a, int *b) {
  int *c=ivec_new(a[0]+b[0]);
  if(a[0]>b[0]){
    for(int i=1;i<=b[0];i+=2){
      c[i]=a[(i+1)/2];
      c[i+1]=b[(i+1)/2];
      }
    for(int i=2*b[0]+1;i<=a[0]+b[0];++i){
      c[i]=a[i-b[0]];
      }
  }else if(a[0]<b[0]){
    for(int i=1;i<=a[0];i+=2){
      c[i]=a[(i+1)/2];
      c[i+1]=b[(i+1)/2];
      }
    for(int i=2*a[0]+1;i<=a[0]+b[0];++i){
      c[i]=b[i-a[0]];
      }
  }
  for(int i=1;i<=2*a[0]-1;i+=2){c[i]=a[(i+1)/2];c[i+1]=b[(i+1)/2];}
  return c;
}
int main(void){
  int a[]={3,1,3,5},b[]={3,2,4,6},c[]={6,1,2,3,4,5,6},d[]={2,2,4},e[]={5,1,2,3,4,5},f[]={5,2,1,4,3,5};
  expect_iarray(ivec_alter(a,b),c,7,"[1,2,3,4,5,6]");
  expect_iarray(ivec_alter(a,d),e,6,"[1,2,3,4,5]");
  expect_iarray(ivec_alter(d,a),f,6,"[2,1,4,3,5]");
  return 0;
}
(実行例)
OK [1,2,3,4,5,6]
  6  1  2  3  4  5  6
  6  1  2  3  4  5  6
OK [1,2,3,4,5]
  5  1  2  3  4  5 
  5  1  2  3  4  5
OK [2,1,4,3,5]
  5  2  1  4  3  5  
  5  2  1  4  3  5 
(説明)
まず2つの配列が同じ長さかそうでないかということについて場合分け
をし、もし同じでない場合には、for文を利用してint型の変数iを短い
方の配列の長さまで2つずつ変化させて途中まで内容を交互に並べ、短
い方の配列の内容が全て並べ終わったら長い方の配列の内容を順に並べ
ていくようにした。配列の長さが同じ場合は、for文を利用してint型の
変数iを配列の長さまで2つずつ変化させてi番目に1爪の配列の内容、
i+1に2番目の配列の内容を順に並べていくプログラムである。

(考察)
iを2ずつ変化させたことで、1回のループで2つの配列の内容が同時に並
べられるようになることが発見できた。また、配列の最初の数が配列の
長さとして設定されているため、単体テストを行う際に長さの設定には
気を付けなければいけないと思った。

3. アンケート
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。
間接参照演算子がどのように使われているか
Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に
付いていないと思うことは何ですか。
ポインタ型の扱い
Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
単体テストを行うことで、本当にプログラムを正しくかけているのか吟
味できるというのは新しい発見であった。また、アドレスとポインタに
ついての理解が足りていないので復習するようにしたい。

m1910601 2b:○ Mon May 25 19:21:33 2020


1910601
「個人作業」
5月25日

「1つ目の課題」
(演習1-c)何らかの整列アルゴリズムで配列を昇順に整列させる関数
void iarray_sortをつくること。

[プログラムのソース]
 実際に書いたプログラムは次の通りである。

// 1-c --- iarray_sort
#include <stdio.h>
#include <stdbool.h>
void iarray_read(int *a, int n) {
  int i;
  for(i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  int i;
  for(i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
bool iarray_equal(int *a, int *b, int n) {
  int i;
  for(i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}


void iarray_sort(int *a, int n) {
  int i, k, y, num, small;
  for(i = 0; i < n; ++i) {
    small = a[i]; num = i;
    for(k = i; k < n; ++k) {
      if(small > a[k]) { small = a[k]; num = k;}
    }
    if(num != i){y = a[i];
      a[i] = small;
      a[num] = y;
    }
  }
}


char *bool2str(bool b) { return b ? "true" : "false"; }
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
  int a[] = {8,5,2,4,1}, b[] = {1,2,4,5,8},c[] = {3,1,4}, d[] = {1,3,4}, e[] = {9,3,1,5}, f[] = {1,3,5,9};
  iarray_sort(a, 5); expect_iarray(a, b, 5, "85241 -> 12458");
  iarray_sort(c, 3); expect_iarray(c, d, 3, "314 -> 134");
  iarray_sort(e, 4); expect_iarray(e, f, 4, "9315 -> 1359");
  return 0;
}

[プログラムの説明]
今回は整列アルゴリズムとして選択ソートを使用した。関数void
iarray_sortで、1つ目のiを用いたループは、選択ソートで最小の要素
を見つけるために調べる要素の範囲を決めるためにつくった。まず、ルー
プ内で最小の要素を入れるsmallにa[i]を、その要素が入っていた番号
を記録するnumにiを入れる。次にkを用いた2つ目のループ内でkをiか
らn-1まで変化させ、smallよりも小さい要素があればその都度smallに
はその要素の値を、numには配列の番号を入れ、kを用いたループの終了
後にa[k]とa[i]を入れ替えた。以降iの値を上げてループさせることで
整列が完了していないn-i個の要素について同様の操作を行い、昇順に
並べ、その配列を返した。

[単体テストと実行例]
 単体テストは以下の通りである。

int main(void) {
  int a[] = {8,5,2,4,1}, b[] = {1,2,4,5,8},c[] = {3,1,4}, d[] = {1,3,4}, e[] = {9,3,1,5}, f[] = {1,3,5,9};
  iarray_sort(a, 5); expect_iarray(a, b, 5, "85241 -> 12458");
  iarray_sort(c, 3); expect_iarray(c, d, 3, "314 -> 134");
  iarray_sort(e, 4); expect_iarray(e, f, 4, "9315 -> 1359");
  return 0;
}

 実行例は次のようになり、しっかりと動作していることが確認できた。

$ ./a.out
OK 85241 -> 12458
  1  2  4  5  8
  1  2  4  5  8
OK 314 -> 134
  1  3  4
  1  3  4
OK 9315 -> 1359
  1  3  5  9
  1  3  5  9

[考察]
この課題をやっているとき、テストケースに助けられた場面があった。
間違えていた個所はループで指定する数が違ったというケアレスミスで
あったが、今まで通りに数字を入れていき、何が間違っているのかを1
回1回探っていくよりも、単体テストを実行する方が値なども表記して
確かめるため、ミスに気づきやすく、修正が早くできた。単体テストで
は、表示上での違いはもちろん、同じ文字列が表示されていても、関数
として表すものが違うことでもNGが表示されるため、確認方法としてか
なり優れていることを実感できた。もちろん、ケアレスミスをしないの
が一番いいのだが、プログラムを書いていく上で単に関数をつくるだけ
でなく、単体テストのように確認する構造をつくることも重要だと考え
られる。

ーーーーーーーーーー

「2つ目の課題」
(演習2-a)2つの配列を受け取り、両方の列の内容を交互に並べた列
を返す関数をつくること。

[プログラムのソース]
 実際に作成したプログラムは以下の通りである。

// 2-a ----- cross
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
void ivec_read(int *a) { iarray_read(a+1, a[0]); }
void ivec_print(int *a) { iarray_print(a+1, a[0]); }


int *ivec_crosscat(int *a, int *b) {
  int *c = ivec_new(a[0]+b[0]);
  int syo = a[0];
  bool han = true;
  if(a[0] > b[0]){syo = b[0]; han = false;}
  for(int i = 1; i <= syo; ++i) { c[2*i-1] = a[i]; }
  for(int i = 1; i <= syo; ++i) { c[2*i] = b[i]; } 
  int k = 1;
  if(han == true){for(int i = syo; i < b[0]; ++i){c[2 * syo + k] = b[i+1]; k = k + 1;}}
  if(han == false){for(int i = syo; i < a[0]; ++i){c[2 * syo + k] = a[i+1]; k = k + 1;}}
 return c;
}


bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
int a[] = {3,1,2,3}, b[] = {2,4,5}, c[] = {5,1,4,2,5,3};
int d[] = {4, 1, 3, 5, 7}, e[] = {2, 2, 4}, f[] = {6,2,1,4,3,5,7}, g[] = {4,4,2,5,4};
int *p = ivec_crosscat(a, b);
expect_iarray(p, c, 6, "[1,2,3]+[4,5]=[1,4,2,5,3]");
int *q = ivec_crosscat(e, d);
expect_iarray(f, q, 7, "[2,4]+[1,3,5,7]=[2,1,4,3,5,7]");
int *r = ivec_crosscat(b, e);
expect_iarray(r, g, 5, "[4,5]+[2,4]=[4,2,5,4]");
return 0;
}

[プログラムの説明]
 まず、与えられた2つの配列の長さが異なるときには、はじめに交互に並べたあとに、まだ並べていないものをその後ろに続けて並べていくようにした。
 (例: [1,3,5,7]と[2,4]ならば[1,2,3,4,5,7]となる)

配列を交互に並べる関数をint *ivec_crosscatとして作成した。*c =
ivenc_new(a[]+b[])では、cの配列の長さをaとbの配列の長さを足して
決めている。関数内の2行目 syo = a[0] から4行目までは、aとb、どち
らの配列の方が短いかを調べており、短い方の長さをint型のsyoに入れ、
またbool型のhanを用いて、aの方が短い時はtrueを、bの方が短い時は
falseとしてのちの条件分岐に使用した。続いて関数内の5、6行目では
ループを用いて配列の内容を交互に並べていき、7行目以降では先ほど
のbool型のhanにより短かった方を判定して、長かった配列の方のまだ
並べられていないものをcに入れていった。

[単体テストと実行例]
 単体テストは次の通りである。

int main(void) {
int a[] = {3,1,2,3}, b[] = {2,4,5}, c[] = {5,1,4,2,5,3};
int d[] = {4, 1, 3, 5, 7}, e[] = {2, 2, 4}, f[] = {6,2,1,4,3,5,7}, g[] = {4,4,2,5,4};
int *p = ivec_crosscat(a, b);
expect_iarray(p, c, 6, "[1,2,3]+[4,5]=[1,4,2,5,3]");
int *q = ivec_crosscat(e, d);
expect_iarray(f, q, 7, "[2,4]+[1,3,5,7]=[2,1,4,3,5,7]");
int *r = ivec_crosscat(b, e);
expect_iarray(r, g, 5, "[4,5]+[2,4]=[4,2,5,4]");
return 0;
}

 実行例は次のようになっており、しっかりと動作していることが確認できた。

./a.out
OK [1,2,3]+[4,5]=[1,4,2,5,3]
  5  1  4  2  5  3
  5  1  4  2  5  3
OK [2,4]+[1,3,5,7]=[2,1,4,3,5,7]
  6  2  1  4  3  5  7
  6  2  1  4  3  5  7
OK [4,5]+[2,4]=[4,2,5,4]
  4  4  2  5  4
  4  4  2  5  4


[考察]
配列の長さを配列の中に記録することで、長さ自体の管理は先頭のデー
タだけで行えるため、可変長配列は扱いやすかった。この課題をやって
いるとき、配列の長さが異なるときにどうやって後ろに続けるかが悩み
どころであった。はじめは配列の長い方を判断することで管理をしよう
としていて手が止まってしまったが、逆に短い方を判断するようにする
と、あっさりと書けるようになった。はじめから短い方を判断にとって
おけばいい、という話ではあるが、今回のケースのように手が止まった
際には、判断基準としていたものを変えてみるとあっさり行くことは他
の場面でもあると思われる。そのため、プログラムを書く際には今回の
ように判断基準を変えていくことも留意していきたい。

[アンケート]
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いま
すか。
どこの番地を使用しているのかというイメージをしっかりもつことと、
その管理をすることだと思います。
Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に付いて
いないと思うことは何ですか。
関数の全体構造を考えてから書きはじめることです。
Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
まだポインタに苦手意識があるので、しっかり復習したいです。

m1910603 2b:○ Mon May 25 20:42:52 2020


課題2b


1.b
配列の並び順を逆順にする関数

#include <stdio.h>
#include <stdbool.h>

void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

void iarray_reverse(int *a, int n){
  int c[n];
  for(int i = 0; i < n; ++i){
    c[i] = a[i];
  }
  for(int j = 0; j < n; ++j){
    a[j] = c[n-1-j];
  }
}

bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}

void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}

int main(void){
  int a[] = {8,5,2,4,1} , b[] = {1,4,2,5,8};
  iarray_reverse(a,5); expect_iarray(a,b,5,"85241 -> 14258");
  int c[] = {5,7,8,3,6,2} , d[] = {2,6,3,8,7,5};
  iarray_reverse(c,6); expect_iarray(c,d,6,"578362 -> 263875");
  int e[] = {8,-2,-4,5} , f[] = {5,-4,-2,8};
  iarray_reverse(e,4); expect_iarray(e,f,4,"8-2-45 -> 5-4-28");
  return 0;
}

実行例
OK 85241 -> 14258
  1  4  2  5  8
  1  4  2  5  8

OK 578362 -> 263875
  2  6  3  8  7  5
  2  6  3  8  7  5


OK 8-2-45 -> 5-4-28
  5 -4 -2  8
  5 -4 -2  8

関数iarray_reverse内で新しい配列cを定義し、配列aの中身をコピー。
コピーしたcの中身の逆順をaに上書きした。

考察
関数の戻り値として配列を返せないため元からある配列aを上書きする
形となった。この時a[i] = a[n-1-i]として繰り返すと1 4 2 4 1という
配列となった。これはaの後半部分を上書きするとき、すでに前半部分
が上書きされ元のaと違う値となっているからである。なのでaの上書き
に影響されないように一度cにaをコピーし、それの逆順をaに上書きし
た。


1.d
2つの配列を受け取り、2番目の各要素の値を1番目の各要素に足す関数

#include <stdio.h>
#include <stdbool.h>

void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

void iarray_add(int *a, int *b, int n){
  for(int i = 0; i < n; ++i){
    a[i] = a[i] + b[i];
  }
}

bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}

void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}

int main(void){
  int a[] = {8,5,2,4,1} , b[] = {1,1,2,2,3} , c[] = {9,6,4,6,4};
  iarray_add(a,b,5); expect_iarray(a,c,5,"85241+11223 -> 96464");
  int d[] = {8,5,2,4,1} , e[] = {-3,3,1,1,-2} , f[] = {5,8,3,5,-1};
  iarray_add(d,e,5); expect_iarray(d,f,5,"85241+-3311-2 -> 5835-1");
 int g[] = {8,5,6,7,4,2} , h[] = {3,3,2,2,1,1} , i[] = {11,8,8,9,5,3};
  iarray_add(g,h,6); expect_iarray(g,i,6,"856742+332211 -> 1188953");
  return 0;
}

実行例
OK 85241+11223 -> 96464
  9  6  4  6  4
  9  6  4  6  4

OK 85241+-3311-2 -> 5835-1
  5  8  3  5 -1
  5  8  3  5 -1

OK 856742+332211 -> 1188953
 11  8  8  9  5  3
 11  8  8  9  5  3

関数iarray_addで各要素ごとに足し算をし、それを繰り返した。

考察
1.bと同様に配列aに上書きする形となった。
長さが違う配列の場合のときも長いほうの配列を基準とすると上手くで
きそうだと考えた。

Q1.参照先の把握
Q2.簡潔な書き方ができていない
Q3.特になし


m1910606 2b:○ Mon May 25 21:48:36 2020


課題2b
学籍番号 1910606
名前@@@ 牧野 広隼
提出日時 5月25日

演習1-b
課題の再掲
配列の並び順を逆順にする関数を作成する。

プログラムのソース
#include <stdio.h>
#include <stdbool.h>
void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void iarray_reverse(int *a, int n){
  int x;
  for(int i = 0;i < n/2 ;++i){
    x = a[i];
    a[i] = a[n-1-i];
    a[n-1-i] = x;
  }
}

void expect_iarray(int *b1, int *b2, int n, char *msg) {
  printf("%s %s\n", iarray_equal(b1,b2,n)?"OK":"NG",msg);
         iarray_print(b1,n),iarray_print(b2,n);
}
int main(void) {
  int a[] = {8,5,2,4,1}, b[] = {1,4,2,5,8}, c[] = {3,2,5,7,8,0}, d[] = {0,8,7,5,2,3};
  iarray_reverse(a, 5);
  expect_iarray(a, b, 5, "85241 -> 14258");
  iarray_reverse(c, 6);
  expect_iarray(c, d, 6, "325780 -> 087523");
  return 0;
}

実行例
[m1910606@sol ~]$ ./a.out
OK 85241 -> 14258
  1  4  2  5  8
  1  4  2  5  8
OK 325780 -> 087523
  0  8  7  5  2 3
  0  8  7  5  2 3


説明

配列を逆順にする関数は、まず、頭の値をxに入れ、頭に末尾の数値を
代入、そして、xに入れた値を末尾に代入することで入れ替えを行い、
配列の内側へそれを繰り返した。テストは要素数が奇数であるものと偶
数であるものを試した。

考察
逆順を作る関数は悩むことなく完成させることができた。単体テストを
出力する際、expect_iarrayのprintfの中にiarray_printを入れていた
ためうまくいかなかった。iaaray_printの中にはもともとprintfが入っ
ているのでできないのは当たり前だ気づいた。

演習2-a
課題の再掲
2つの配列を交互に並べた列を返す関数を作成する。

プログラムのソース
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}

int *ivec_concat(int *a, int *b) {
  int *c = ivec_new(a[0]+b[0]);
  if(a[0] == b[0]){
    for(int i = 1; i <= a[0]; ++i){
      c[2*i-1] = a[i];
      c[2*i] = b[i];
    }
  }
  if(a[0] > b[0]){
    for(int i = 1; i <= b[0]; ++i){
      c[2*i-1] = a[i];
      c[2*i] = b[i];
    }
    for(int i = 1; i <= a[0]-b[0]; ++i){
      c[2*b[0]+i] = a[b[0]+i];
    }
  }
  if(b[0] > a[0]){
    for(int i = 1; i <= a[0]; ++i){
      c[2*i-1] = a[i];
      c[2*i] = b[i];
    }
    for(int i = 1; i <= b[0]-a[0]; ++i){
      c[2*a[0]+i] = b[a[0]+i];
    } 
  }
  return c;
}

bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
  int a[] = {3,1,3,5}, b[] = {3,2,4,6}, c[] = {6,1,2,3,4,5,6},
      d[] = {2,5,6}, e[] = {3,2,6,8}, f[] = {5,5,2,6,6,8},
      g[] = {4,7,3,0,4}, h[] = {2,7,1}, i[] = {6,7,7,3,1,0,4};
  int *p = ivec_concat(a, b);
  int *q = ivec_concat(d, e);
  int *r = ivec_concat(g, h);
  expect_iarray(p, c, 7, "[1,3,5]+[2,4,6]=[1,2,3,4,5,6]");
  expect_iarray(q, f, 6, "[5,6]+[2,6,8]=[5,2,6,6,8]");
  expect_iarray(r, i, 7, "[7,3,0,4]+[2,7,1]=[7,7,3,1,0,4]");
  return 0;
}

実行例
[m1910606@sol ~]$ ./a.out
OK [1,3,5]+[2,4,6]=[1,2,3,4,5,6]
  6  1  2  3  4  5  6
  6  1  2  3  4  5  6
OK [5,6]+[2,6,8]=[5,2,6,6,8]
  5  5  2  6  6  8
  5  5  2  6  6  8
OK [7,3,0,4]+[2,7,1]=[7,7,3,1,0,4]
  6  7  7  3  1  0  4
  6  7  7  3  1  0  4


説明
一つ目の配列が奇数番目に、二つ目の配列が偶数番目に来るように作成
した。(配列の最初の値は要素数のため数えない。)列の長さが違う場
合、余った要素はそのまま順に出力するようにした。単体テストは二つ
の列の長さを変えて行った。

考察
二つの列の長さが異なる場合の扱いに苦戦した。長さが同じ場合と変え
ずに出力すると、足りない分の要素を0が補ってしまうため題意に沿わ
ない列となった。スライドの例題を参考にすることで解決したが、そこ
の箇所は自分の力のみで書きたかった。今回は上記のような形で作成し
たが、問題文には「長さが異なる場合の扱いは好きに決めていいと」あ
るので、ほかにどのようなパターンがあるのか見てみたい。

アンケート
Q1.初期化を忘れずにすること。
Q2.プログラムの意味を読み取る力。
Q3.ポインタはしっかりと復習しておきたい。

m1910615 2b:○ Mon May 25 23:55:52 2020


第02回bレポート

学籍番号:1910615
提出日時 2020/05/25
ペア:個人作業

[課題1] 
・演習1c
    c. 何らかの整列アルゴリズムで配列を昇順に整列する関数 void
    iarray sort(int *a, int n) を作成する。
    int a[] = {8,5,2,4,1}, b[] = {1,2,4,5,8};
    iarray_sort(a, 5); expect_iarray(a, b, 5, "85241 -> 12458");

・コード

#include <stdio.h>
#include <stdbool.h>

bool iarray_equal(int *a, int *b, int n);
void iarray_sort(int *a, int n);
void expect_iarray(int *a, int *b, int n, char *msg);
void iarray_print(int *a, int n);


int main(void){
    int a[] = {8,5,2,4,1}, b[] = {1,2,4,5,8};
    iarray_sort( a, 5);expect_iarray(a,b,5,"85241 -> 12458");
    //追加
    int c[] = {5,1,3,2,9,8,6,5}, d[] = {1,2,3,5,5,6,8,9};
    iarray_sort( c, 8);expect_iarray(c,d,8,"51329865 -> 12355689");
    return 0;
}

void iarray_sort(int *a, int n){
    int i,j,kp;
    for(i = 0; i < n; ++i){
        for(j= i+1; j < n; ++j){
            if(*(a + i) > *(a + j)){
                kp = *(a + i);
                *(a + i) = *(a + j);
                *(a + j) = kp;
            }
        }
    }
}

bool iarray_equal(int *a, int *b, int n) {
    int i;
    for(i = 0; i < n; ++i) {
        if(a[i] != b[i]) { return false; }
    }
    return true;
}


void expect_iarray(int *a, int *b, int n, char *msg) {
    printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
    iarray_print(a, n); iarray_print(b, n);
}

void iarray_print(int *a, int n) {
    int i;
    for(i = 0; i < n; ++i) { printf(" %2d", a[i]); }
    printf("\n");
}

・単体テスト
  int a[] = {8,5,2,4,1}, b[] = {1,2,4,5,8};
    iarray_sort( a, 5);expect_iarray(a,b,5,"85241 -> 12458");
    //追加
    int c[] = {5,1,3,2,9,8,6,5}, d[] = {1,2,3,5,5,6,8,9};
    iarray_sort( c, 8);expect_iarray(c,d,8,"51329865 -> 12355689");
    return 0;

・実行結果
[m1910615@sol ~]$ ./a.out
OK 85241 -> 12458
  1  2  4  5  8
  1  2  4  5  8
OK 51329865 -> 12355689
  1  2  3  5  5  6  8  9
  1  2  3  5  5  6  8  9


・説明

単体テストは上記のとおりである。iarray_sort内では、まず全体の要
素から一番小さい数を比較して、a[0]に置いた。その後a[1]以降も同様
に行い、a[n]まで並べ替えた。並べ替える際には、移される側のデータ
が消えないよう、移される側を一旦kpに保存して移し替えた。例のテス
トケースには同じ数字が二個以上含まれた配列が無かったのでそのよう
なテストケースを追加した。

・考察

テストでは、要素に二桁の数字を入れて行ったモノもあったが、expext
関数の*msgの都合上、見づらくなるため掲載しなかった。今回は全要素
比較していく方法を用いたが、基礎プログラミング及び演習のときのよ
うに他の方法の並べ替えも同様にできるはずであるので、時間があった
ら挑戦したい。


[課題2]
・演習2a
   a. 2 つの列を受け取り、両方の列の内容を交互に並べた列を返す
  (例: 「1, 2, 3」「4, 5, 6」→「1, 4, 2, 5, 3, 6」。
  長さが異なる場合の扱いは好きに決めてよい)

・コード

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

void iarray_read(int *a, int n) {
    int i;
    for(i = 0; i < n; ++i) {
        printf("%d> ", i+1); scanf("%d", a+i);
    }
}

void iarray_print(int *a, int n) {
    int i;
    for(i = 0; i < n; ++i) { printf(" %2d", a[i]); }
    printf("\n");
}

int *ivec_new(int size) {
    int *a = (int*)malloc((size+1) * sizeof(int));
    a[0] = size; return a;
}


void ivec_read(int *a) { iarray_read(a+1, a[0]); }


void ivec_print(int *a) { iarray_print(a+1, a[0]); }


int *ivec_kougo(int *a, int *b) {
    int *c = ivec_new(a[0]+b[0]);
    int i;
    if(a[0] >= b[0]){
        for(i = 1; i <= b[0] ; ++i){
            c[2*i - 1] = a[i];
            c[2*i] = b[i];
        }
        for(i = 1; i <= a[0]-b[0] ; ++i){
            c[2*b[0] + i] = a[b[0] + i];
        }
    }else{
        for( i = 1; i <= a[0] ; ++i){
            c[2*i - 1] = a[i];
            c[2*i] = b[i];
        }
        for(i = 1; i <= b[0]-a[0] ; ++i){
            c[2*a[0] + i] = b[a[0] + i];
        }
    }
    return c;
}


bool iarray_equal(int *a, int *b, int n) {
    int i;
    for(i = 0; i < n; ++i) {
        if(a[i] != b[i]) { return false; }
    }
    return true;
}



void expect_iarray(int *a, int *b, int n, char *msg) {
    printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
    iarray_print(a, n); iarray_print(b, n);
}



int main(void) {
    int a[] = {6,1,2,3,4,5,6}, b[]={7,2,3,4,5,6,7,8};
    int c[] = {13,1,2,2,3,3,4,4,5,5,6,6,7,8};
    int *p = ivec_kougo(a, b);
    expect_iarray(p,c,14, "[1,2,3,4,5,6]&[2,3,4,5,6,7,8] = [1,2,2,3,3,4,4,5,5,6,6,7,8]");
    return 0;
}



・単体テスト
int main(void) {
    int a[] = {6,1,2,3,4,5,6}, b[]={7,2,3,4,5,6,7,8};
    int c[] = {13,1,2,2,3,3,4,4,5,5,6,6,7,8};
    int *p = ivec_kougo(a, b);
    expect_iarray(p,c,14, "[1,2,3,4,5,6]&[2,3,4,5,6,7,8] = [1,2,2,3,3,4,4,5,5,6,6,7,8]");
    return 0;
}

・実行結果
[m1910615@sol ~]$ ./a.out
OK [1,2,3,4,5,6]&[2,3,4,5,6,7,8] = [1,2,2,3,3,4,4,5,5,6,6,7,8]
 13  1  2  2  3  3  4  4  5  5  6  6  7  8
 13  1  2  2  3  3  4  4  5  5  6  6  7  8

・説明
まず、a[]とb[]の配列の長さを比較し、aのほうが長ければ交互に配列
させた後、aの残りを配列に入れるようにした。(b[]が大きかった場合
はb)交互の順番は、関数に入れた順になっている。for文でiが小さいほ
うの配列の長さになるまでループさせ、Cの配列の、iの二倍から1引い
た値の位置にa(先)二倍の位置にb(後)で入るようになっている。テスト
ケースはテキストの例を改変させて完成させた。



・考察

配列の0の値に長さが入ってくれるので、より直感的に配列を扱えた。
今回の講義分のプログラムではiarrayprintが%2dとなっており、見やす
く実行するならば
一桁の正整数のみに限られているが、malloc内のsizeof(int)の変更、
%2dを%8fなどへ変更などすれば、正負でも浮動小数でも対応できると考
える。
今回は手打ちでもプログラムを実行したので結果を示しておく。
 手打ちしたときの実行結果は以下
 [m1910615@sol ~]$ ./a.out
1> 0
2> 0
3> 0
4> 0
5> 0
6> 0
7> 0
1> 1
2> 1
3> 1
4> 1
5> 1
6> 1
7> 1
8> 1
9> 1
  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  1

[アンケート]
 Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。
scanfでもらったアドレスをそのまま使用して関数内で操作したあと、
もらった元の値と比較できない(変更されてしまっているので)から、異
なるアドレスでもう一度もらう
必要がある等。
   
 Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に付いていないと思うこ
とは何ですか。
  型のビット数の理解
  
 Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
  次回も頑張ります。

m1910616 2b:○ Mon May 25 23:41:27 2020


プログラミング通論’20 #2 – アドレスとポインタ
課題2b

学籍番号:1910616
ペアの学籍番号:1910525
提出日時:2020/5/25

・演習2a
【課題の再掲】
 2つの配列を受け取って、両方の列の内容を交互に並べた新しい列を返す。

【コード】

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}

void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}

void ivec_read(int *a) { iarray_read(a+1, a[0]); }

void ivec_print(int *a) { iarray_print(a+1, a[0]); }

bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i <= n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}


void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}


int *ivec_kougo(int *a, int *b){
  int *c = ivec_new(a[0]+b[0]);
  for(int i = 1; i <= c[0]; i+=2){ c[i] = a[(i+1) / 2]; c[i+1] = b[(i+1) / 2]; }
  return c;
}

int main(void) {
  int a[]={3,1,2,3},b[]={3,4,5,6},c[]={6, 1, 4, 2, 5, 3, 6}; 
  int*p=ivec_kougo(a,b); 
  expect_iarray(p,c,6,"[1,2,3]+[4,5,6}]=[ 1, 4, 2, 5, 3, 6]"); 

  int d[]={5,8,2,5,3,2},e[]={5,1,2,3,4,5},f[]={10,8,1,2,2,5,3,3,4,2,5}; 
  int*q=ivec_kougo(d,e); 
  expect_iarray(q,f,10,"[8,2,5,3,2]+[1,2,3,4,5}]=[8,1,2,2,5,3,3,4,2,5]"); 

  int g[]={7,1,2,3,4,5,6,7},h[]={7,8,9,10,11,12,13,14},i[]={14,1,8,2,9,3,10,4,11,5,12,6,13,7,14}; 
  int*r=ivec_kougo(g,h); 
  expect_iarray(r,i,14,"[1,2,3,4,5,6,7]+[8,9,10,11,12,13,14}]=[ 1,8,2,9,3,10,4,11,5,12,6,13,7,14]"); 

  return 0;
}

【実行例】

OK [1,2,3]+[4,5,6}]=[ 1, 4, 2, 5, 3, 6]
  6  1  4  2  5  3
  6  1  4  2  5  3
OK [8,2,5,3,2]+[1,2,3,4,5}]=[8,1,2,2,5,3,3,4,2,5]
 10  8  1  2  2  5  3  3  4  2
 10  8  1  2  2  5  3  3  4  2
OK [1,2,3,4,5,6,7]+[8,9,10,11,12,13,14}]=[ 1,8,2,9,3,10,4,11,5,12,6,13,7,14]
 14  1  8  2  9  3 10  4 11  5 12  6 13  7
 14  1  8  2  9  3 10  4 11  5 12  6 13  7

【説明】
引数として二つの配列を受け、それらの要素を交互に並べる関数
ivec_kougoを作成した。関数では、まずそれぞれの配列の先頭に入って
いる項数を足し合わせて、その値を先頭に持つ配列c[]を作成する。そ
れから、その値の分だけ繰り返し行うforループでは、iを2ずつ進め、c
に第一引数である配列aの値を入れてから第二因数である配列bの値を入
れることを実行する。

【考察】
この関数ivec_kougoでは、必ず第一引数の先頭の項から値が入れられて
いく。第三引数にブール代数を入れられるようにし、TrueFalseで第一
引数か第二引数のどちらかを先頭に持っていくか決められるようにもで
きた。しかし、こうすると扱う引数が多くなって綺麗でないため、単に
第一の引数を先頭に持っていくだけで良いと言える。また、今回は同じ
項数の配列しか扱えず、仮に異なる項数の配列を入れた場合には予期せ
ぬ値のある配列がかえされると見込まれる。それゆえ、同じ項数でない
ふたつの配列が入れられた場合にif文でerrorを出すようにするか、異
なる項数のものを扱えるようにする必要があったと言える。後者の場合、
引数の配列におけるふたつの先頭の値のうち、小さい方を超過した値に
forループのiがなったときに一方の配列の値のみを配列cに入れていけ
ば良いと考える。


・演習3a
【課題の再掲】
等差数列を初項に戻す機能cdseq_resetをcdseq_demo.cに追加する。

【コード】

// cdseq.h --- constant difference sequence API.
struct cdseq *cdseq_new(int s, int d);
int cdseq_get(struct cdseq *r);
void cdseq_free(struct cdseq *r);
void cdseq_reset(struct cdseq *r);


// cdseq.c -- cdseq implementation.
#include <stdlib.h>
#include "cdseq.h"

struct cdseq {int ft, value, diff; } ;

struct cdseq *cdseq_new(int s, int d) {
  struct cdseq *r = (struct cdseq*)malloc(sizeof(struct cdseq));
  r->ft = s; r->value = s; r->diff = d; return r;
}

int cdseq_get(struct cdseq *r) {
  int v = r->value; r->value += r->diff; return v;
}

void cdseq_free(struct cdseq *r) {
  free(r);
}

void cdseq_reset(struct cdseq *r){
  r->value = r->ft;
}


// cdseq_demo.c -- cdseq demonstration.
#include <stdio.h>
#include "cdseq.h"
int main(void) {
  struct cdseq *s1 = cdseq_new(1, 2);
  struct cdseq *s2 = cdseq_new(0, 3);
  int i;
  for(i = 0; i < 13; ++i) {
    printf(" %2d", cdseq_get(s1));
    printf(" %2d", cdseq_get(s1));
    printf(" %2d", cdseq_get(s2));
    printf("\n");
    if (i == 7){
      cdseq_reset(s1);
      cdseq_reset(s2);
    }
  }
  cdseq_free(s1); cdseq_free(s2);
  return 0;
}


【実行例】

  1  3  0 
  5  7  3
  9 11  6
 13 15  9
 17 19 12
 21 23 15
 25 27 18
 29 31 21
  1  3  0 
  5  7  3
  9 11  6
 13 15  9
 17 19 12

【説明】
引数に入れた値を初項に戻す関数cdseq_resetを作成した。構造体cdseq
にint型のftを加え、cdseqを作成した際には初項をftに入れられる。関
数cdseq_resetを実行した際にはそれを現在の項に呼び戻すことで初項
へリセットすることが可能である。

【考察】
構造体作成時に初項をftに入れて疑似的に定数のように扱う方法をとっ
たが、ここに入れる値はvalueに入れる値と同じである。それゆえ、何
かしらの方法をとって作成時にftに値を入れないようにする方法を考え
たが、見付けられなかった。ポインタ型を考え、初項は&valueの示す先
に、第二項以降は(&value + 1)の示す先に値を入れ込むようにすること
で、構造体を作成する際にftを用いずにできる方法も考えたが、これが
適切かどうかは私の知識では判断できなかった。

【アンケート】
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。
 A. 参照先かアドレスの値かで間違えないようにする点に注意すべきだと思いました。

Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に
付いていないと思うことは何ですか。
 A. 見本のコードや他人のコードを読む際にかなりの気力が必要なの
ですが、これをもう少しすらすら読むことを身に付けたいと思いました。

Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
 A. アドレスやポインタの扱い方を以前より掴めてきた気がします。

m1910619 2b:○ Mon May 25 11:50:53 2020


プログラム通論 #02 課題2b

[課題の再掲1]
配列の並び順を逆順にする関数void iarray_revese(int *a, int n)を作成する。

[実施したこととその結果1]
作成したプログラム
//与えられた配列を逆順に入れ替えるプロフラム                                    

#include <stdio.h>
#include <stdbool.h>

void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}

void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

int iarray_reverse(int *a, int n){
    for(int i=0;i<n/2;i++){
      int t =a[i];
      a[i]=a[n-i-1];
      a[n-i-1]= t;
    }
    return *a;
}

bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}

int main(void){
   int a[] = {8,5,2,4,1}, b[] = {1,4,2,5,8};
   iarray_reverse(a, 5); expect_iarray(a, b, 5, "85241 -> 14258");

   //自分で作成したテストケース                                                 
   int c[] ={1,2,3,4,5,6,7,8,9,0}, d[]= {0,9,8,7,6,5,4,3,2,1};
   iarray_reverse(c, 10); expect_iarray(c, d, 10, "1234567890 -> 0987654321");
   int e[] ={5,4,7,9,8}, f[]= {8,9,7,4,5};
   iarray_reverse(e, 5); expect_iarray(e, f, 5, "54798 -> 89745");
   int g[] ={8,2,4}, h[]= {4,2,8};
   iarray_reverse(g, 3); expect_iarray(g, h, 3, "824 -> 428");
   
   return 0;

}


結果
[m1910619@sol ~/pro2]$ gcc8 d.c
[m1910619@sol ~/pro2]$ ./a.out
OK 85241 -> 14258
  1  4  2  5  8
  1  4  2  5  8
OK 1234567890 -> 0987654321
  0  9  8  7  6  5  4  3  2  1
  0  9  8  7  6  5  4  3  2  1
OK 54798 -> 89745
  8  9  7  4  5
  8  9  7  4  5
OK 824 -> 428
  4  2  8
  4  2  8


[プログラムの説明1]
配列の順序を入れ替えるプログラムを作成した.方法としては,配列の
始めの要素に配列の最後の要素を代入し,それをforを使い,要素の番
号をずらしながら反復させた.また,関数iarray_equalと関数
expect_iarrayは自分で作成したプログラムとあらかじめ用意していた
テストケースの結果が一致するかどうかを判断するプログラムである.

[考察1]
自ら全てのプログラムの間違いを正したり,バグを見つけるのは難しい
ことだと思う.そこで役立つのがテストケースであると感じた.単体テ
ストでたまたまプログラムがうまく作動したからといって,全ての値に
うまく機能するとは限らない.だが,網羅的にあらゆる場合をテストケー
スで試すことで,バグを浮き上がらせることができると感じた.そのテ
ストケースをプログラムして半自動的にできるようにすればもっと良い
プログラムができると感じた.

[課題の再掲2]
2つの列を受け取り、両方の列の内容を交互に並べた列を返すプログラム

[実施したこととその結果2]
作成したプログラム
//交互に配列を表示するプログラム                                                

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}

int *cross(int *a, int *b){
  int *c = ivec_new(sizeof(a)+sizeof(b));
  for(int i = 0;i < sizeof(c); i++) {
    c[2*i] = a[i];
    c[2*i+1] = b[i];
  }
  return c;
}

bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}

void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", (a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}

int main(void) {
  int a[] = {1,2,3}, b[] = {4,5,6},c[]={1,4,2,5,3,6};
  int *p = cross(a, b);
  expect_iarray(p, c, 6, "[1,2,3]+[4,5,6]=[1,4,2,5,3,6]");
  int d[] = {4,4,7,2}, e[] = {8,5,3,8},f[]={4,8,4,5,7,3,2,8};
  int *q = cross(d, e);
  expect_iarray(q, f, 8, "[4,4,7,2]+[8,5,3,8]=[4,8,4,5,7,3,2,8]");
  return 0;
}


結果
[m1910619@sol ~/pro2]$ gcc8 e.c
[m1910619@sol ~/pro2]$ ./a.out
OK [1,2,3]+[4,5,6]=[1,4,2,5,3,6]
  1  4  2  5  3  6
  1  4  2  5  3  6
OK [4,4,7,2]+[8,5,3,8]=[4,8,4,5,7,3,2,8]
  4  8  4  5  7  3  2  8
  4  8  4  5  7  3  2  8

[プログラムの説明2]
要素数が同じ配列2つについて,両方の列の内容を交互に並べた列を返
すプログラムを作成した.自分で作成したプログラムは主に関数cross
で他の部分については,教科書を参考にしながら作成した.まず,2つ
の配列の要素の数を足し合わせ,大きさを決めて,偶数番目と奇数番目
にそれぞれの配列の要素を入れていった.

[考察2]
配列の大きさを先に決めてしまうことが大切に感じた.そうすることで,
仮に大きさの違う配列2つでこのプログラムを動かしても,エラーが起
きず,足りないところには0が表示されるんではないかと感じている.
想定外の入力の際に,エラーが起きるか形式上結果が返ってくるかを考
えると,やはりエラーが起きた方がプログラムを修正しやすいと感じた.

[アンケート]
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。
ポインタを使うと間接になり,どこのポインタがどこに繋がっているか
をしっかり把握することが必要だと感じました.

Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に
付いていないと思うことは何ですか。
相手が求める要求に,不足なしに全て満たすプログラムを作成する注意
深さだと思います.

Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
まだまだ実力不足なので,努力していきたいです.

m1910620 2b:○ Mon May 25 23:26:08 2020


y revese(int *a, int n) を 作成する。
[プログラミング]
// reverse                                                                                       
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
void iarray_reverse(int *a, int n){
    for(int i=0;i<n/2;++i){
      int b=a[i];
    a[i]=a[n-1-i];
    a[n-1-i]=b;
    }
}

bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}

void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}

int main(void) {
  int a[] = {8,5,2,4,1}, b[] = {1,4,2,5,8}, c[] = {4,6,8}, d[]= {8,6,4}, e[] = {1,6,3,5,8,6,4}, \
f[] = {4,6,8,5,3,6,1};
   iarray_reverse(a,5);expect_iarray(a,b,5,"85241->14258");
   iarray_reverse(c,3);expect_iarray(c,d,3,"468->864");
   iarray_reverse(e,7);expect_iarray(e,f,7,"1635864->4685361");
  return 0;
}


OK 85241->14258
  1  4  2  5  8
  1  4  2  5  8
OK 468->864
  8  6  4
  8  6  4
OK 1635864->4685361
  4  6  8  5  3  6  1
  4  6  8  5  3  6  1

[説明]
iarray_reverseで配列の順番を逆にするプログラミングを作った。初め
の要素に配列の最後の要素を代入し、for文を用いそれらを配列の最後
まで繰り返した。またその他の関数を用い、予期していたテストケース
と一致するかどうか判断し、出力させた。

[考察]
テストケースによって自分の作成したプログラミングのミスがわかり、
デバッグが簡単になり、手間かけるが必要なことだと理解できた。今回
は配列の先頭から末尾を入れ替えるようなプログラミングを利用したの
で、for文の条件をつけるとき、n回全て繰り返さなくてよく、nの半分
だけでよかった。

2つ目の課題
 2つの列を受け取り、両方の列の内容を交互に並べた列を返す。
[プログラミング]
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
int *ivec_cross(int *a, int *b) {
  int *c = ivec_new(a[0]+b[0]);
    int x;
    if (a[0]>b[0]){x = a[0];}
    else{x = b[0];}
  for(int i = 1; i <= x; ++i) { c[2*i-1] = a[i]; }
  for(int i = 1; i <= x; ++i) { c[2*i] = b[i]; }
    if(a[0]>b[0]){for(int i = 1; i <= a[0]+b[0]; ++i) { c[2*x+i] = a[x+i]; };}
    else{ for(int i = 1; i <= a[0]+b[0]; ++i) { c[2*x+i] = b[x+i]; };}
    return c;
}

bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
  int a[] = {4,1,3,5,7}, b[] = {2,2,4}, c[] = {6,1,2,3,4,5,7};
  int d[] = {3,1,2,3}, e[] = {8,1,2,3,4,5,6,7}, f[] = {11,1,1,2,2,3,3,4,5,6,7};
  int *p = ivec_cross(a, b);
  expect_iarray(p, c, 7, "[1,3,5,7]+[2,4]=[1,2,3,4,5,7]");
  int *q = ivec_cross(d, e);
    expect_iarray(q, f, 11, "[1,2,3]+[1,2,3,4,5,6,7]=[1,1,2,2,3,3,4,4,5,6,7]");
  return 0;
}

int main(void) {
  int a[] = {4,1,3,5,7}, b[] = {2,2,4}, c[] = {6,1,2,3,4,5,7};
  int d[] = {3,1,2,3}, e[] = {7,1,2,3,4,5,6,7}, f[] = {10,1,1,2,2,3,3,4,5,6,7};
  int *p = ivec_cross(a, b);
  expect_iarray(p, c, 7, "[1,3,5,7]+[2,4]=[1,2,3,4,5,7]");
  int *q = ivec_cross(d, e);
    expect_iarray(q, f, 11, "[1,2,3]+[1,2,3,4,5,6,7]=[1,1,2,2,3,3,4,4,5,6,7]");
  return 0;
}

OK [1,3,5,7]+[2,4]=[1,2,3,4,5,7]
  6  1  2  3  4  5  7
  6  1  2  3  4  5  7
OK [1,2,3]+[1,2,3,4,5,6,7]=[1,1,2,2,3,3,4,4,5,6,7]
 11  1  1  2  2  3  3  4  5  6  7
 11  1  1  2  2  3  3  4  5  6  7

[説明]
配列a,bを用意し配列aは奇数番号目に配列bは偶数番号目にそれぞれの
要素を入れ要素が交互に現れるようにした。長さが異なる場合は後ろに
付け足す形にした。

[考察]
場合わけでfor文を用いたがどこがどのような場合なのか頭だけで考え
ると頭が混乱するためかみを用い整理しながらプログラムすることでミ
スを減らし、丁寧なコードが書けると思った。
Mallocを用い、可変長配列が扱えるようになると視覚的にプログラミン
グを扱いやすいのかなと感じた。

[アンケート]
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。
どこがどこにつながっているか把握することが大事だと思った。
 Q2. ここまでのところで、プログラムを作るときに重要だが自分で身
 に付いていないと思うことは何ですか。
 要領よく簡潔にプログラミングをまとめること
Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
難しいです。

m1910626 2b:○ Mon May 25 11:28:25 2020


1910626
個人作業
5/25(月)
<演習1d>
2つの配列(長さは同じ)を受け取り、2番目の各要素を1番目の配列
の各要素に足し込む関数void iarray_add(int *a, int *b, int n)を作
成する。
[コード]
#include <stdio.h>
#include <stdbool.h>
void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

void iarray_add(int *a, int *b, int n){
    
    for(int i = 0; i < n; ++i){
        a[i]=a[i]+b[i];
    }
    
}


void expect_int(int i1, int i2, char *msg) {
printf("%s %d:%d %s\n", (i1==i2)?"OK":"NG", i1, i2, msg);
}


bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}


int main(void) {
    int a[] = {8,5,2,4,1}, b[] = {1,1,2,2,3}, c[] =
    {9,6,4,6,4},d[] = {-3,0,1,20,7,9}, e[] = {4,50,2,-27,0,8},
    f[] = {1,50,3,-7,7,17};
    iarray_add(a, b, 5);
    expect_iarray(a, c, 5, "85241+11223 -> 96464");
    iarray_add(d, e, 6);
    expect_iarray(d, f, 6, "-3012079+4502-2708 -> 1503-7717");
    return 0;
}

[実行結果]
mizukoshikairinoMacBook-Pro:c mizukoshikairi$ ./a.out
OK 85241+11223 -> 96464
  9  6  4  6  4
  9  6  4  6  4
OK -3012079+4502-2708 -> 1503-7717
  1 50  3 -7  7 17
  1 50  3 -7  7 17

[void iarray_addの説明]
a,b2つの配列の各要素を、初期値0の変数iによる要素番号の指定で足
し合わせる。for内でi番目の要素を順に足していき、結果としてaの配
列が、最初の配列aと配列bの各要素の和を要素に持つ配列へと書き換わ
る。

[考察]
至ってシンプルに作成することができたと思う。新しくcという要素数
n,各要素が0の配列を用意し、配列aとbの各要素の和を代入していくと
いう方法もできたが、別段元々の配列aが必要というわけでもなかった
のでコードを短くするために配列aを書き換える方法をとった。テスト
ケースでは、要素数を6にし、0や2桁の数や負の数を積極的に織り交
ぜたが、問題なかった。

<演習2a>
2つの列を受け取り、両方の列の内容を交互に並べた列を返す。
[コード]
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
int *ivec_kougo(int *a, int *b) {
  int *c = ivec_new(a[0]+b[0]);
    int x;
    if (a[0]<b[0]){x = a[0];}
    else{x = b[0];}
  for(int i = 1; i <= x; ++i) { c[2*i-1] = a[i]; }
  for(int i = 1; i <= x; ++i) { c[2*i] = b[i]; }
    if(a[0]>b[0]){for(int i = 1; i <= a[0]+b[0]; ++i) { c[2*x+i] = a[x+i]; };}
    else{ for(int i = 1; i <= a[0]+b[0]; ++i) { c[2*x+i] = b[x+i]; };}
    return c;
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
  int a[] = {4,1,2,3,4}, b[] = {2,4,5}, c[] = {6,1,4,2,5,3,4};
  int d[] = {3,-1,-2,-3}, e[] = {8,1,2,3,4,5,6,7,8}, f[] = {11,-1,1,-2,2,-3,3,4,5,6,7,8};
  int *p = ivec_kougo(a, b);
  expect_iarray(p, c, 7, "[1,2,3,4]+[4,5]=[1,4,2,5,3,4]");
  int *q = ivec_kougo(d, e);
    expect_iarray(q, f, 12, "[-1,-2,-3]+[1,2,3,4,5,6,7,8]=[-1,1,-2,2,-3,3,4,5,6,7,8]");
  return 0;
}

[実行結果]
mizukoshikairinoMacBook-Pro:c mizukoshikairi$ ./a.out
OK [1,2,3,4]+[4,5]=[1,4,2,5,3,4]
  6  1  4  2  5  3  4
  6  1  4  2  5  3  4
OK [-1,-2,-3]+[1,2,3,4,5,6,7,8]=[-1,1,-2,2,-3,3,4,5,6,7,8]
 11 -1  1 -2  2 -3  3  4  5  6  7  8
 11 -1  1 -2  2 -3  3  4  5  6  7  8

[*ivec kougoの説明]
まず、要素数を表すaとbの各先頭の数字を足した要素数の配列cを作り、
変数xに配列a,bのうち小さい方の要素数を格納する。その後、配列aはc
の1から2x-1までの奇数番目に、配列bはcの2から2xまでの偶数番目
に各要素がそれぞれ入るようにすることで、要素が交互に入るようにし
た。aとbの要素数(長さ)が異なる時は、cの2x+1番目以降に付け足す
形にした。

[考察]
テストケースを初めて実行する時、結果となる配列cの要素数の指定を
a[0]+b[0]と同じものだと勘違いしていた。そうすると、NGとはならな
いものの、配列aとbをつなげた配列cの長さよりも1小さいため、本来現
れる最後の要素が省略されてしまった。当たり前のことだが可変長配列
cの先頭にも長さを告げる要素が1つあるためcの長さはa[0]+b[0]+1とな
ることに気を付けたい。

<アンケート>
A1.変数と各アドレスの中身を整理しておくこと。
A2.テストケースから問題点と原因をスムーズに発見すること。
A3.テストケースを試してもどこに問題があるのかがよくわからず時間
がかかってしまった。

m1910627 2b:○ Mon May 25 23:11:57 2020


学籍番号 1910627
個人作業
提出日時 5月25日

課題一つ目
演習1-b
配列の並び順を逆順にする関数 void iarray_revese(int *a, int n) を作成する

作ったプログラム
#include <stdio.h>
#include <stdbool.h>

void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

void iarray_reverse(int *a, int n){
  int m = n/2;
  for(int i = 0; i < m ;i++ ){
    int p = a[i];
    a[i] = a[n-1-i];
    a[n-1-i] = p;
  }
  
  for(int j = 0;  j < n; j++){
    printf("%d",a[j]);
  }

  printf("\n");
}


bool iarray_equal(int *a, int *b, int n) {
for(int i = 0; i < n; ++i) {
if(a[i] != b[i]) { return false; }
}
return true;
}

void expect_iarray(int *a, int *b, int n, char *msg) {
printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
iarray_print(a, n); iarray_print(b, n);
}


int main(void){
  int a[] = {8,5,2,4,1}, b[] = {1,4,2,5,8};
  iarray_reverse(a, 5);
  expect_iarray(a, b, 5, "85241 -> 14258");

  int c[] = {1,2,3,4,5,6,7}, d[] = {7,6,5,4,3,2,1};
  iarray_reverse(c, 7);
  expect_iarray(c,d, 7, " 1234567 -> 7654321");


  int e[] = {1}, f[] = {1};
  iarray_reverse(e, 1);
  expect_iarray(e,f, 1, "1 -> 1");


  return 0;
}

単体テストと実行例
m1910627@DESKTOP-QOKSOM0:/mnt/c/Users/みずたかずし/Documents/prog/02$ gcc 1b.c  
m1910627@DESKTOP-QOKSOM0:/mnt/c/Users/みずたかずし/Documents/prog/02$ ./a.out
14258
OK 85241 -> 14258
  1  4  2  5  8
  1  4  2  5  8
7654321
OK  1234567 -> 7654321
  7  6  5  4  3  2  1
  7  6  5  4  3  2  1
1
OK 1 -> 1
  1
  1

  説明

配列を逆順にした後と前の配列は真ん中軸に対称なので、まず要素数の
半分の値(mとした)を求めて、iを0からmまで、a[i]とa[n-1-i]を交換す
ることで元の配列の逆順の配列が得られる。

  考察

この問題を考えているときに最初要素数が偶数のときと奇数のときとで
場合分けがいるかと思ったが整数型で奇数を2で割った時の商は小数点
以下切り下げということを思い出して一つの繰り返し文ですますことが
できた。
小数点以下切り捨てになっていることは切り上げより利点があるという
考えの元での設定なのかと考えた。

  二つ目の課題
  演習1-c
  何らかの整列アルゴリズムで配列を昇順に整列する関数 void iarray
  sort(int *a, intn) を作成する。
 

  作ったプログラム

  #include <stdio.h>
#include <stdbool.h>

void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

void  iarray_sort(int *a, int n){
    int i,j;

    for(i = 0; i < n; i++){
        
        int min = a[i];
        // 要素a[i]~a[n-1]までのなかから最小の値をa[i]にし、最小値があったところにa[i]を代入
        for(j = i; j < n; j++){
            if(min > a[j]){
                int p = min;
                min = a[j];
                a[j] = p;
            }

         }
    a[i] = min;
    }
    for(int i = 0; i < n; i++){
        printf("%d", a[i]);
    }
    printf("\n");
} 


bool iarray_equal(int *a, int *b, int n) {
    for(int i = 0; i < n; ++i) {    
    if(a[i] != b[i]) { return false; }
}
return true;
}

void expect_iarray(int *a, int *b, int n, char *msg) {
    printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
    iarray_print(a, n); iarray_print(b, n);
}





int main(void){

    int a[] = {8,5,2,4,1}, b[] = {1,2,4,5,8};
    iarray_sort(a, 5); expect_iarray(a, b, 5, "85241 -> 12458");

    int c[] = {5,3,8,9,4,1,6},d[] = {1,3,4,5,6,8,9};
    iarray_sort(c, 7); expect_iarray(c, d, 7, "5389416 -> 1345689");





    return 0;
}

実行例

m1910627@DESKTOP-QOKSOM0:/mnt/c/Users/みずたかずし/Documents/prog/02$ gcc 1c.c  
m1910627@DESKTOP-QOKSOM0:/mnt/c/Users/みずたかずし/Documents/prog/02$ ./a.out
12458
OK 85241 -> 12458
  1  2  4  5  8
  1  2  4  5  8
1345689
OK 5389416 -> 1345689
  1  3  4  5  6  8  9
  1  3  4  5  6  8  9


  説明

小さい値を左に詰めていくので、まずa[i]を固定し、a[i]~a[n-1]まで
の中で最小の値があるところとa[i]を入れ替えて、iを+1する。
これによりiがn-1までいくことで昇順の配列が出来上がっている。

 考察

プログラム組んでいる途中で//を利用して今やっていることをコメント
することが、繰り返し文中の繰り返しなどちょっと複雑なことをしよう
とするときに役にたつとわかった。
テストケースを行うことで複数の実行結果を同時に試せ、また目に見え
る形で残るのは作ってる側としてはありがたいものだと思った。

アンケート
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。

*を使う場所

Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に
付いていないと思うことは何ですか。

一応形をなしているアルゴリズムを作る力、またそのアルゴリズムを作っ
たときそれをプログラミングする力、精神的な体力

Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
難しくなってきた

m1910629 2b:○ Sun May 24 05:11:24 2020


1910629
個人作業,5/19

演習2a:交互に配列
--以下コード
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
int *ivec_merge(int *a, int *b){
  int min = a[0]<b[0]? a[0] : b[0];
  int *c = ivec_new(2*min);
  for(int i=1;i<=min;i++){
    c[2*i - 1] = a[i];
    c[2*i] = b[i];
  }
  return c;
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", (a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
  int a[] = {4,3,1,2,3}, b[] = {3,2,4,5}, c[] = {6,3,2,1,4,2,5}, d[] = {3,0,5,5}, e[] = {6,2,0,4,5,5,5};
  int *p = ivec_merge(a, b);
  int *q = ivec_merge(b, d);
  expect_iarray(p, c, 7, "[3,1,2,3]+[2,4,5]=[3,2,1,3,2,5]");
  expect_iarray(q, e, 7, "[2,4,5]+[0,5,5]=[2,0,4,5,5,5]");
  return 0;
}

実行例:
OK [3,1,2,3]+[2,4,5]=[3,2,1,3,2,5]
  6  3  2  1  4  2  5
  6  3  2  1  4  2  5
OK [2,4,5]+[0,5,5]=[2,0,4,5,5,5]
  6  2  0  4  5  5  5
  6  2  0  4  5  5  5

説明:基本的なコード(ivec_new,,iarray_print,expect_iarray)はサン
プルから必要な所を抜き出してきた.
ivec_mergeは2つの配列を受け取り,それらの要素を交互に並べた配列
を返す関数.配列の長さが違う場合,短い方の長さに合わせる.
最初にminに短い方の配列の要素数を入れ,次の行で要素数が短い方の2
倍の空の配列を作る.for文内で交互に空配列に入れ,最後にそれを返
している.
main関数では2つのテストケースでテストしている.

考察:三項演算子を使ったりfor文を集約してコードを見やすくできた.
配列のテストケースについては,最低でも要素数が同じ場合と違う場合
を用意すると良いと分かった.また,要素数が1の場合もテストすると
さらに正確になると思った.

演習1e:関数ポインタ
--以下コード
#include <stdio.h>

int iadd(int x, int y){ return x + y; }
int imax(int x, int y){ return (x > y) ? x : y;}

int iarray_inject(int *a, int n, int (*fp)(int, int)){
    int num = a[0];
    for(int i=1;i<n;i++){
        num = fp(num,a[i]);
    }
    return num;
}

void expect_int(int i1, int i2, char *msg) {
  printf("%s %d:%d %s\n", (i1==i2)?"OK":"NG", i1, i2, msg);
}

int main(void){
    int a[] = {8,5,2,4,1}, b[] = {0,-4,6,3,-1};
    expect_int(iarray_inject(a, 5, iadd), 20, "8+5+2+4+1");
    expect_int(iarray_inject(a, 5, imax), 8, "max(8,5,2,4,1)");
    expect_int(iarray_inject(b, 5, iadd), 4, "0-4+6+3-1");
    expect_int(iarray_inject(b, 5, imax), 6, "max(0,-4,6,3,-1)");
    expect_int(iarray_inject(b+1, 1, iadd), -4, "-4");
    expect_int(iarray_inject(b+1, 1, iadd), -4, "max(-4)");
    return 0;
}

実行例:
OK 20:20 8+5+2+4+1
OK 8:8 max(8,5,2,4,1)
OK 4:4 0-4+6+3-1
OK 6:6 max(0,-4,6,3,-1)
OK -4:-4 -4
OK -4:-4 max(-4)

説明:iaddは2数の合計,imaxは2数の大きい方を返す関数.
iarray_injectは初期値として配列の先頭を保存し,2番目からは保存し
た値とi番目の値を合計(iadd)/比較(imax)して最後までいったらその値
を返している.
main関数では単体テストを行っている.

考察:関数ポインタを使わずとも別の関数に分けて書いてもできるが,
同じような動作をする関数であれば集約するとかなり見やすくなると感
じた.
四則演算は記述法を互換できる場合が多いため,関数ポインタが使える
思った.(rubyのinject関数など)

アンケート:
A1. 「*」を付ける場合を間違えないこと.
A2. 関数を分割すること.
A3. 関数ポインタが分からなかったが,課題をやって少し理解したよう
な気がした.

m1910631 2b:○ Sun May 24 14:29:44 2020


プログラミング通論レポートb#2

<課題1>
演習2d 2つの昇順の整数配列を昇順でマージするプログラム

<コード>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

void iarray_read(int *a, int n){
    for (int i = 0; i < n; ++i)
    {
        printf("%d> ", i + 1);
        scanf("%d", a + i);
    }
}
void iarray_print(int *a, int n){
    for (int i = 0; i < n; ++i)
    {
        printf(" %2d", a[i]);
    }
    printf("\n");
}
bool iarray_equal(int *a, int *b, int n){
    for (int i = 0; i < n; ++i)
    {
        if (a[i] != b[i])
        {
            return false;
        }
    }
    return true;
}
char *bool2str(bool b) { return b ? "true" : "false"; }

void expect_int(int i1, int i2, char *msg){
    printf("%s %d:%d %s\n", (i1 == i2) ? "OK" : "NG", i1, i2, msg);
}

void expect_double(double d1, double d2, char *msg){
    printf("%s %g:%g %s\n", (d1 == d2) ? "OK" : "NG", d1, d2, msg);
}

void expect_iarray(int *a, int *b, int n, char *msg){
    printf("%s %s\n", iarray_equal(a, b, n) ? "OK" : "NG", msg);
    iarray_print(a, n);
    iarray_print(b, n);
}

int *ivec_new(int size){
    int *a = (int *)malloc((size + 1) * sizeof(int));
    a[0] = size;
    return a;
}
void ivec_read(int *a) { iarray_read(a + 1, a[0]); }
void ivec_print(int *a) { iarray_print(a + 1, a[0]); }
int *ivec_concat(int *a, int *b){
    int *c = ivec_new(a[0] + b[0]);
    for (int i = 1; i <= a[0]; ++i)    {
        c[i] = a[i];
    }
    for (int i = 1; i <= b[0]; ++i)    {
        c[i + a[0]] = b[i];
    }
    return c;
}
int *ivec_sort_merge(int *a, int *b){
    int *c = ivec_new(a[0] + b[0]);
    int count_a = 1, count_b = 1, count_c = 1;
    while (!(count_a > a[0] && count_b > b[0])){
        if (a[count_a] < b[count_b] || count_b > b[0]){
            c[count_c] = a[count_a];
            count_a++;
            count_c++;
        }
        else{
            c[count_c] = b[count_b];
            count_b++;
            count_c++;
        }
    } 
    return c;
}

int main(){
    int a[] = {5,2,5,6,9,11}, b[] = {3,3,7,8}, c[] = {8, 2, 3, 5, 6, 7, 8, 9, 11};
    int *p = ivec_sort_merge(a, b);
    expect_iarray(p, c, 9, "5,2,5,6,9,11 + 3,3,7,8 = 8, 2, 3, 5, 6, 7, 8, 9, 11");
    int d[] = {1,2}, e[] = {1,2}, f[] = {2,2,2};
    int *q = ivec_sort_merge(d, e);
    expect_iarray(q, f, 3, "2 + 2 = 2,2");
    int g[] = {4,3,3,3,90}, h[] = {4,1,3,3,3}, i[] = {8, 1, 3, 3, 3, 3, 3, 3, 90};
    int *r = ivec_sort_merge(g, h);
    expect_iarray(r, i, 9, "3,3,3,90 + 1,3,3,3 = 1, 3, 3, 3, 3, 3, 3, 90");
    return 0;
}
<説明>

ivec_sort_merge 関数が自作部分。引数配列a,bと戻り値用配列cにそれ
ぞれカウンタ変数を用意し、aとbをカウンタを動かしながら比較して大
きい方を配列cに入れるようにした。a,bそれぞれの末尾に到達した場合
は相手側の要素をcに入れるようにした。
    

<テスト結果>
OK 5,2,5,6,9,11 + 3,3,7,8 = 8, 2, 3, 5, 6, 7, 8, 9, 11
  8  2  3  5  6  7  8  9 11
  8  2  3  5  6  7  8  9 11
OK 2 + 2 = 2,2
  2  2  2
  2  2  2
OK 3,3,3,90 + 1,3,3,3 = 1, 3, 3, 3, 3, 3, 3, 90
  8  1  3  3  3  3  3  3 90
  8  1  3  3  3  3  3  3 90

<考察>

前にマージソートの実装でマージしたい配列の末尾にどの要素よりも大
きい整数を入れると末尾に到達したときの処理を簡単にできるというの
を読んだのでそれでできないかと思ったが、配列の要素の範囲が指定さ
れていなかったのでこのようにした。配列の末尾に到達したかの判定に
配列長がすぐわかるのが便利だと思った。bool値の否定のやり方が分か
らなかったので調べたら!を前につけると反転するということを初めて
知った。

<課題2>
演習3a cdseqで等差数列の初項に戻すプログラム

<コード>
//cdseq.c
#include <stdlib.h>
#include "cdseq.h"
struct cdseq { int value, diff, initial; };
struct cdseq *cdseq_new(int s, int d) {
    struct cdseq *r = (struct cdseq*)malloc(sizeof(struct cdseq));
    r->value = s; r->initial = s; r->diff = d; return r;
}
int cdseq_get(struct cdseq *r) {
    int v = r->value; r->value += r->diff; return v;
}
void cdseq_free(struct cdseq *r) {
    free(r);
}
void cdseq_reset(struct cdseq *r) {
    r->value = r->initial;
}

//3a.c
#include <stdio.h>
#include "cdseq.h"
void expect_int(int i1, int i2, char *msg) {
    printf("%s %d:%d %s\n", (i1==i2)?"OK":"NG", i1, i2, msg);
}
int main(void) {
    struct cdseq *s = cdseq_new(2, 3);
    expect_int(cdseq_get(s), 2, "2+3*0 = 2");
    expect_int(cdseq_get(s), 5, "2+3*1 = 5");
    expect_int(cdseq_get(s), 8, "2+3*2 = 8");
    cdseq_reset(s);
    expect_int(cdseq_get(s), 2, "2+3*0 = 2");
    expect_int(cdseq_get(s), 5, "2+3*1 = 5");
    expect_int(cdseq_get(s), 8, "2+3*2 = 8");
    cdseq_reset(s);
    cdseq_reset(s);
    expect_int(cdseq_get(s), 2, "2+3*0 = 2");
    return 0;
}

<説明>
構造体cdseqに初期状態を保存する変数を追加し、cdseq_resetで読み込めるようにした。

<テスト結果>
OK 2:2 2+3*0 = 2
OK 5:5 2+3*1 = 5
OK 8:8 2+3*2 = 8
OK 2:2 2+3*0 = 2
OK 5:5 2+3*1 = 5
OK 8:8 2+3*2 = 8
OK 2:2 2+3*0 = 2

<考察>
情報隠蔽の仕組みについてイメージをつかむのに少し時間がかかったが、
ほかの言語のクラスのようなものだという説明で何となく分かった。ポ
インタはc言語にしかないが、ポインタを使って実装するようなことは
多言語ではもう少し工夫されてわかりやすくなっていることがわかった。

<アンケート>
A1.関係ないアドレスを扱わない
A2.数学力
A3.ポインタの実用

m1910632 2b:◎ Mon May 25 17:33:28 2020

レポート2b


演習2-a: 2 つの列を受け取り、両方の列の内容を交互に並べた列を返す

方針:
aのサイズ+bのサイズの回数のループを回し、偶奇で分けて新たな配列c
に交互に入れることで、演習の内容を達成することを考えた。
また一方の配列のサイズよりもう一方の配列のサイズが十分大きいとき、
余った要素をそのまま新たな配列に入れることにした。

例えば、
[1,2,3,4,5,6,7,8]+[0,0,0]=[1,0,2,0,3,0,4,5,6,7,8]
というようにする。

コード:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

int *ivec_new(int size) {
    int *a = (int*)malloc((size+1) * sizeof(int));
    a[0] = size; return a;
}
int *ivec_concat(int *a, int *b) {
    int *c = ivec_new(a[0]+b[0]);
    for(int i = 1; i <= a[0]; ++i) { c[i] = a[i]; }
    for(int i = 1; i <= b[0]; ++i) { c[i + a[0]] = b[i]; }
    return c;
}
bool iarray_equal(int *a, int *b, int n) {
    for(int i = 0; i < n; ++i) {
        if(a[i] != b[i]) { return false; }
    }
    return true;
}
void iarray_print(int *a, int n) {
    for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
    printf("\n");
}
void expect_iarray(int *a, int *b, int n, char *msg) {
    printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
    iarray_print(a, n); iarray_print(b, n);
}

int *ivec_concat_mutual(int *a, int *b);
int main(void) {
    int a0[] = {3,1,2,3}, b0[] = {2,4,5}, c0[] = {5,1,4,2,5,3};
    int *p = ivec_concat_mutual(a0, b0);
    expect_iarray(p, c0, 6, "[1,2,3]+[4,5]=[1,4,2,5,3]");
    
    int a1[] = {3,1,2,3}, b1[] = {3,4,5,6}, c1[] = {6,1,4,2,5,3,6};
    p = ivec_concat_mutual(a1, b1);
    expect_iarray(p, c1, 7, "[1,2,3]+[4,5,6]=[1,4,2,5,3,6]");
    
    int a2[] = {3,1,2,3}, b2[] = {4,4,5,6,7}, c2[] = {7,1,4,2,5,3,6,7};
    p = ivec_concat_mutual(a2, b2);
    expect_iarray(p, c2, 8, "[1,2,3]+[4,5,6,7]=[1,4,2,5,3,6,7]");
    
    int a3[] = {8,1,2,3,4,5,6,7,8}, b3[] = {3,0,0,0}, c3[] = {11,1,0,2,0,3,0,4,5,6,7,8};
    p = ivec_concat_mutual(a3, b3);
    expect_iarray(p, c3, 12, "[1,2,3,4,5,6,7,8]+[0,0,0]=[1,0,2,0,3,0,4,5,6,7,8]");
    
    int a4[] = {3,0,0,0}, b4[] = {5,0,0,0,0,0}, c4[] = {8,0,0,0,0,0,0,0,0};
    p = ivec_concat_mutual(a4, b4);
    expect_iarray(p, c4, 9, "[0,0,0]+[0,0,0,0,0]=[0,0,0,0,0,0,0,0]");
    
    int a5[] = {3,1,2,3}, b5[] = {0}, c5[] = {3,1,2,3};
    p = ivec_concat_mutual(a5, b5);
    expect_iarray(p, c5, 4, "[1,2,3]+[]=[1,2,3]");
    
    int a6[] = {0}, b6[] = {3,1,2,3}, c6[] = {3,1,2,3};
    p = ivec_concat_mutual(a6, b6);
    expect_iarray(p, c6, 4, "[]+[1,2,3]=[1,2,3]");
    
    int a7[] = {0}, b7[] = {0}, c7[] = {0};
    p = ivec_concat_mutual(a7, b7);
    expect_iarray(p, c7, 1, "[]+[]=[]");
    
    int a8[] = {7,39,9453,23,-425,82348085,34590,-543}, b8[] = {6,0,0,1,1,2,2};
    int c8[] = {13,39,0,9453,0,23,1,-425,1,82348085,2,34590,2,-543};
    p = ivec_concat_mutual(a8, b8);
    expect_iarray(p, c8, 14, "[39,9453,23,-425,82348085,34590,-543]+[0,0,1,1,2,2]=[39,0,9453,0,23,1,-425,1,82348085,2,34590,2,-543]");
    
    int a9[] = {3,34589,435809,3458}, b9[] = {10,324,-435,4351143,9687,239,324,-435,4351143,9687,239};
    int c9[] = {13,34589,324,435809,-435,3458,4351143,9687,239,324,-435,4351143,9687,239};
    p = ivec_concat_mutual(a9, b9);
    expect_iarray(p, c9, 14, "[324,-435,4351143,9687,239,324,-435,4351143,9687,239]+[34589,435809,3458]=[34589,324,435809,-435,3458,4351143,9687,239,324,-435,4351143,9687,239]");
    
    int c10[] = {13,324,34589,-435,435809,4351143,3458,9687,239,324,-435,4351143,9687,239};
    p = ivec_concat_mutual(b9, a9);
    expect_iarray(p, c10, 14, "[34589,435809,3458]+[324,-435,4351143,9687,239,324,-435,4351143,9687,239]=[324,34589,-435,435809,4351143,3458,9687,239,324,-435,4351143,9687,239]");
    
    int a11[] = {10,0,1,2,3,4,5,6,7,8,9}, b11[] = {1,1000000000}, c11[] = {11,0,1000000000,1,2,3,4,5,6,7,8,9};
    p = ivec_concat_mutual(a11, b11);
    expect_iarray(p, c11, 12, "[0,1,2,3,4,5,6,7,8,9]+[1000000000]=[0,1000000000,1,2,3,4,5,6,7,8,9]");
    
    p = ivec_concat_mutual(b11, a11);
    int c12[] = {11,1000000000,0,1,2,3,4,5,6,7,8,9};
    expect_iarray(p, c12, 12, "[1000000000]+[0,1,2,3,4,5,6,7,8,9]=[1000000000,0,1,2,3,4,5,6,7,8,9]");
    
    return 0;
}

int *ivec_concat_mutual(int *a, int *b){
    int *c = ivec_new(a[0]+b[0]);
    
    int a_idx = 1, b_idx = 1;
    for(int i = 1; i <= c[0]; i++){
        if((i % 2 == 1 && a_idx <= a[0]) || b_idx > b[0]){
            c[i] = a[a_idx];
            a_idx++;
        }else{
            c[i] = b[b_idx];
            b_idx++;
        }
    }
    
    return c;
}

実行例:

OK [1,2,3]+[4,5]=[1,4,2,5,3]
  5  1  4  2  5  3
  5  1  4  2  5  3
OK [1,2,3]+[4,5,6]=[1,4,2,5,3,6]
  6  1  4  2  5  3  6
  6  1  4  2  5  3  6
OK [1,2,3]+[4,5,6,7]=[1,4,2,5,3,6,7]
  7  1  4  2  5  3  6  7
  7  1  4  2  5  3  6  7
OK [1,2,3,4,5,6,7,8]+[0,0,0]=[1,0,2,0,3,0,4,5,6,7,8]
 11  1  0  2  0  3  0  4  5  6  7  8
 11  1  0  2  0  3  0  4  5  6  7  8
OK [0,0,0]+[0,0,0,0,0]=[0,0,0,0,0,0,0,0]
  8  0  0  0  0  0  0  0  0
  8  0  0  0  0  0  0  0  0
OK [1,2,3]+[]=[1,2,3]
  3  1  2  3
  3  1  2  3
OK []+[1,2,3]=[1,2,3]
  3  1  2  3
  3  1  2  3
OK []+[]=[]
  0
  0
OK [39,9453,23,-425,82348085,34590,-543]+[0,0,1,1,2,2]=[39,0,9453,0,23,1,-425,1,82348085,2,34590,2,-543]
 13 39  0 9453  0 23  1 -425  1 82348085  2 34590  2 -543
 13 39  0 9453  0 23  1 -425  1 82348085  2 34590  2 -543
OK [324,-435,4351143,9687,239,324,-435,4351143,9687,239]+[34589,435809,3458]=[34589,324,435809,-435,3458,4351143,9687,239,324,-435,4351143,9687,239]
 13 34589 324 435809 -435 3458 4351143 9687 239 324 -435 4351143 9687 239
 13 34589 324 435809 -435 3458 4351143 9687 239 324 -435 4351143 9687 239
OK [34589,435809,3458]+[324,-435,4351143,9687,239,324,-435,4351143,9687,239]=[324,34589,-435,435809,4351143,3458,9687,239,324,-435,4351143,9687,239]
 13 324 34589 -435 435809 4351143 3458 9687 239 324 -435 4351143 9687 239
 13 324 34589 -435 435809 4351143 3458 9687 239 324 -435 4351143 9687 239
OK [0,1,2,3,4,5,6,7,8,9]+[1000000000]=[0,1000000000,1,2,3,4,5,6,7,8,9]
 11  0 1000000000  1  2  3  4  5  6  7  8  9
 11  0 1000000000  1  2  3  4  5  6  7  8  9
OK [1000000000]+[0,1,2,3,4,5,6,7,8,9]=[1000000000,0,1,2,3,4,5,6,7,8,9]
 11 1000000000  0  1  2  3  4  5  6  7  8  9
 11 1000000000  0  1  2  3  4  5  6  7  8  9

解説:

main関数ではexpect_iarrayを用いてテストを行う。テストする関数で
あるivec_concat_mutualでは次の動作を行う。

まずivec_new(a[0]+b[0])で2つの配列a, bの長さを足した新たな可変長
配列cを作る。次に変数a_idx = 1, b_idx = 1を作る。これはa,bそれぞ
れ次にcに入れる候補となる数値が入っているa, bのindexを指す。例え
ばa_idxがa[0](aのサイズ)以下であるとき、cに次入る予定のa[a_idx]
である。また、a_idx > a[0] のときはaの全ての要素を見終えたことに
なる。

1 <= i <= c[0](配列cのサイズ) のループでは次の操作を行う。iが奇
数かつa_idxがa[0]以下である、またはb_idx > b[0](bを全て見終えた)
とき、c[i]にa[a_idx]を入れ、a_idxに1を足す。iが偶数かつb_idxが
b[0]以下である、またはa_idx > a[0](aを全て見終えた)とき、c[i]に
b[b_idx]を入れ、b_idxに1を足す。

最後にcを返して動作を終える。

考察:

このループでは一見、b_idx > b[0] かつ a_idx > a[0] のとき、1番目
の分岐に入ってしまいバグを起こしてしまうように見える。しかし各ルー
プではa_idxかb_idxのどちらかにしか1を足さず、 a_idx + b_idx <=
c[0] が常に成立する。b_idx > b[0] かつ a_idx > a[0] のとき a_idx
+ b_idx > a[0] + b[0] = c[0] であり、上の事実に反するためこのよ
うな状況にはならないことがわかった。これによりこのfor文の正しさ
が言えた。

交互に操作を行いたいときは偶奇に分けると簡単であると思いこのコー
ドを組んだが、これはn個の配列にも拡張できることに気がついた。i %
n というようにあまりをとることで、i % n == 0のときは1番目の配列、
1のときは2番目の配列、というようにすれば拡張することができると考
えた。

テストケースを作るときは要素数1の配列と十分大きい配列、要素のな
い配列を混ぜる、要素数が1個違いの配列、同じ配列でもいれる順番を
変えるなど盲点になりそうなところを中心に作るとバグを見つけやすい
と考えた。


///////////////////////////////////////


演習 3-e: 構造体と情報隠蔽のしくみを使って、何か面白いと思う機能
を実現する

方針:

n個の数字がどのグループに属すかを管理するAPIを作ることにした。例
えばはじめは別々のグループ{(0),(1),(2)}だがunite(1,2)とすると
{(0),(1,2)}となるようなものである。

コード:
---------------group.c---------------

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#include "group.h"

group *group_new(int size){
    group *g = (group*)malloc(sizeof(group));
    g->size = size;
    g->next = size;
    g->belongto = (int*)malloc(sizeof(int)*size);
    for(int i = 0; i < size; i++){
        g->belongto[i] = i;
    }
    return g;
}

void unite(group *g, int a, int b){
    for(int i = 0; i < g->size; i++){
        if(i == b) continue;
        if(is_same(g, i, b)){
            g->belongto[i] = g->belongto[a];
        }
    }
    g->belongto[b] = g->belongto[a];
}

bool is_same(group *g, int a, int b){
    return g->belongto[a] == g->belongto[b];
}

void leave(group *g, int a){
    g->belongto[a] = g->next++;
}

void group_free(group *g){
    free(g->belongto);
    free(g);
}

-------------------------------------

---------------group.h---------------

#include <stdbool.h>

typedef struct {
    int size, next, *belongto;
} group;

group *group_new(int size);
void unite(group *g, int a, int b);
bool is_same(group *g, int a, int b);
void leave(group *g, int a);
void group_free(group *g);

-------------------------------------

---------------main.c---------------

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "group.h"

char *bool2str(bool b) { return b ? "true" : "false"; }
void expect_bool(bool b1, bool b2, char *msg) {
  printf("%s %s:%s %s\n", (b1==b2)?"OK":"NG",
         bool2str(b1), bool2str(b2), msg);
}

int main(void){
    group *g = group_new(5);
    expect_bool(is_same(g, 1, 4), false, "(0),(1),(2),(3),(4), 1 != 4");
    expect_bool(is_same(g, 1, 1), true, "(0),(1),(2),(3),(4), 1 == 1");
    expect_bool(is_same(g, 2, 2), true, "(0),(1),(2),(3),(4), 2 == 2");
    unite(g, 1, 4);printf("   unite: 1, 4\n");
    expect_bool(is_same(g, 1, 4), true, "(0),(1,4),(2),(3), 1 == 4");
    unite(g, 1, 3);printf("   unite: 1, 3\n");
    expect_bool(is_same(g, 1, 3), true, "(0),(1,3,4),(2), 1 == 3");
    expect_bool(is_same(g, 3, 4), true, "(0),(1,3,4),(2), 3 == 4");
    expect_bool(is_same(g, 1, 4), true, "(0),(1,3,4),(2), 1 == 4");
    leave(g, 4);printf("   leave: 4\n");
    expect_bool(is_same(g, 1, 4), false, "(0),(1,3),(2),(4), 1 != 4");
    expect_bool(is_same(g, 1, 3), true, "(0),(1,3),(2),(4), 1 == 3");
    leave(g, 1);printf("   leave: 1\n");
    expect_bool(is_same(g, 1, 4), false, "(0),(1),(2),(3),(4), 1 != 3");
    expect_bool(is_same(g, 1, 4), false, "(0),(1),(2),(3),(4), 1 != 2");
    unite(g, 0, 4);printf("   unite: 0, 4\n");
    unite(g, 1, 2);printf("   unite: 1, 2\n");
    expect_bool(is_same(g, 2, 0), false, "(0,4),(1,2),(3), 0 != 2");
    expect_bool(is_same(g, 0, 4), true, "(0,4),(1,2),(3), 0 != 2");
    expect_bool(is_same(g, 1, 2), true, "(0,4),(1,2),(3), 0 != 2");
    expect_bool(is_same(g, 2, 3), false, "(0,4),(1,2),(3), 0 != 2");
    expect_bool(is_same(g, 4, 1), false, "(0,4),(1,2),(3), 4 != 1");
    expect_bool(is_same(g, 2, 0), false, "(0,4),(1,2),(3), 2 != 0");
    unite(g, 0, 1);printf("   unite: 0, 1\n");
    expect_bool(is_same(g, 2, 0), true, "(0,1,2,4),(3), 0 == 2");
    expect_bool(is_same(g, 0, 4), true, "(0,1,2,4),(3), 0 == 4");
    expect_bool(is_same(g, 2, 4), true, "(0,1,2,4),(3), 2 == 4");
    expect_bool(is_same(g, 0, 1), true, "(0,1,2,4),(3), 0 == 1");
    expect_bool(is_same(g, 0, 3), false, "(0,1,2,4),(3), 0 != 3");
    expect_bool(is_same(g, 1, 3), false, "(0,1,2,4),(3), 1 != 3");
    expect_bool(is_same(g, 2, 3), false, "(0,1,2,4),(3), 2 != 3");
    expect_bool(is_same(g, 4, 3), false, "(0,1,2,4),(3), 4 != 3");
    leave(g, 1);printf("   leave: 1\n");
    expect_bool(is_same(g, 1, 3), false, "(0,2,4),(1),(3), 1 != 3");
    expect_bool(is_same(g, 0, 1), false, "(0,2,4),(1),(3), 0 != 1");
    expect_bool(is_same(g, 4, 1), false, "(0,2,4),(1),(3), 4 != 1");
    expect_bool(is_same(g, 2, 0), true, "(0,2,4),(1),(3), 0 == 2");
    expect_bool(is_same(g, 0, 4), true, "(0,2,4),(1),(3), 0 == 4");
    unite(g, 1, 3);printf("   unite: 1, 3\n");
    expect_bool(is_same(g, 0, 1), false, "(0,2,4),(1,3), 0 != 1");
    expect_bool(is_same(g, 0, 2), true, "(0,2,4),(1,3), 0 == 2");
    expect_bool(is_same(g, 0, 3), false, "(0,2,4),(1,3), 0 != 3");
    expect_bool(is_same(g, 0, 4), true, "(0,2,4),(1,3), 0 == 4");
    expect_bool(is_same(g, 1, 2), false, "(0,2,4),(1,3), 1 != 2");
    expect_bool(is_same(g, 1, 3), true, "(0,2,4),(1,3), 1 == 3");
    expect_bool(is_same(g, 1, 4), false, "(0,2,4),(1,3), 1 != 4");
    expect_bool(is_same(g, 2, 3), false, "(0,2,4),(1,3), 2 != 3");
    expect_bool(is_same(g, 2, 4), true, "(0,2,4),(1,3), 2 == 4");
    expect_bool(is_same(g, 3, 4), false, "(0,2,4),(1,3), 3 != 4");
    unite(g, 0, 3);printf("   unite: 0, 3\n");
    expect_bool(is_same(g, 0, 1), true, "(0,1,2,3,4), 0 == 1");
    expect_bool(is_same(g, 0, 2), true, "(0,1,2,3,4), 0 == 2");
    expect_bool(is_same(g, 0, 3), true, "(0,1,2,3,4), 0 == 3");
    expect_bool(is_same(g, 0, 4), true, "(0,1,2,3,4), 0 == 4");
    expect_bool(is_same(g, 1, 2), true, "(0,1,2,3,4), 1 == 2");
    expect_bool(is_same(g, 1, 3), true, "(0,1,2,3,4), 1 == 3");
    expect_bool(is_same(g, 1, 4), true, "(0,1,2,3,4), 1 == 4");
    expect_bool(is_same(g, 2, 3), true, "(0,1,2,3,4), 2 == 3");
    expect_bool(is_same(g, 2, 4), true, "(0,1,2,3,4), 2 == 4");
    expect_bool(is_same(g, 3, 4), true, "(0,1,2,3,4), 3 == 4");
    return 0;
}

------------------------------------

実行例:
OK false:false (0),(1),(2),(3),(4), 1 != 4
OK true:true (0),(1),(2),(3),(4), 1 == 1
OK true:true (0),(1),(2),(3),(4), 2 == 2
   unite: 1, 4
OK true:true (0),(1,4),(2),(3), 1 == 4
   unite: 1, 3
OK true:true (0),(1,3,4),(2), 1 == 3
OK true:true (0),(1,3,4),(2), 3 == 4
OK true:true (0),(1,3,4),(2), 1 == 4
   leave: 4
OK false:false (0),(1,3),(2),(4), 1 != 4
OK true:true (0),(1,3),(2),(4), 1 == 3
   leave: 1
OK false:false (0),(1),(2),(3),(4), 1 != 3
OK false:false (0),(1),(2),(3),(4), 1 != 2
   unite: 0, 4
   unite: 1, 2
OK false:false (0,4),(1,2),(3), 0 != 2
OK true:true (0,4),(1,2),(3), 0 != 2
OK true:true (0,4),(1,2),(3), 0 != 2
OK false:false (0,4),(1,2),(3), 0 != 2
OK false:false (0,4),(1,2),(3), 4 != 1
OK false:false (0,4),(1,2),(3), 2 != 0
   unite: 0, 1
OK true:true (0,1,2,4),(3), 0 == 2
OK true:true (0,1,2,4),(3), 0 == 4
OK true:true (0,1,2,4),(3), 2 == 4
OK true:true (0,1,2,4),(3), 0 == 1
OK false:false (0,1,2,4),(3), 0 != 3
OK false:false (0,1,2,4),(3), 1 != 3
OK false:false (0,1,2,4),(3), 2 != 3
OK false:false (0,1,2,4),(3), 4 != 3
   leave: 1
OK false:false (0,2,4),(1),(3), 1 != 3
OK false:false (0,2,4),(1),(3), 0 != 1
OK false:false (0,2,4),(1),(3), 4 != 1
OK true:true (0,2,4),(1),(3), 0 == 2
OK true:true (0,2,4),(1),(3), 0 == 4
   unite: 1, 3
OK false:false (0,2,4),(1,3), 0 != 1
OK true:true (0,2,4),(1,3), 0 == 2
OK false:false (0,2,4),(1,3), 0 != 3
OK true:true (0,2,4),(1,3), 0 == 4
OK false:false (0,2,4),(1,3), 1 != 2
OK true:true (0,2,4),(1,3), 1 == 3
OK false:false (0,2,4),(1,3), 1 != 4
OK false:false (0,2,4),(1,3), 2 != 3
OK true:true (0,2,4),(1,3), 2 == 4
OK false:false (0,2,4),(1,3), 3 != 4
   unite: 0, 3
OK true:true (0,1,2,3,4), 0 == 1
OK true:true (0,1,2,3,4), 0 == 2
OK true:true (0,1,2,3,4), 0 == 3
OK true:true (0,1,2,3,4), 0 == 4
OK true:true (0,1,2,3,4), 1 == 2
OK true:true (0,1,2,3,4), 1 == 3
OK true:true (0,1,2,3,4), 1 == 4
OK true:true (0,1,2,3,4), 2 == 3
OK true:true (0,1,2,3,4), 2 == 4
OK true:true (0,1,2,3,4), 3 == 4

解説:
構造体groupには次のような変数を持つ
 - int size: グループのサイズ
 - int next: まだこのグループに存在しないグループ番号
 - int *belongto: グループ番号の配列。例えばbelongto[3] = 5 であ
 れば3という数はグループ5に属していることになる。
 
group_newでは構造体groupを生成する。
まずgroup分のメモリをmallocで確保する。
sizeとnextにグループのサイズを代入する。
belongtoにサイズ分のintのメモリを確保する。
belongto[i] = i (0 <= i < size)
となるように数値を入れる。この数値はグループの番号である。
最後にグループのポインタgを返す。

以降の関数ではgroupのポインタgを必ず受け取るものとする。

uniteでは受け取った番号を同じグループにする。
まず、bを除く0からg->size未満の整数についてg->belongto[i] ==
g->belongto[b]、すなわちbと同じグループに属すもののグループ番号
をg->belongto[i] = g->belongto[a] することによりaと同じにする。
最後にbのグループ番号も書き換えて操作を終える。

is_sameでは受け取った2つの番号が同じグループに属すかを確認する。
同じであればtrue, 違えばfalseを返す。

leaveでは受け取った数aを今いるグループから脱出させる。
具体的にはg->nextをaのグループ番号とすることによりグループから抜け出させる。
g->nextはまだ使われていない番号であるのでどのグループにも属すことがない。
最後にg->nextがどのグループにも属すことがないように1を足して関数を抜ける。

group_freeではグループgのメモリを解放する。
まずg->belongtoをfreeしてからgをfreeする。
これにより数列分のメモリもちゃんとfreeすることができる。

group.hではこれらの関数の宣言と構造体を定義する。

考察:

この方法で隠匿できるのは関数だけなので、この方法だと構造体の中身
をそのまま変更されてデータが壊れてしまう可能性を考えた。それに対
する対処として、値を得るためのget_x(), set_x(int x)のような関数
を作り、構造体の値への直接のアクセスはAPI外からは絶対に行わない
ような取り決めをすると良いと考えた。

グループから離れる時にg->belongto[i] = g->next++として新しいグルー
プ名を付与していたがこの方法ではintの上限に達したときにオーバー
フローなどによりグループ番号が競合する危険性がある。そのために定
期的にグループ番号を「整頓」する必要があると考えた。例えば
belongto = {5, 9, 2, 2, 5, 11} を {1, 2, 3, 3, 1, 4} に変えるこ
とでこの問題を回避できると考えた。

Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。
どれがポインタでどれが値なのかをしっかり覚えておくことと、ポイン
タに加算をしすぎることによる範囲外アクセスに注意した方が良いと感
じた。

Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に
付いていないと思うことは何ですか。
バグを書かないように気を付けて書くこととはやくバグを見つけること
が難しいと感じている。

Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
C言語での構造体を使った情報隠匿ついて理解することができた。また
malloc, freeの考え方を何故か1年次に苦手に思っていたが今回しっか
り向き合ってみたら理解できたので良かった。

m1910636 2b:◎ Mon May 25 22:59:33 2020




演習2 a,b
 a. 2つの列を受け取り,両方の列の内容を交互に並べた列を返す
 ivec_2a(int *a,int *b)を実装した.2つの長さが異なる場合は片方が
 末端まで行ったら後は残りの方を順に並べるだけにした.
 
 b. また,1つの列を受け取り,その内容を逆順にした列を返す
 ivec_2b(int a*)を実装した.


コード:
// ivec_demo.c --- int vector demonstration.
#include <stdio.h>
#include <stdlib.h>
/*void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}*/
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
//void ivec_read(int *a) { iarray_read(a+1, a[0]); }
void ivec_print(int *a) { iarray_print(a+1, a[0]); }
/*int *ivec_concat(int *a, int *b) {
  int *c = ivec_new(a[0]+b[0]);
  for(int i = 1; i <= a[0]; ++i) { c[i] = a[i]; }
  for(int i = 1; i <= b[0]; ++i) { c[i + a[0]] = b[i]; }
  return c;
}*/

/***** ここから追加 *****/
int *ivec_2a(int *a,int *b){
  int *c =ivec_new(a[0]+b[0]);//サイズ
  int aorb=1;//0:b other:a
  int ai=1,bi=1,ci=1;//a[ai],b[bi]
  while(ci<=c[0]){
    if(aorb){
      if(ai<=a[0]){
        c[ci]=a[ai];
        ai++;
        ci++;
      }
    }else{
      if(bi<=b[0]){
        c[ci]=b[bi];
        bi++;
        ci++;
      }
    }
    aorb=!aorb;
  }
  return c;
}

int *ivec_2b(int *a){
  int *b=ivec_new(*a);
  for(int i=1;i<=*b;i++){
    b[i]=a[*a-i+1];
  }
  return b;
}
/*** チェッカー コピーしてきた***/
int iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return 0; }
  }
  return 1;
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}

/*** ここからmain ***/
int main(void) {
  int *a, *b, *c;
  a = ivec_new(3);
  a[1]=0;a[2]=1;a[3]=2;
  b = ivec_new(5);
  b[1]=10;b[2]=11;b[3]=12;b[4]=13;b[5]=14;

//テストケース群

  c= ivec_2a(a,b);
  int correctC[]={8,0,10,1,11,2,12,13,14};
  expect_iarray(c,correctC,9,"8,{0 10 1 11 2 12 13 14}");//a>bの場合

  int *e=ivec_2a(b,a);//逆にした
  int correctE[]={8,10,0,11,1,12,2,13,14};
  expect_iarray(e,correctE,9,"8,{10,0,11,1,12,2,13,14}");//b>aの場合

  int *f=ivec_new(0);
  int *g=ivec_2a(f,b);//aが空
  int correctG[]={5,10,11,12,13,14};
  expect_iarray(g,correctG,6,"5,{10,11,12,13,14}");

  int *h=ivec_2a(a,f);//bが空
  int correctH[]={3,0,1,2};
  expect_iarray(h,correctH,4,"3,{0,1,2}");

  int *i=ivec_2a(f,f);//どちらも空
  int correctI[]={0};
  expect_iarray(i,correctI,1,"0,{}");


  int *k=ivec_2a(c,e);//同じ長さ
  int correctK[]={16,0,10,10,0,1,11,11,1,2,12,12,2,13,13,14,14};
  expect_iarray(k,correctK,17,"16,{0,10,10,0,1,11,11,1,2,12,12,2,13,13,14,14}");


  int *d=ivec_2b(c);//逆順
  int correctD[]={8,14,13,12,2,11,1,10,0};
  expect_iarray(d,correctD,9,"8,{14,13,12,2,11,1,10,0}");

  int *j=ivec_2b(f);//空の逆順
  int correctJ[]={0};
  expect_iarray(j,correctJ,1,"0,{}");


  free(a); free(b); free(c);free(d); free(e); free(f); free(g);free(i); free(j);
  return 0;
}

ivec_2aは2つの列を受け取り,長さを足したcという列を生成してその
アドレスを返す.while文を用い,すべてコピーし切るまで繰り返した.
このときa,bとも先頭から順に交互にコピーしていき,末端までたどり
着いたら飛ばすようにした.テストケースとして,それぞれ長さが違う
列(a>b,b>a両方)や,少なくも一方が長さ0の場合,長さが同じ場合を調
べた.
ivec_2bは受け取った列をfor文を使って逆順にコピーしたものを作成し
そのアドレスを返した.テストケースとして,普通の場合と渡す列が空
の場合を調べた.

実行結果:
$ ./activity2.out
OK 8,{0 10 1 11 2 12 13 14}
  8  0 10  1 11  2 12 13 14
  8  0 10  1 11  2 12 13 14
OK 8,{10,0,11,1,12,2,13,14}
  8 10  0 11  1 12  2 13 14
  8 10  0 11  1 12  2 13 14
OK 5,{10,11,12,13,14}
  5 10 11 12 13 14
  5 10 11 12 13 14
OK 3,{0,1,2}
  3  0  1  2
  3  0  1  2
OK 0,{}
  0
  0
OK 16,{0,10,10,0,1,11,11,1,2,12,12,2,13,13,14,14}
 16  0 10 10  0  1 11 11  1  2 12 12  2 13 13 14 14
 16  0 10 10  0  1 11 11  1  2 12 12  2 13 13 14 14
OK 8,{14,13,12,2,11,1,10,0}
  8 14 13 12  2 11  1 10  0
  8 14 13 12  2 11  1 10  0
OK 0,{}
  0
  0

演習3 a,b,e
a. 上のcdseq.cに,等差数列を初項に戻す機能cdseqresetを追加してみよ.
b.上の例ではgetを呼ぶたびに次の値が出て来たが,getだけでは値が進
まず,cdseqfwdを呼ぶと次の値に進むように変更してみよ.さらに,現
在が何番目(最初が0とする)の項かを返す機能cdseqnumを追加してみよ.
e.その他,構造体と情報隠蔽のしくみを使って,何か面白いと思う機能
を実現してみよ.
演習eとして,数列の情報を羅列するvoid cdseq_printDetails(struct
cdseq r),また,せっかくなのでオブジェクト指向風に公差を設定/所
得できるvoid setDiff(struct cdseq r,int d)およびint
getDiff(struct cdseq r)を実装した

/************************* cdseq.h **********************/

// cdseq.h --- constant difference sequence API.
struct cdseq *cdseq_new(int s, int d);
int cdseq_get(struct cdseq *r);
void cdseq_free(struct cdseq *r);
void cdseq_reset(struct cdseq *r);
void cdseq_fwd(struct cdseq *r);
int cdseq_num(struct cdseq *r);

int cdseq_getDiff(struct cdseq *r);
void cdseq_setDiff(struct cdseq *r, int d);
void cdseq_printDetails(struct cdseq *r);


/****************************cdseq.c **************************/

// cdseq.c -- cdseq implementation.
#include <stdlib.h>
#include<stdio.h>//演習eで必要
#include "cdseq.h"
struct cdseq { int value, diff,first,num; };//first,numを追加
struct cdseq *cdseq_new(int s, int d) {
  struct cdseq *r = (struct cdseq*)malloc(sizeof(struct cdseq));
  r->value = s; r->diff = d;
  r->first=s;
  r->num=0;
   return r;
}
int cdseq_get(struct cdseq *r) {//変更 進まないように
  int v = r->value; /*r->value += r->diff;ここをコメントアウト*/ return v;
}
void cdseq_free(struct cdseq *r) {
  free(r);
}

/****追加 ****/
void cdseq_reset(struct cdseq *r){
    r->value=r->first;
    r->num=0;
}

void cdseq_fwd(struct cdseq *r){
  r->value+=r->diff;
  r->num++;
}
int cdseq_num(struct cdseq *r){
  return r->num;
}

/*演習e*/
int cdseq_getDiff(struct cdseq *r){
  return r->diff;
}
void cdseq_setDiff(struct cdseq *r, int d){
  r->diff=d;
}
void cdseq_printDetails(struct cdseq *r){
  printf("初項%d, 公差%dの等差数列, 今は%d番目で値は%d.\n",r->first,r->diff,r->num,r->value);
}


/*************************mian.c *********************/

// cdseq_demo.c -- cdseq demonstration.
#include <stdio.h>
#include "cdseq.h"

void test_int(int a,int b,char *msg){
  printf("%s:%d %d %s\n",a==b? "OK":"NG",a,b,msg);
}

int main(void) {
  struct cdseq *s1 = cdseq_new(1, 2);
  struct cdseq *s2 = cdseq_new(0, 3);
  struct cdseq *s3 = cdseq_new(2, 0);//公差0
  int i;
  //s1->value=1;//これはコンパイルエラーになった
  for(i = 0; i < 4; ++i) {
    test_int(cdseq_get(s1),1+i*2,"get");
    test_int(cdseq_get(s2),0+i*3,"get");
    test_int(cdseq_get(s3),2+i*0,"get");
    
    test_int(cdseq_num(s1),i,"num");
    test_int(cdseq_num(s2),i,"num");
    test_int(cdseq_num(s3),i,"num");
    
    cdseq_fwd(s1);
    cdseq_fwd(s2);
    cdseq_fwd(s3);
  }
  cdseq_printDetails(s1);
  cdseq_printDetails(s2);
  cdseq_printDetails(s3);


  cdseq_reset(s1);//リセット
  cdseq_reset(s2);//リセット
  cdseq_reset(s3);//リセット
  test_int(cdseq_get(s1),1,"get");
  test_int(cdseq_get(s2),0,"get");
  test_int(cdseq_get(s3),2,"get");
  
  test_int(cdseq_num(s1),0,"num");
  test_int(cdseq_num(s2),0,"num");
  test_int(cdseq_num(s3),0,"num");
    
  cdseq_printDetails(s1);
  cdseq_printDetails(s2);
  cdseq_printDetails(s3);

  cdseq_setDiff(s1,10);
  cdseq_setDiff(s2,-4);
  test_int(cdseq_getDiff(s1),10,"getDiff");
  test_int(cdseq_getDiff(s2),-4,"getDiff");

  for(i=0;i<3;i++){
    test_int(cdseq_get(s1),1+i*10,"get");
    test_int(cdseq_get(s2),0+i*(-4),"get");
    
    cdseq_fwd(s1);
    cdseq_fwd(s2);
  }

  printf("\n"); cdseq_free(s1); cdseq_free(s2);
  return 0;
}

cdseq_resetを実装するために,構造体にint firstというフィールドを
追加した.これに初項を入れておくことでvalueを初項に戻せるように
した.
cdseq_fwd,cdseq_numは,int numというフィールドを追加し,
cdseq_getの中でvalueの値を更新しないように書き換え,cdseq_fwdの
中でnumをインクリメントし,valueに公差を足した.
また,単体テストを実行するために,test_intという2つの整数とメッ
セージ用の文字列を受け取り,値が同じかどうかとメッセージを出力す
る関数を作成した.これとcdseq_printDetailsを用いて,値が想定通り
かどうかを確かめた.具体的には,公差が0のときや負のとき,
cdseq_resetを実行したときについて調べた.

実行例:

$ ./activity3.out 
OK:1 1 get
OK:0 0 get
OK:2 2 get
OK:0 0 num
OK:0 0 num
OK:0 0 num
OK:3 3 get
OK:3 3 get
OK:2 2 get
OK:1 1 num
OK:1 1 num
OK:1 1 num
OK:5 5 get
OK:6 6 get
OK:2 2 get
OK:2 2 num
OK:2 2 num
OK:2 2 num
OK:7 7 get
OK:9 9 get
OK:2 2 get
OK:3 3 num
OK:3 3 num
OK:3 3 num
初項1, 公差2の等差数列, 今は4番目で値は9.
初項0, 公差3の等差数列, 今は4番目で値は12.
初項2, 公差0の等差数列, 今は4番目で値は2.
OK:1 1 get
OK:0 0 get
OK:2 2 get
OK:0 0 num
OK:0 0 num
OK:0 0 num
初項1, 公差2の等差数列, 今は0番目で値は1.
初項0, 公差3の等差数列, 今は0番目で値は0.
初項2, 公差0の等差数列, 今は0番目で値は2.
OK:10 10 getDiff
OK:-4 -4 getDiff
OK:1 1 get
OK:0 0 get
OK:11 11 get
OK:-4 -4 get
OK:21 21 get
OK:-8 -8 get

考察:
メモリの割り当てにはmallocという関数を用いたが,これについて調べ
てみるとcallocとreallocというものもあることが分かった.callocは
確保する長さを区画とその個数の2つの引数で与え,割り当てた後全領
域を0で初期化するもので,reallocはすでに割り当てられた領域を指す
ポインタと長さを指定して,再割り当てを行うものと書かれていた.

また,mallocを使えば広い領域も割り当てられるので,どれくらい割り
当てが可能かを調べるために,次のプログラムを作成した

#include<stdio.h>
#include<stdlib.h>
int main(){
    int count=0;
    while(1){
        int *a=malloc(count*1024*1024);
        if(a==0){
            break;
        }
        count++;
        printf("success %d MB",count);
    }
}

これは,whileループで割り当てる領域を1MBずつ徐々に増やしていくと
いうものである.実行結果は,count=2024で終了したので,2GBが限界
だということが分かった.
 
演習3において,実際に情報の隠蔽ができているかを確かめるために,
main関数内でs1->valueのようにアクセスしようと試みたが,コンパイ
ル時にエラーになったため,オブジェクト指向でいうprivateのように
情報隠蔽ができていると言える.
また,cdseq.hの中で,struct cdseq *r = (struct
cdseq*)malloc(sizeof(struct cdseq));という文があるが,
sizeof(struct cdseq))の値はどうなるのか,今回の演習でstruct
cdseqに新しくフィールドを追加したがこれはこのサイズに変化を与え
るのか,などを疑問に思ったため,次のプログラムを作成し実行した.

#include<stdio.h>
#include<stdlib.h>
struct test1{
    int a;
    int b;
};

struct test2{
    int a;
    int b;
    int c;
};
struct test3{
    int a;
    int b;
    int c;
    int d;
};
int main(){
    struct test1 t1;
    struct test2 t2;
    struct test3 t3;
    printf("test1.length=%ld\n",sizeof(struct test1));
    printf("t1.length=%ld\n",sizeof(t1));
    printf("test2.length=%ld\n",sizeof(struct test2));
    printf("t2.length=%ld\n",sizeof(t2));
    printf("test3.length=%ld\n",sizeof(struct test3));
    printf("t3.length=%ld\n",sizeof(t3));
}
これは,test1~3の3つのフィールドの数が異なる構造体を作成し,その
メモリ上のサイズを表示するものである.
    
実行結果:

$ ./structLength.out 
test1.length=8
t1.length=8
test2.length=12
t2.length=12
test3.length=16
t3.length=16

この結果から,構造体のサイズはフィールドのそれぞれのサイズの和に
なっていることがわかる.なお,書籍で調べると必ずしもそうではなく,
フィールド同士の間などに使われないアドレスが入り,サイズの和以上
になることもあるという.兎に角,構造体のメモリ上のサイズはフィー
ルドによることが分かったので,オブジェクト指向の継承のような機能
を構造体で実装しようとするときは気をつけなければならないと考えた.
参考文献:
 河西朝雄,「C言語標準文法」,技術評論社,2011.

アンケート
Q1.アドレス,ポインタを使うプログラムで注意すべきことは何だと思いますか.
 そのポインタが何を指しているかや,何のために宣言しているのかな
どをわかりやすくして,コードを書きやすく,わかりやすくすることだ
と思う.

Q2.ここまでのところで,プログラムを作るときに重要だが自分で身に
付いていないと思うことは何ですか.

可読性をもたせることが自分には足りていないと思う.今は意識してコ
メントを書いたり改行,空行,スペースを入れているがこれを意識せず
とも自然にできるようにしたい.実際に自分が書いたコードであるにも
かかわらず1周間後には読めなくなるということが多いので気をつけた
い.

Q3.リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ.
今回の課題で,ポインタの扱いやそれを応用したデータの管理・隠蔽に
ついて分かった.難しい考え方だと思ったが,Cで文字列を扱ったり,
カプセル化を行うときにうまく使うと賢くコードが書けるということが
よく分かったので,活用できるようにしたい.

m1910640 2b:○ Mon May 25 09:42:19 2020


プログラミング通論 #2
提出日付:5月25日

[課題の再掲]
演習1-c 何らかの整列アルゴリズムで配列を昇順に整列する関数void
iarray_sort(int *a, int n)を作成する。

[実施したこととその結果]
この課題で作ったプログラムは下である。
#include <stdio.h>

#define ARRAY_SIZE 5
#define DEBUG

void iarray_sort(int *a, int n); int expect_iarray(int *a, int *b, int c, char *msg);

int main(void){
    int array[ARRAY_SIZE] = {4, 6, 3, 9, 7};
    iarray_sort(array, ARRAY_SIZE);
    #ifdef DEBUG
    int a[6] = {70, 66, 54, 4, 91, 76}, b[6] = {91, 76, 70, 66, 54, 4};
    expect_iarray(a, b, 6, "70 66 54 4 91 76 -> 91 76 70 66 54 4");
    int a1[5] = {66, 54, 4, 91, 76}, b1[5] ={91, 76, 66, 54, 4};
    expect_iarray(a1, b1, 5, "66 54 4 91 76 -> 91 76 66 54 4");
    int a2[4] = {54, 4, 91, 76}, b2[4] = {91, 76, 54, 4};
    expect_iarray(a2, b2, 4, "54 4 91 76 -> 91 76 54 4");
    int a3[8] = {39, 48, 93, 70, 8, 19, 7, 45}, b3[8] = {93, 70, 48, 45, 39, 19, 8, 7};
    expect_iarray(a3, b3, 8, "39 48 93 70 8 19 7 45 -> 93 70 48 45 39 19 8 7");
    int a4[7] = {48, 93, 70, 8, 19, 7, 45}, b4[7] = {93, 70, 48, 45, 19, 8, 7};
    expect_iarray(a4, b4, 7, "48 93 70 8 19 7 4 -> 93 70 48 45 19 8 7");
    int a5[6] = {93, 70, 8, 19, 7, 45}, b5[6] = {93, 70, 45, 19, 8, 7};
    expect_iarray(a5, b5, 6, "93 70 8 19 7 -> 93 70 45 19 8 7");
    #endif
    return 0;
}

void iarray_sort(int *a, int n){int stock;
    for(int i = 0; i < n; i++){
        for(int x = 0; x < n-1; x++){
            if(a[x]<a[x+1]){
                stock = a[x]; a[x] = a[x+1];
                a[x+1] = stock;
            }
        }
    }
    for(int i = 0; i < n; i++){
        printf("%d ", a[i]);
    }
    printf("\n");
}

int expect_iarray(int *a, int *b, int c, char *msg){
    printf("ideal   :");printf("%s\n", msg);
    printf("expected:");
    for(int i = 0; i < c; i++){
        printf("%d ", b[i]);
    }
    printf("\n");
    printf("result  :");iarray_sort(a, c);
    for(int i = 0; i < c; i++){
        if(a[i]!=b[i]){
            printf("NG\n");
            return 0;
        }
    }
    printf("OK\n");
    return 0;
}

このプログラムでは冒頭に用意する関数を置いてmain関数を書いた後に
先頭で宣言した関数をそれぞれ書き込んだ。
まず、iarray_sortについては配列と、その配列数を代入することによっ
てバブルソートを行った。このとき、二連続の配列の値について大小比
較し、二つ目の値が大きい場合は書き換えるために、一つ目の値を
stockに代入し二つ目の値で上書きする。その後、stockを二つ目の値が
ある場所に代入する。これを配列の大きさの分だけ繰り返す。
次に、expect_iarrayは配列aと配列bとこれらの配列数と文字列を代入
する。aはバブルソートを行う前の配列。bはバブルソートを行った後の
理想的な配列である。msgの文字列は変換の流れを表したものである。
プログラムの中身にはmsgによる理想的変換を出力した後に理想的な結
果idealと実際にiarray_sortを使って得られた結果resultを表示し、そ
れらの配列が一致するかどうかを確認する。一致するときはOKを出力、
一致しないときはNGを出力する。
main関数では具体的な配列の代入値についてexpect_iarrayを実行し、
テストケースも作った。テストケースはDEBUGの有無によって表示を切
り替えられるようにした。

このプログラムを実際に動かした結果は下である。
9 7 6 4 3
ideal   :70 66 54 4 91 76 -> 91 76 70 66 54 4
expected:91 76 70 66 54 4
result  :91 76 70 66 54 4
OK
ideal   :66 54 4 91 76 -> 91 76 66 54 4
expected:91 76 66 54 4
result  :91 76 66 54 4
OK
ideal   :54 4 91 76 -> 91 76 54 4
expected:91 76 54 4
result  :91 76 54 4
OK
ideal   :39 48 93 70 8 19 7 45 -> 93 70 48 45 39 19 8 7
expected:93 70 48 45 39 19 8 7
result  :93 70 48 45 39 19 8 7
OK
ideal   :48 93 70 8 19 7 4 -> 93 70 48 45 19 8 7
expected:93 70 48 45 19 8 7
result  :93 70 48 45 19 8 7
OK
ideal   :93 70 8 19 7 -> 93 70 45 19 8 7
expected:93 70 45 19 8 7
result  :93 70 45 19 8 7
OK


[考察]
上手く行かなかったテストケース
#ifdef DEBUG
    int a[6] = {70, 66, 54, 4, 91, 76}, b[6] = {91, 76, 70, 66, 54, 4},
    B1[5] = {91, 76, 66, 54, 4}, B2[4] = {91, 76, 54, 4};
    expect_iarray(a, b, 6, "70 66 54 4 91 76 -> 91 76 70 66 54 4");
    expect_iarray(a+1, B1, 5, "66 54 4 91 76 -> 91 76 66 54 4");
    expect_iarray(a+2, B2, 4, "54 4 91 76 -> 91 76 54 4");
    int a1[8] = {39, 48, 93, 70, 8, 19, 7, 45}, b1[8] = {93, 70, 48, 45, 39, 19, 8, 7},
    B11[7] = {93, 70, 48, 45, 19, 8, 7}, B12[6] = {93, 70, 45, 19, 8, 7};
    expect_iarray(a1, b1, 8, "39 48 93 70 8 19 7 45 -> 93 70 48 45 39 19 8 7");
    expect_iarray(a1+1, B11, 7, "48 93 70 8 19 7 4 -> 93 70 48 45 19 8 7");
    expect_iarray(a1+2, B12, 6, "93 70 8 19 7 -> 93 70 45 19 8 7");
#endif
このテストケースではあらかじめ用意した配列にかんしてaをa+1と表現
することによって無駄に配列を用意しないで進められるようにしたかっ
た。しかし、このプログラムのexpect_iarrayでは代入された配列の中
身を入れ替えてしまう。ゆえに、二つ目のexpect_iarrayを実行すると
きにはすでに一つ目のexpect_iarrayによって順番が変わってしまった
配列を代入してしまっていた。よって、このexpect_iarrayのテストケー
スは毎回配列を定義して代入しなくてはいけなかった。この改善策とし
て、expect_iarray内に代入された配列と同じ大きさの配列を用意し、
それに代入された配列をコピーしてバブルソートを行えば上記のテスト
ケースでもうまくいくはずであると考えた。

実際にこの考えて修正したiarray_expectは下となった。
int expect_iarray(int *a, int *b, int c, char *msg){
    int copy[c];
    printf("ideal   :");printf("%s\n", msg);
    printf("expected:");
    for(int i = 0; i < c; i++){
        printf("%d ", b[i]);
        copy[i] = a[i];
    }
    printf("\n");
    printf("result  :");iarray_sort(copy, c);
    for(int i = 0; i < c; i++){
        if(copy[i]!=b[i]){
            printf("NG\n");
            return 0;
        }
    }
    printf("OK\n");
    return 0;
}
copy配列によってaは変化することなくテストケースも正しく動いた。


[課題の再掲]
演習2a 二つの配列を受け取り、両方の列の内容を交互に並べた列を返す。

[実施したこととその結果]
この課題で作ったプログラムは下である。
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define DEBUG

void iarray_read(int *a, int n){
    for(int i = 0; i < n; ++i){
        printf("%d> ", i+1); scanf("%d", a+i);
    }
}
void iarray_print(int *a, int n){
    for(int i = 0; i < n; ++i){ printf(" %2d", a[i]);}
    printf("\n");
}
int *ivec_new(int size){
    int *a = (int*)malloc((size+1) * sizeof(int));
    a[0] = size; return a;
}
void ivec_read(int *a){ iarray_read(a+1, a[0]); }
void ivec_print(int *a) { iarray_print(a+1, a[0]); }
bool iarray_equal(int *a, int *b, int n){
    for(int i = 0; i < n; ++i){
        if(a[i] != b[i]){return false;}
    }
    return true;
}
void expect_iarray(int *a, int *b, int n, char *msg){
    printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
    iarray_print(a, n); iarray_print(b, n);
}
int *alternation(int *a, int *b){
    int *c = ivec_new(a[0]+b[0]);
    c[0] = a[0]+b[0];
    int j = 1;
    for(int i = 1; i <= a[0]+b[0]; i++){
        if(i<=a[0]){
            c[j] = a[i];j++;
        }
        if(i<=b[0]){
            c[j] = b[i];j++;
        }
    }
    return c;
}

int main(void){
    int *a, *b, *c;int a_size, b_size;
    printf("This program output contents of two array alternately.\n");
    printf("Enter first array size.>");scanf("%d", &a_size);
    printf("Enter second array size.>");scanf("%d", &b_size);
    printf("Enter the contents of first array.\n");
    a = ivec_new(a_size); ivec_read(a);
    printf("Enter the contents of second array.\n");
    b = ivec_new(b_size); ivec_read(b);
    c = alternation(a, b); ivec_print(c);
    free(a);free(b);free(c);
    #ifdef DEBUG
    int a1[] = {5, 1, 3, 5, 7, 9}, b1[] = {5, 2, 4, 6, 8, 10},
    c1[] = {10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    expect_iarray(alternation(a1, b1), c1, 11, "5 1 3 4 5 7 9 + 5 2 4 6 8 10 -> 10 1 2 3 4 5 6 7 8 9 10");
    int a2[] = {5, 81, 59, 53, 59, 30}, b2[] = {5, 92, 29, 1, 41, 86},
    c2[] = {10, 81, 92, 59, 29, 53, 1, 59, 41, 30, 86};
    expect_iarray(alternation(a2, b2), c2, 11, "5 81 59 53 59 30 + 5 92 29 1 41 86 -> 10 81 92 59 29 53 1 59 41 30 86");
    int a3[] = {4, 81, 59, 53, 59}, b3[] = {6, 30, 92, 29, 1, 41, 86},
    c3[] = {10, 81, 30, 59, 92, 53, 29, 59, 1, 41, 86};
    expect_iarray(alternation(a3, b3), c3, 11, "4 81 59 53 59 + 6 30 92 29 1 41 86 -> 10 81 30 59 92 53 29 59 1 41 86"); 
    int a4[] = {6, 81, 59, 53, 59, 30, 92}, b4[] = {4, 29, 1, 41, 86}, 
    c4[] = {10, 81, 29, 59, 1, 53, 41, 59, 86, 30, 92};
    expect_iarray(alternation(a4, b4), c4, 11, "6 81 59 53 59 30 92 + 4 29 1 41 86 -> 10 81 29 59 1 53 41 59 86 30 92");
    int a5[] = {0}, b5[] = {2, 1, 2},
    c5[] = {2 ,1, 2};
    expect_iarray(alternation(a5, b5), c5, 3, "0 + 2 1 2 -> 2 1 2");
    #endif
    return 0;
}

このプログラムでは配列を読み取り出力するためのiarray_read、
iarray_print、ivec_new、ivec_read、ivec_printを用意した。また、
二つの配列が等しいかどうかを調べるexpect_iarrayと二つの配列の中
身を交互にして合わせるalternationを用意した。
alternationには配列先頭が配列に入っている数字の数を表していると
する。alternationでは配列cを用意し、代入した配列a,bの先頭を足し
合わせてcの配列数とする。そして、a,bの配列の中身を交互にcに代入
していく。なお、a,bは配列の先頭の数字の数だけ交互に代入していき、
足りなくなったら代入しないような条件文を付けた。最後に文字列cを
出力するようにした。
次にmain関数では二つの文字を自由に入力できるようにした。二つの配
列を入力した後に交互に組み合わせて出力した後にメモリを解放。その
後にはテストケースを用意した。

実際にこのプログラムを実行した結果は下である。
This program output contents of two array alternately.
Enter first array size.>0
Enter second array size.>0
Enter the contents of first array.
Enter the contents of second array.

This program output contents of two array alternately.
Enter first array size.>3
Enter second array size.>3
Enter the contents of first array.
1> 1
2> 2
3> 3
Enter the contents of second array.
1> 1
2> 2
3> 3
  1  1  2  2  3  3

~テストケース~
OK 5 1 3 4 5 7 9 + 5 2 4 6 8 10 -> 10 1 2 3 4 5 6 7 8 9 10
 10  1  2  3  4  5  6  7  8  9 10
 10  1  2  3  4  5  6  7  8  9 10
OK 5 81 59 53 59 30 + 5 92 29 1 41 86 -> 10 81 92 59 29 53 1 59 41 30 86
 10 81 92 59 29 53  1 59 41 30 86
 10 81 92 59 29 53  1 59 41 30 86
OK 4 81 59 53 59 + 6 30 92 29 1 41 86 -> 10 81 30 59 92 53 29 59 1 41 86
 10 81 30 59 92 53 29 59  1 41 86
 10 81 30 59 92 53 29 59  1 41 86
OK 6 81 59 53 59 30 92 + 4 29 1 41 86 -> 10 81 29 59 1 53 41 59 86 30 92
 10 81 29 59  1 53 41 59 86 30 92
 10 81 29 59  1 53 41 59 86 30 92
OK 0 + 2 1 2 -> 2 1 2
  2  1  2
  2  1  2


[考察]
freeでメモリ解放をした後、すでにint型として定義してあるaに値を入
れなおすことによってテストケースを行おうとしたが、うまくいかなかっ
た。これより配列は一度初期化したらメモリ解放しても配列を代入し直
すことはできないことがわかった(例としてint a1をaとして代入する等)。
また、このプログラムは入力する配列において配列に入れる数字の個数
を配列の先頭に入れなければいけない。このような仕様は最初に宣言す
るようにすべきだと感じた。
また、配列に含む数字の数を入れる仕様になっているので配列に何も入っ
ていない場合にエラーが起きそうだが、そういった例外はこの配列の仕
様によって防げているのでとても良い点だと思った。

[アンケート]
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。
1文字を変数に代入するときはchar aとポインタ変数にしなくてよいが、
文字列を変数に代入するときにはchar *aのようにポインタ指定しなけ
ればならない。
Q2. ここまでのところで、プログラムを作るときに重要だが身について
いないと思うことは何ですか。
アルゴリズムを作るうえで計算速度は大事であるが、自分のプログラム
は余分な所が多いのでプログラムをコンパクトにまとめるのができてい
ません。
Q3. リフレクション・感想・要望
ある程度ポインタはわかってきたのでこれからもっと練習します。

m1910646 2b:○ Mon May 25 19:05:57 2020


1910646
「個人作業」
5/25
[課題1]
演習1-a
[課題の再掲]
配列の最大値を求める関数int iarray_maxを作成し、単体テストを動かす。
[プログラムのソース]
#include <stdio.h>

void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
int iarray_max(int *a, int n){
  int max = a[0];
  for (int i = 0; i < n; ++i) {
    if(max < a[i]) {
      max = a[i];
    }
  }
  return max;
}
int main(void) {
  int a[5], max;
  iarray_read(a, 6);
  max = iarray_max(a, 6);
  printf("max is %d ", max);
  return 0;
}

[実行例]
1> 9
2> 4
3> 3
4> 2
5> -1
6> 4
max is 9

1> -1
2> -4
3> -2
4> 0
5> -4
6> -9
max is 0

[単体テスト]
#include <stdio.h>

void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
int iarray_max(int *a, int n){
  int max = a[0];
  for (int i = 0; i < n; ++i) {
    if(max < a[i]) {
      max = a[i];
    }
  }
  return max;
}
void expect_int(int i1, int i2, char *msg) {
  printf("%s %d:%d %s\n", (i1==i2)?"OK":"NG", i1, i2, msg);
}
int main(void) {
  int a[] = {9,0,0,1,2,3}, b[] = {-1,-3,-2,-4,-1}, c[] = {4, 1, 1, 1};
  expect_int(iarray_max(a, 6), 9, "9 0 0 1 2 3");
  expect_int(iarray_max(a+1, 5), 3, "0 0 1 2 3");
  expect_int(iarray_max(b, 5), -1, "-1 -3 -2 -4 -1");
  expect_int(iarray_max(b+1, 4), -1, "-3 -2 -4 -1");
  expect_int(iarray_max(c, 4), 4, "4 1 1 1");
  expect_int(iarray_max(c+1, 3), 1, "1 1 1");
  return 0;
}

[単体テストの実行例]
OK 9:9 9 0 0 1 2 3
OK 3:3 0 0 1 2 3
OK -1:-1 -1 -3 -2 -4 -1
OK -1:-1 -3 -2 -4 -1
OK 4:4 4 1 1 1
OK 1:1 1 1 1

[説明]
まず配列の初項a[0]の値をmaxに代入する。for文を用いてiを0から配列
の要素数-1まで1づつ足していき、a[i]がmaxよりも大きくなった時に
maxにa[i]を代入することで、for文が完了したときにmaxに配列の最大
値が代入してある状況にした。

[考察]
はじめはmain関数を
int main(void) {
  int a[5], max;
  iarray_read(a, 6);
  iarray_max(a, 6);
  printf("max is %d ", max);
  return 0;
}
としており、常に配列の最終項を返すだけの関数になってしまった。こ
の問題はmax = iarray_max(a, 6);とすることで解決したが、なぜこの
ようにしなかった場合常に配列の最終項を返すのだろうと思った。

[課題2]
演習2-a
[課題の再掲]
二つの配列を交互にして返すプログラムを作成し単体テストを実行する。
[プログラムのソース]
#include <stdio.h>
#include <stdlib.h>
void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
void ivec_read(int *a) { iarray_read(a+1, a[0]); }
void ivec_print(int *a) { iarray_print(a+1, a[0]); }
int *ivec_alternation(int *a, int *b) {
  int *c = ivec_new(a[0]+b[0]);
  for(int i = 1; i <= a[0]; ++i) {
    c[2*i - 1] = a[i];
  }
  for(int j = 1; j <=b[0]; ++j) {
    c[2*j] = b[j];
  }
  return c;
}
int main(void) {
  int *a, *b, *c;
  a = ivec_new(3); ivec_read(a);
  b = ivec_new(2); ivec_read(b);
  c = ivec_alternation(a, b); ivec_print(c);
  free(a); free(b); free(c);
  return 0;
}

[実行例]
1> 1
2> 2
3> 3
1> 4
2> 5
  1  4  2  5  3

1> 3
2> -2
3> 43
1> 2
2> 0
  3  2 -2  0 43

[単体テスト]
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
void ivec_print(int *a) { iarray_print(a+1, a[0]); }
int *ivec_alternation(int *a, int *b) {
  int *c = ivec_new(a[0]+b[0]);
  for(int i = 1; i <= a[0]; ++i) {
    c[2*i - 1] = a[i];
  }
  for(int j = 1; j <=b[0]; ++j) {
    c[2*j] = b[j];
  }
  return c;
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
  int a[] = {3,1,2,3}, b[] = {3,4,5,6}, c[] = {6,1,4,2,5,3,6};
  int *p = ivec_alternation(a, b);
  expect_iarray(p, c, 7, "[1,2,3]+[4,5,6]=[1,4,2,5,3,6]");
  int d[] = {4,9,8,2,3}, e[] = {3,5,6,3}, f[] = {7,9,5,8,6,2,3,3};
  int *q = ivec_alternation(d,e);
  expect_iarray(q, f, 8, "[9,8,2,3]+[5,6,3]=[9,5,8,6,2,3,3]");
  int g[] = {2,1,1}, h[] = {2,1,1}, i[] = {4,1,1,1,1};
  int *r = ivec_alternation(g,h);
  expect_iarray(r, i, 5, "[1,1]+[1,1]=[1,1,1,1]");
  return 0;
}

[単体テストの実行例]
OK [1,2,3]+[4,5,6]=[1,4,2,5,3,6]
  6  1  4  2  5  3  6
  6  1  4  2  5  3  6
OK [9,8,2,3]+[5,6,3]=[9,5,8,6,2,3,3]
  7  9  5  8  6  2  3  3
  7  9  5  8  6  2  3  3
OK [1,1]+[1,1]=[1,1,1,1]
  4  1  1  1  1
  4  1  1  1  1

[説明]
iに関するfor文では配列cの2i-1番目、すなわち1から始まる奇数番目の
要素を配列aのi番目の要素に対応させた。jを関するfor文では、cの2か
ら始まる偶数番目の要素に配列bのj番目の要素を対応させた。このよう
にすることで配列cは配列aと配列bを交互にしたものになる。

[考察]
はじめはalternation内のfor文をまとめて
int *ivec_alternation(int *a, int *b) {
  int *c = ivec_new(a[0]+b[0]);
  for(int i = 1; i <= a[0]+b[0]; ++i) {
    c[2*i - 1] = a[i];
    c[2*i] = b[i];
  }
  return c;
}

としていた。このプログラム自体は問題なく動かせたが、単体テストを
するとコンパイルまではできるが実行することができなかった。この問
題はfor文を二つに分けたら解決したが、元のプログラムも問題なく配
列を交互にして出力できるのに単体テストを実行できなくなるのはなぜ
だろうと思った。

[アンケートの回答]
Q1. アドレスを値として扱えること。

Q2. アドレスについての知識

q3. 授業内容が難しく感じたので、もう一度ポインタやアドレスについ
て勉強しなおそうと思った。

n1910483 2b:○ Mon May 25 05:11:45 2020


学籍番号:1910483
個人作業

今回の課題:可変長配列を用いた配列の操作を行う。

<課題2-b:入力配列を逆順にして返す>

コード)
int *ivec_concat(int *a) {
  int *c = ivec_new(a[0]);
  for(int i = 1; i <= (a[0]/2)+1 ; i+=1) { c[i] = a[a[0]-i+1]; c[a[0]-i+1] = a[i];}
  return c;
}

テストコード)
  int a[] = {3,1,2,3}, b[] = {4,4,2,3,1}, c[] = {}, d[] = {3,3,2,1}, e[]={4,1,3,2,4},f[] = {};
  int *p = ivec_concat(a);
  int *q = ivec_concat(b);
  int *r = ivec_concat(c);
  expect_iarray(p, d, 4, "[1,2,3]=>[3,2,1]");
  expect_iarray(q, e, 5, "[4,2,3,1]=>[1,3,2,4]");
  expect_iarray(r, f, 0, "[]=>[]");

実行例)
OK [1,2,3]=>[3,2,1]
  3  3  2  1
  3  3  2  1
OK [4,2,3,1]=>[1,3,2,4]
  4  1  3  2  4
  4  1  3  2  4
OK []=>[]

このプログラムでは、入力した配列に対し、その逆順を出力するプログ
ラムである。配列の要素数を表すa[0]の半分の回数だけ、配列の前半と
後半の要素を入れ替えることによって、入力した配列の逆順となる配列
を出力している。

テストケースに関しては、配列の要素数が奇数のケース、偶数のケース、
何もないケースの3パターンで試行している。奇数の配列を逆順にする
とき、中央の要素が入れ替わらないため、それに適応しているか確認す
るためである。

とはいえ、このプログラムは配列a(入力配列)と配列c(出力配列)と
で配列の要素数に変化はないし、複数の配列の要素を用いているわけで
はないので、mallocを用いて領域を新たに割り当てたあとにそこに出力
するメリット自体は薄いと考えられる。逆に、複数のオプションを施す
のなら仕様用途があると推測される。

例えば、テキストに指定されている複数の配列を合わせてソートしたあ
とに、逆順(降順)にして閲覧する、等の仕様用途で用いることができ
る。

<課題2-c:入力配列を昇順にソートする>
コード)
int *ivec_concat(int *a) {
  int *c = ivec_new(a[0]),i,j,k,memo;
  for( i = 1; i <= a[0] ; i+=1){ //i番目を選定
    for( j = i+1; j <= a[0] ; j+=1) { //a[j]で比較対称となるa[i]以降の数字を確定
      if(a[i] > a[j]){ //a[i]と比較し、小さいものが入れ替わる
	memo = a[i];
	a[i] = a[j];
	a[j] = memo;
      }
    }
  }
  for(k = 1; k <= a[0];k+=1 ){
    c[k] = a[k];
  }
  return c;
}

テストコード)
  int a[] = {3,3,2,1}, b[] = {4,4,-2,3,1}, c[] = {}, d[] = {3,1,2,3}, e[]={4,-2,1,3,4},f[] = {};
  int *p = ivec_concat(a);
  int *q = ivec_concat(b);
  int *r = ivec_concat(c);
  expect_iarray(p, d, 4, "[3,2,1]=>[1,2,3]");
  expect_iarray(q, e, 5, "[4,-2,3,1]=>[-2,1,3,4]");
  expect_iarray(r, f, 0, "[]=>[]");

実行例)
OK [3,2,1]=>[1,2,3]
  3  1  2  3
  3  1  2  3
OK [4,-2,3,1]=>[-2,1,3,4]
  4 -2  1  3  4
  4 -2  1  3  4
OK []=>[]

このプログラムでは、入力配列を昇順にソートするプログラムである。
比較する要素a[i]を決め、その比較対象となるa[j]と比較し、a[j]の方
が小さかったらa[i]とa[j]を入れ替えることで、配列のソートを行って
いる。

テストコードでは、自然数のソート、負の数を含む配列のソート、何も
ない配列のソートで試行している。テスト結果から負の数が含まれてい
ても問題なくソートできていることがわかる。

このコードの仕様として、予めaの配列で入れ替えておき、入れ替わっ
たものを配列cにコピーするといった手法をとっている。このため、配
列自体の長さは変らず、やはり可変長配列を用いなくても事足りると考
えられる。可変長配列が重宝するのは、次のような場合である。


<課題2-d:2つの入力配列を合わせて、1つの配列に昇順にソートして入れる>
int *ivec_concat(int *a,int *b) {
  int *c = ivec_new(a[0]+b[0]),i=1,j=1,k=1,l,m,n=0;
  // a sort
    while(i <= a[0] && j <= b[0]){
      //bの先頭と比較 小さい方をcに格納
      if(a[i] < b[j]){
	c[k] = a[i];
	i = i + 1;
	k = k + 1;
      }else{
	c[k] = b[j];
	j = j + 1;
	k = k + 1;
      }
    }
    if(j != b[0] + 1){
      for(l=j;l<=b[0];++l){
	c[n+k] = b[l];
	n = n + 1;
      }
    }else if(i != a[0] + 1){
      for(m=i;m<=a[0];++m){
	c[n+k] = a[m];
	n = n + 1;
      }
    }
    return c;
}

テストコード)
  int a[] = {3,1,3,5}, b[] = {3,2,4,6}, c[] = {3,1,4,6}, d[] = {4,2,3,5,7}, e[]={5,-3,-2,1,3,4},f[] = {4,0,2,5,6},g[]={},h[]={};
  int i[] = {6,1,2,3,4,5,6}, j[] = {7,1,2,3,4,5,6,7}, k[] = {9,-3,-2,0,1,2,3,4,5,6},l[]={};
  int *p = ivec_concat(a,b);
  int *q = ivec_concat(c,d);
  int *r = ivec_concat(e,f);
  int *s = ivec_concat(g,h);
  expect_iarray(p, i, 7, "[1,3,5] + [2,4,6]=>[1,2,3,4,5,6]");
  expect_iarray(q, j, 8, "[1,4,6] + [2,3,5,7]=>[1,2,3,4,5,6,7]");
  expect_iarray(r, k, 10, "[-3,-2,1,3,4] + [0,2,5,6]=>[-3,-2,0,1,2,3,4,5,6]");
  expect_iarray(s, l, 0, "[]+[]=>[]");

実行例)
OK [1,3,5] + [2,4,6]=>[1,2,3,4,5,6]
  6  1  2  3  4  5  6
  6  1  2  3  4  5  6
OK [1,4,6] + [2,3,5,7]=>[1,2,3,4,5,6,7]
  7  1  2  3  4  5  6  7
  7  1  2  3  4  5  6  7
OK [-3,-2,1,3,4] + [0,2,5,6]=>[-3,-2,0,1,2,3,4,5,6]
  9 -3 -2  0  1  2  3  4  5  6
  9 -3 -2  0  1  2  3  4  5  6
OK []+[]=>[]


このプログラムでは、入力された2つのソート済みの配列を、まとめて
1つの配列にソートして出力するプログラムである。2つの配列の先頭
の数同士を比較して、小さい方を配列cに入れる、といった手法をとっ
ている。

テストケースでは2つの配列がそれぞれ、2つの配列の長さが同じパター
ン、異なるパターン2つとしている。それぞれのパターンで問題なくソー
トできていることがわかる。

このプログラムでは入力配列の要素数と出力配列の要素数が一致してい
るとは限らないため、可変長配列が重宝する。以上のように、配列の要
素数が柔軟に変わる場合に、こういったプログラムが重宝すると考えら
れる。

例えば、課題4の複数の配列のソートで例を挙げると、複数のデータを1
つの集合に纏めたい場合等が考えられる。このように、実際の応用ケー
スを考えながらプログラムを作成することをしていきたい。

<アンケート>
Q1.モデルとなるケース等を用いる等をして、実際に何が起こっている
のか把握しながらプログラムを作成すること

Q2.↑このようなこと。すなわちプログラムに対する想像力

Q3.今回はテストコードも沿えたプログラムとなったが、想定ケースが
不十分なのではと不安が残る。様々なパターンを想定できるよう心掛け
たい。

n1910485 2b:○ Sat May 23 15:54:11 2020


提出日時:5/22

①演習2a
・課題の再掲
2つの列を交互に並べた列を返すプログラムを作る。
・ソースコード
#include <stdio.h>
#include <stdlib.h>
void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
void ivec_read(int *a) { iarray_read(a+1, a[0]); }

void ivec_print(int *a) { iarray_print(a+1, a[0]); }

int *ivec_mutual(int *a, int *b) {
  int *c = ivec_new(a[0]+b[0]);
  int i;
  int min = a[0];
  int max = b[0];
  if(a[0]>b[0]){min=b[0];max=a[0];}
  for(i=1;i<=min;i++){
    c[2*i-1]=a[i];
    c[2*i]=b[i];
  }
  for(i=min+1;i<=max;i++){
    if(a[0]<b[0]){c[min+i]=b[i];}
    else if(a[0]>b[0]){c[min+i]=a[i];}
  }
  return c;
}

int main(void) {
  int *a, *b, *c;
  a = ivec_new(6); ivec_read(a);
  b = ivec_new(2); ivec_read(b);
  c = ivec_mutual(a,b); ivec_print(c);
  free(a); free(b); free(c);
  return 0;
}
・単体テストのソースコード
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}

void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}

bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i <= n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}

void ivec_read(int *a) { iarray_read(a+1, a[0]); }

void ivec_print(int *a) { iarray_print(a+1, a[0]); }

int *ivec_mutual(int *a, int *b) {
  int *q = ivec_new(a[0]+b[0]);
  int i;
  int min = a[0];
  int max = b[0];
  if(a[0]>b[0]){min=b[0];max=a[0];}

for(i=1;i<=min;i++){
q[2*i-1]=a[i];
q[2*i]=b[i];
}
for(i=min+1;i<=max;i++){
if(a[0]>b[0]){q[i+min]=a[i];}
if(a[0]<b[0]){q[i+min]=b[i];}
}
  return q;
}

void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  ivec_print(a); ivec_print(b);
}

int main(void) {
  int a[] = {3,1,2,3},b[] = {2,4,5}, c[] ={5,1,4,2,5,3};
  expect_iarray(ivec_mutual(a,b),c,5,"1 4 2 5 3");
  int a1[] = {8,6,5,2,8,0,2,4,7},b1[] = {5,1,5,3,0,4}, c1[] ={13,6,1,5,5,2,3,8,0,0,4,2,4,7};
  expect_iarray(ivec_mutual(a1,b1),c1,13,"6 1 5 5 2 3 8 0 0 4 2 4 7");
  int a2[] = {3,-1,3,5},b2[] = {3,0,0,0}, c2[] ={6,-1,0,3,0,5,0};
  expect_iarray(ivec_mutual(a2,b2),c2,6,"-1 0 3 0 5 0");
  return 0;
}

・実行結果
[n1910485@sol ~/prg]$ ./a.out
1> 1
2> 0
3> 1
4> 01^H
5> 6> 1> 2>   1  0  0  0  1  1  0  0
[n1910485@sol ~/prg]$
[n1910485@sol ~/prg]$
[n1910485@sol ~/prg]$ ./a.out
1> 1
2> 0
3> 1
4> 0
5> 1
6> 0
1> 0
2> 1
  1  0  0  1  1  0  1  0
・単体テストの結果
OK 1 4 2 5 3
  1  4  2  5  3
  1  4  2  5  3
OK 6 1 5 5 2 3 8 0 0 4 2 4 7
  6  1  5  5  2  3  8  0  0  4  2  4  7
  6  1  5  5  2  3  8  0  0  4  2  4  7
OK -1 0 3 0 5 0
 -1  0  3  0  5  0
 -1  0  3  0  5  0

・プログラムの説明
交互に列を並べるプログラム。まず、2つの列a,bのa[0]を除いた要素数
を取得し、その大きさの和の数だけデータの入っている可変長配列cを
ivec_newにより取得する。そして、a[0],b[0]の小さい方をmin、大きい
方をmaxとする。c[1]からc[2*min]までは奇数番目にaの要素を、偶数番
目にbの要素を順に入れていく。残りには配列の長さが長い方の残りを
そのまま入れる。

・考察
今回は列の長さを6,2としたが、ivec_new(n)とすれば長さnの列につい
ても同様のことができる。また、aを奇数番目、bを偶数番目というのは
逆でもよいが、その場合は返ってくる配列も違うので、この演習問題に
ついては複数通りの配列が考えられる。
単体テストでは、列の長さを変えたり、負の数も入れてエラーが出ない
ことを確認している。

②演習2b
・課題の再掲
入力された列とは逆順の列を返す。
・ソースコード
#include <stdio.h>
#include <stdlib.h>
void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
void ivec_read(int *a) { iarray_read(a+1, a[0]); }

void ivec_print(int *a) { iarray_print(a+1, a[0]); }

int *ivec_rev(int *a){
  int *b = ivec_new(a[0]);
  int s = a[0];
  b[0] = s;
  int i;
  for(i=1;i<=s;i++){b[i]=a[s-i+1];}
  return b;
}

int main(void) {
  int *a, *b;
  a = ivec_new(5); ivec_read(a);
  b = ivec_rev(a); ivec_print(b);
  free(a); free(b);
  return 0;
}

・単体テストのソースコード
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}

void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}

bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i <= n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}

void ivec_read(int *a) { iarray_read(a+1, a[0]); }

void ivec_print(int *a) { iarray_print(a+1, a[0]); }

int *ivec_rev(int *a){
  int *b = ivec_new(a[0]);
  int s = a[0];
  b[0] = s;
  int i;
  for(i=1;i<=s;i++){b[i]=a[s-i+1];}
  return b;
}

void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  ivec_print(a); ivec_print(b);
}

int main(void) {
  int a0[] = {3,1,2,3},b0[] = {3,3,2,1};
  expect_iarray(ivec_rev(a0), b0, 3,"3 2 1");
  int a1[] = {5,-1,0,4,12,8000},b1[] = {5,8000,12,4,0,-1};
  expect_iarray(ivec_rev(a1), b1, 5,"8000 12 4 0 -1");
  int a2[] = {1,0},b2[] = {1,0};
  expect_iarray(ivec_rev(a2), b2, 1,"0");
  return 0;
}
・実行例
[n1910485@sol ~/prg]$ ./a.out
1> 1
2> 2
3> 3
4> 4
5> 5
  5  4  3  2  1
[n1910485@sol ~/prg]$ ./a.out
1> 0
2> -4
3> 9
4> 3
5> 19
・単体テストの実行例
[n1910485@sol ~/prg]$ ./a.out
OK 3 2 1
  3  2  1
  3  2  1
OK 8000 12 4 0 -1
 8000 12  4  0 -1
 8000 12  4  0 -1
OK 0
  0
  0

・プログラムの説明
受け取った列とは逆順の列を返す。列aと同じ要素数のbを取得し、
i=1~sについて、b[i]にはa[s-i+1]を入れることでbがaとは逆順の列に
なる。sはaとbに入っているデータ数のことである。

・考察
可変長配列においては、一番最初にデータが何個入っているかを示す要
素があるので、forループのところでiが0からではなく1からとなってい
るので、そこもケアする必要があった。また、このコードを実行したと
き、最初はSegmentation fault(core dumped)と出たが、これは最初に
ivec_revにおいてbを定義するとき、int *bとしていたからであった。
(int *b = ivec_new(a[0])が正しい)他の関数では可変長配列を前提
としているので、ただの配列ではうまく動かないのだと考えられる。

③アンケート
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。
配列を指定するとき、間接参照演算子を付けるかどうかをチェックすること。
Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に
付いていないと思うことは何ですか。
アドレスとポインタの詳しい概念について。
Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
アドレスやポインタについて以前よりは分かってきた。

n1910493 2b:○ Mon May 25 23:25:59 2020


プログラミング通論レポート 2b

学籍番号:1910493
ペアの学籍番号:個人作業
提出日時:2020/5/25

------
・演習1-b:配列の並び順を逆順にする関数の作成

// iarray_reverse.c --- 配列の並び順を逆順にする
#include <stdio.h>
#include <stdbool.h>
void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
void iarray_reverse(int *a, int n) { 
  for(int i = 0; i < (n/2); ++i) {
    int x = a[i];
    a[i] = a[n-i-1];
    a[n-i-1] = x;
  }
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
  int a[] = {8,5,2,4,1}, b[] = {1,4,2,5,8},
      c[] = {1,4,3,5,8,0}, d[] = {0,8,5,3,4,1};
  iarray_reverse(a, 5); expect_iarray(a, b, 5, "85241 -> 14258");
  iarray_reverse(c, 6); expect_iarray(c, d, 6, "143580 -> 085341");
  return 0;
}

---実行例---
% ./a.out
OK 85241 -> 14258
  1  4  2  5  8
  1  4  2  5  8
OK 143580 -> 085341
  0  8  5  3  4  1
  0  8  5  3  4  1

---説明---
forループ内でxという整数型の変数を用意して、該当する配列の要素を
コピーし、その要素の対称にあたる要素を元あった要素にコピーし、さ
らに対称にあたる要素にxをコピーすることで逆順にしている。(n-i)で
はなく(n-i-1)という値を取っているのは、前者だとi=0のときに配列の
要素からはみ出してしまうからだ。また、配列の要素の中心を軸として
対称的に入れ替えているため、(n-1)の半分の回数ループさせるように
している。(n-1)回だと、2度入れ替えてしまい元に戻ってしまう。

---考察---
対称的に入れ替えるということは、nが奇数か偶数かによって真ん中の
数がどうなるか様子が異なるので、テストケースとしては設問で与えら
れている要素が奇数個の単体テストに加え、要素が偶数個の配列を用意
しそれで判定する単体テストを加えた。整数は奇数か偶数かのどちらか
なので、これらが共にOKであれば、題意を満たすコードであると考える。

------
・演習2-a:2つの列を受け取り、両方の列の内容を交互に並べて返す
// ivec_kougo.c --- 2つの列を受け取り、内容を交互に並べて返す
#include <stdio.h>
#include <stdlib.h>
void iarray_reada(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("a%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_readb(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("b%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
void ivec_reada(int *a) { iarray_reada(a+1, a[0]); }
void ivec_readb(int *b) { iarray_readb(b+1, b[0]); }
void ivec_print(int *a) { iarray_print(a+1, a[0]); }
int *ivec_kougo(int *a, int *b) {
  int *c = ivec_new(a[0]+b[0]);
  if(a[0] == b[0]) {
    for(int i = 1; i < 2*a[0]; ++i) { c[2*i-1] = a[i]; c[2*i] = b[i]; }
  }
  if(a[0] > b[0]) {
    for(int i = 1; i < 2*b[0]; ++i) { c[2*i-1] = a[i]; c[2*i] = b[i];}
    for(int j = 2*b[0]+1; j <= 2*b[0]+a[0]-b[0]; ++j) { c[j] = a[j-b[0]]; }
  }
  if(a[0] < b[0]) {
    for(int i = 1; i < 2*a[0]; ++i) { c[2*i-1] = a[i]; c[2*i] = b[i];}
    for(int j = 2*a[0]+1; j <= 2*a[0]+b[0]-a[0]; ++j) { c[j] = b[j-a[0]]; }
  }
  return c;
}
int main(void) {
  int *a, *b, *c, numa, numb;
  printf("What is the size of a > "); scanf("%d", &numa);
  printf("What is the size of b > "); scanf("%d", &numb);
  a = ivec_new(numa); ivec_reada(a);
  b = ivec_new(numb); ivec_readb(b);
  c = ivec_kougo(a, b); ivec_print(c);
  free(a); free(b); free(c);
  return 0;
}

---実行例---
(aのサイズ = bのサイズの場合)
% ./a.out
What is the size of a > 3
What is the size of b > 3
a1> 1
a2> 2
a3> 3
b1> 4
b2> 5
b3> 6
  1  4  2  5  3  6

(aのサイズ < bのサイズの場合)
% ./a.out
What is the size of a > 3
What is the size of b > 5
a1> 1
a2> 2
a3> 3
b1> 4
b2> 5
b3> 6
b4> 7
b5> 8
  1  4  2  5  3  6  7  8

(aのサイズ > bのサイズの場合)
% ./a.out
What is the size of a > 6
What is the size of b > 3
a1> 1
a2> 2
a3> 3
a4> 4
a5> 5
a6> 6
b1> 7
b2> 8
b3> 9
  1  7  2  8  3  9  4  5  6

---説明---
まず、ivec_kougo関数の中でaのサイズとbのサイズについて、同じ場合、
aが大きい場合、bが大きい場合でそれぞれ場合分けをした。
(1)aとbのサイズが同じ場合では、aの要素がcの奇数番目、bの要素がc
の偶数番目に入ることに注目して、奇数と偶数の一般化を用いて
a[1],b[1],a[2],b[2],a[3],...といったように入るようにした。
(2)aが大きい場合では、小さいbに合わせ、bの最後尾が入るまでまず
(1)を行う。そして続きには、aの残りを順番に入れるようにした。ルー
プの条件ではbのサイズの2倍にaのサイズ-bのサイズを足すことで、続
きとなるcの番号を求めている。ただし、そのcの番号は続くaの番号と
はかけ離れているため、そこからbのサイズを引くことで正しいaの番号
に戻している。
(3)bが大きい場合では、(2)と同様のことを、aとbを逆にして行った。
次に、main関数の中では新たにnumaとnumbという整数型の変数を定義し、
これをivec_newの引数とすることで、プロンプトからa,bのサイズを指
定できるようにした。その2値の大小によって、先に述べた場合分けが
行われる。
ちなみに、どちらの変数に入れているかわかるよう、プロンプトは(an>
or bn>)という形にして、読み込む関数もそれぞれaとbで区別している。

---単体テスト---
// test_ivec_kougo.c --- ivec_kougo.cのテスト
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
int *ivec_kougo(int *a, int *b) {
  int *c = ivec_new(a[0]+b[0]);
  if(a[0] == b[0]) {
    for(int i = 1; i < 2*a[0]; ++i) { c[2*i-1] = a[i]; c[2*i] = b[i]; }
  }
  if(a[0] > b[0]) {
    for(int i = 1; i < 2*b[0]; ++i) { c[2*i-1] = a[i]; c[2*i] = b[i];}
    for(int j = 2*b[0]+1; j <= 2*b[0]+a[0]-b[0]; ++j) { c[j] = a[j-b[0]]; }
  }
  if(a[0] < b[0]) {
    for(int i = 1; i < 2*a[0]; ++i) { c[2*i-1] = a[i]; c[2*i] = b[i];}
    for(int j = 2*a[0]+1; j <= 2*a[0]+b[0]-a[0]; ++j) { c[j] = b[j-a[0]]; }
  }
  return c;
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
  int a[] = {3,1,2,3}, b[] = {3,2,4,5},
    c[] = {5,1,2,3,4,5}, d[] = {2,3,2},
    e[] = {6,1,2,2,4,3,5}, f[] = {8,1,1,2,2,3,3,4,5},
    g[] = {5,1,3,2,2,3};
  int *ab = ivec_kougo(a, b);
  int *ac = ivec_kougo(a, c);
  int *ad = ivec_kougo(a, d);
  expect_iarray(ab, e, 7, "[1,2,3]+[2,4,5]=[1,2,2,4,3,5]");
  expect_iarray(ac, f, 9, "[1,2,3]+[1,2,3,4,5]=[1,1,2,2,3,4,5]");
  expect_iarray(ad, g, 6, "[1,2,3]+[3,2]=[1,3,2,2,3]");
  return 0;
}

---単体テストの実行例---
% ./a.out
OK [1,2,3]+[2,4,5]=[1,2,2,4,3,5]
  6  1  2  2  4  3  5
  6  1  2  2  4  3  5
OK [1,2,3]+[1,2,3,4,5]=[1,1,2,2,3,4,5]
  8  1  1  2  2  3  3  4  5
  8  1  1  2  2  3  3  4  5
OK [1,2,3]+[3,2]=[1,3,2,2,3]
  5  1  3  2  2  3
  5  1  3  2  2  3

---考察---
テストケースとしては、プログラムの分岐にしたがい、aのサイズとbの
サイズが同じ場合、aのサイズの方が小さい場合、bのサイズの方が小さ
い場合の3通りで十分だと考えた。プログラムでプロンプトから入力さ
せたサイズは、それぞれa[0],b[0],c[0],d[0]にあたる。

テストケースを扱ってきて、部品をそれぞれ検査することがいかに重要
かがわかった。例えばこの演習2-aでは、真ん中のaのサイズが小さいと
きだけNGが出たのだが、まずプログラム内の分岐の(2)a>bと(3)a<bはa
とbの文字を入れ替えただけだから、もしここが違うとしたら下のテス
トケースもNGが出るはず、ということはプログラムに問題があるのでは
なく、テストケースのmain関数に問題があるのだというように、誤って
いる場所を類推しながら修正することができるところが、時間効率や的
確な処置に繋がり必須な行為だと感じた。自分の作ったプログラムに対
して考えられるテストパターンを挙げ、こまめにテストしながら書いて
いくべきだと考える。

---アンケート---
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。
=の右にあるのか左にあるのか、またそれがどういった働きをするのか
を整理しながら使うこと。
Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に
付いていないと思うこ
とは何ですか。
いまだに*の有無でエラーを吐かれることがあり、まだ十分に仕組みと
働きについて理解できていないこと。
Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ
テストを行う意義を身をもって学ぶことができました。ありがとうございました。

n1910496 2b:○ Sun May 24 08:10:08 2020


プログラミング通論 2-b
学籍番号:1910496
個人作業
提出日時;2020/5/24

〇1つ目の課題

・課題の再掲
何らかの整列アルゴリズムで配列を昇順に整列する関数voidiarray
sort(int *a,intn)を作成する。

・プログラム
// 1-c
#include <stdio.h>
#include <stdbool.h>
void iarray_sort(int *a, int n) {
  for(int i = 0; i < n-1; ++i) {
    for(int j = i+1; j < n; ++j) {
      if(a[i] > a[j]) {
        int x = a[i];
        a[i] = a[j];
        a[j] = x;
      }
    }
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
  int a[] = {8,5,2,4,1}, b[] = {1,2,4,5,8};
  iarray_sort(a, 5); expect_iarray(a, b, 5, "85241 -> 12458");
  int c[] = {1}, d[] = {1};
  iarray_sort(c, 1); expect_iarray(c, d, 1, "1 -> 1");
  int e[] = {-8,-5,-2,-4,-1}, f[] = {-8,-5,-4,-2,-1};
  iarray_sort(e, 5); expect_iarray(e, f, 5, "-8-5-2-4-1 -> -8-5-4-2-1");
  return 0;
}

・プログラムの説明
昇順アルゴリズムには端から比較する値を固定してその値とそれ以外の
値をそれぞれ比較していき、小さい値であれば交換をおこない、次の位
置で同様に比較をおこなっていくバブルソートを用いている。

・単体テストと実行結果
演習で指定されたテストケースに加え、配列の値の個数が1つの場合、
全て負の数の配列の場合で単体テストをおこなった。
以下、実行結果である。

OK 85241 -> 12458
  1  2  4  5  8
  1  2  4  5  8
OK 1 -> 1
  1
  1
OK -8-5-2-4-1 -> -8-5-4-2-1
 -8 -5 -4 -2 -1
 -8 -5 -4 -2 -1


・考察
コードを書いてから1回目のコンパイルの時点では、誤ってバブルソー
トのif条件の不等号を逆にしてしまっていた。単体テストでの2つのテ
ストケースより、ソートのコード自体に問題があることがわかったため、
最低2つはテストケースが必要だと分かった。

〇2つ目の課題

・課題の再掲
2つの列を受け取り、両方の列の内容を交互に並べた列を返すプログラ
ムを作成する。長さが異なる場合の扱いは好きに決めてよい。

・プログラム
// 2-a
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
int *ivec_alt(int *a, int *b) {
  int n = 0;
  if(a[0] < b[0]) {
    n = 2 * b[0];
  } else {
    n = 2 * a[0];
  }
  int *c = ivec_new(n);
  for(int i = 1; i <= n; ++i) { c[i-1] = 0; }
  for(int i = 1; i <= a[0]; ++i) { c[2*i-2] = a[i]; }
  for(int i = 1; i <= b[0]; ++i) { c[2*i-1] = b[i]; }
  return c;
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
  int a[] = {3,1,2,3}, b[] = {3,4,5,6}, c[] = {1,4,2,5,3,6};
  int *p = ivec_alt(a, b);
  expect_iarray(p, c, 6, "[1,2,3]+[4,5,6]=[1,4,2,5,3,6]");
  int d[] = {1,1}, e[] = {1,2}, f[] = {1,2};
  int *q = ivec_alt(d, e);
  expect_iarray(q, f, 2, "[1]+[2]=[1,2]");
  int g[] = {4,4,5,6,7}, h[] = {3,1,2,3}, i[] = {4,1,5,2,6,3,7,0};
  int *r = ivec_alt(g, h);
  expect_iarray(r, i, 8, "[4,5,6,7]+[1,2,3]=[4,1,5,2,6,3,7,0]");
  int j[] = {3,1,2,3}, k[] = {4,4,5,6,7}, l[] = {1,4,2,5,3,6,0,7};
  int *s = ivec_alt(j, k);
  expect_iarray(s, l, 8, "[1,2,3]+[4,5,6,7]=[1,4,2,5,3,6,0,7]");
  return 0;
}

・プログラムの説明
配列が長い方の配列の個数の2倍の分だけの長さの配列を用意し、あら
かじめ全てに0を代入する。その後、偶数番目、奇数番目でそれぞれの
配列から代入していき、長さが異なる場合は交互に入る規則は変わらず、
代入されていない場所には見かけ上0が入るようにした。

・単体テストと実行結果
演習で指定されたテストケースに加え、配列の値の個数が1つの場合、
配列の長さが異なる場合をより長い方が異なる2パターンで単体テスト
をおこなった。以下、実行結果である。

OK [1,2,3]+[4,5,6]=[1,4,2,5,3,6]
  1  4  2  5  3  6
  1  4  2  5  3  6
OK [1]+[2]=[1,2]
  1  2
  1  2
OK [4,5,6,7]+[1,2,3]=[4,1,5,2,6,3,7,0]
  4  1  5  2  6  3  7  0
  4  1  5  2  6  3  7  0
OK [1,2,3]+[4,5,6,7]=[1,4,2,5,3,6,0,7]
  1  4  2  5  3  6  0  7
  1  4  2  5  3  6  0  7

・考察
長さの異なる場合に長い方の数字の余った分を、奇数番目もしくは偶数
番目の法則性を変えないよう同様に値を入れていっており、足りなくて
空いた分を0で補っている。そうすると、もとの2つの配列で後尾に0が
連続して一致する箇所があったとしても、もとの2つの数列に分解しや
すいと考えた。


〇アンケート
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。 
何を参照するのかメモしておくこと。

Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に
付いていないと思うことは何ですか。
よりすっきりしたコードを書くための経験、演習量。 

Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
次回も頑張ります。

n1910498 2b:○ Wed May 20 21:59:51 2020


レポート2b


ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

【選択した課題1】
 演習1-c
 何らかの整列アルゴリズムで配列を昇順に整列する関数
 iarray_sort(int *a, intn)を作成せよ。

【コード1】
#include <stdio.h>
#include <stdbool.h>
void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
void iarray_sort(int *a, int n){ 
  for(int i = 0; i < n; ++i) {
   for(int j = i+1; j < n; ++j) {
    if(a[j] < a[i]){
     int x = a[i];
     a[i] = a[j];
     a[j] = x;
    }
   }
  }
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
   if(a[i] != b[i]) { return false; }
  }
  return true;
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
 int a[] = {8,5,2,4,1}, b[] = {1,2,4,5,8};
 int c[] = {8,-1,2,4,1,0}, d[] = {-1,0,1,2,4,8};
 int e[] = {8,-1,2,8,0,0}, f[] = {-1,0,0,2,8,8};
 iarray_sort(a, 5); expect_iarray(a, b, 5, "85241 -> 12458");
 iarray_sort(c, 6); expect_iarray(c, d, 6, "8-12410 -> -101248");
 iarray_sort(e, 6); expect_iarray(e, f, 6, "8-12800 -> -100288");
 return 0;
}

【実行例(単体テスト結果)1】
 $ ./a.out
OK 85241 -> 12458
  1  2  4  5  8
  1  2  4  5  8
OK 8-12410 -> -101248
 -1  0  1  2  4  8
 -1  0  1  2  4  8
OK 8-12800 -> -100288
 -1  0  0  2  8  8
 -1  0  0  2  8  8

【説明1】
 iとjについて繰り返しを使うことで、配列の先頭から順にそれより
右側にその数より小さい数があるか調べていき、もしある場合は交換す
るようなプログラムを作成した。(交換した後もそれより右の数を調べ
ていく)
 この時、iは基準にする数の入る場所を先頭から最後まで回していく
役割。jはiが示す場所より右(後ろ)の場所にある数を指定していく役割
をしている。
 また、単体テストについて例に加えてマイナスの数がある場合と同じ
数字が含まれる場合を作成した。

【考察1】
 このプログラムは一年の時にも出てきたバブルソートを用いて作成し
た。はじめバブルソートを用いるという方針が思いつかず以下のように

void iarray_sort(int *a, int n){ 
  for(int i = 0; i < n; ++i) {
   for(int j = 0; j < n; ++j) {
    if(a[i] < a[j]){
     int x = a[i];
     a[i] = a[j];
     a[j] = x;
    }
   }
  }
}

同じ数も含め全てのペアを比べて交換していくプログラムを作成した。
このプログラムでも単体テストはバブルソートを用いた時と同じように
全てOKになったが、同じ数字を比べていたりと(プログラムの行数的に
は変わらないものの)無駄が多いと考えられるのでやはりバブルソート
を用いるのがよいと思われる。また、一年の時に出てきた他の配列を昇
順に整列するアルゴリズムを用いてプログラムを作成することも時間の
あるときに考えてみたいと思った。


ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

【選択した課題2】
 演習2-a
 2つの列を受け取り、両方の列の内容を交互に並べた列を返す関数を
 作成せよ。

【コード2】
#include <stdio.h>
#include <stdlib.h>
void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
  printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
int *ivec_new(int size) {
int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
void ivec_read(int *a) { iarray_read(a+1, a[0]); }
void ivec_print(int *a) { iarray_print(a+1, a[0]); }
int *ivec_kougo(int *a, int *b) {
  int *c = ivec_new(a[0]+b[0]);
  if(a[0] <= b[0]){
   for(int i = 1; i <= a[0]; ++i) { c[2*i-1] = a[i]; c[2*i] = b[i];}
   for(int i = 2*a[0]+1; i <= a[0]+b[0]; ++i) { c[i] = b[i-a[0]]; }
  }
  if(a[0] > b[0]){
   for(int i = 1; i <= b[0]; ++i) { c[2*i-1] = a[i]; c[2*i] = b[i];}
   for(int i = 2*b[0]+1; i <=a[0]+b[0]; ++i) { c[i] = a[i-b[0]]; }
  }
  return c;
}
int main(void) {
  int *a, *b, *c;
  a = ivec_new(3); ivec_read(a);
  b = ivec_new(2); ivec_read(b);
  c = ivec_kougo(a, b); ivec_print(c);
  free(a); free(b); free(c);
  return 0;
}
<単体テスト(main)>
int main(void) {
  int a[] = {3,1,2,3}, b[] = {2,4,5}, c[] = {5,1,4,2,5,3};
  int d[] = {3,1,2,3}, e[] = {3,4,5,6}, f[] = {6,1,4,2,5,3,6};
  int g[] = {2,1,2}, h[] = {3,3,4,5}, i[] = {5,1,3,2,4,5};
  int j[] = {0}, k[] = {3,1,2,3}, l[] = {3,1,2,3};
  int *p = ivec_kougo(a, b),*q = ivec_kougo(d, e),*r = ivec_kougo(g, h),*s = ivec_kougo(j, k);
  expect_iarray(p, c, 6, "[1,2,3]+[4,5]=[1,4,2,5,3]");
  expect_iarray(q, f, 7, "[1,2,3]+[4,5,6]=[1,4,2,5,3,6]");
  expect_iarray(r, i, 6, "[1,2]+[3,4,5]=[1,3,2,4,5]");
  expect_iarray(s, l, 4, "[]+[1,2,3]=[1,2,3]");
  return 0;
}

【実行例(単体テスト結果)2】
$ ./a.out
OK [1,2,3]+[4,5]=[1,4,2,5,3]
  5  1  4  2  5  3
  5  1  4  2  5  3
OK [1,2,3]+[4,5,6]=[1,4,2,5,3,6]
  6  1  4  2  5  3  6
  6  1  4  2  5  3  6
OK [1,2]+[3,4,5]=[1,3,2,4,5]
  5  1  3  2  4  5
  5  1  3  2  4  5
OK []+[1,2,3]=[1,2,3]
  3  1  2  3
  3  1  2  3

【説明2】
 まずivec_new(a[0]+b[0])で二つの配列のデータの個数の合計分の長
さの配列*cを作成する。
 次に、aとbのデータの個数を比較し少ない方の配列のデータが入りき
るまでまでforを使い交互にcに入れていく。(この時
a[1],b[1],a[2],…とaから交互に入れていくようにした)
 その後、まだcに入れられていない(データの個数が多い方の配列の
余っている)数字をcに入れていくようにした。
 単体テストについて、aとbの長さの差が1かつaが長い場合、aとbの長
さの差が1かつbが長い場合、aとbの長さが同じ場合、aとbの長さの差が
1以上の場合(片方の配列にデータが入っていない場合)を作成した。

【考察2】
 はじめ、関数を作成する時以下のようなものを作成した。
int *ivec_kougo(int *a, int *b) {
  int *c = ivec_new(a[0]+b[0]);
  for(int i = 1; i <= a[0]; ++i) { c[2*i-1] = a[i]; }
  for(int i = 1; i <= b[0]; ++i) { c[2*i] = b[i]; }
  return c;
}
 この場合、単体テストの結果は以下のようになり
$ ./a.out
OK [1,2,3]+[4,5]=[1,4,2,5,3]
  5  1  4  2  5  3
  5  1  4  2  5  3
OK [1,2,3]+[4,5,6]=[1,4,2,5,3,6]
  6  1  4  2  5  3  6
  6  1  4  2  5  3  6
NG [1,2]+[3,4,5]=[1,3,2,4,5]
  5  1  3  2  4  0
  5  1  3  2  4  5
NG []+[1,2,3]=[1,2,3]
  3  0  1  0
  3  1  2  3
 aの方が短い時うまくいかないことがわかった。そこでまずaとbの長
さを比較する必要性があり長さの差が1以上あり綺麗に交互にならない
場合も考えるべきであると考え最終的に上のようなものを作成した。
 作成する中で数がどんな時でもうまく合うように考えるのに少し苦労
しました(具体的にはfor(int i = 2*a[0]+1; i <= a[0]+b[0]; ++i) {
c[i] = b[i-a[0]]; }のところ)。
 また、単体テストについてもう少しテストケースを増やすべきか悩みました。

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

【アンケート】
 Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。
 ポインタ変数が何を指しているのかちゃんと理解しておくこと。
 Q2. ここまでのところで、プログラムを作るときに重要だが自分で身
 に付いていないと思うことは何ですか。
 単体テストなどで間違いが見つかった時に何が違うかちゃんと理解すること。
 Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
 次回も頑張りたいです。

n1910506 2b:○ Tue May 19 16:39:45 2020


プログラミング課題
学籍番号1910506
課題提出日 5月19日

一つ目の課題の再掲
プログラム番号:1e
#include <stdio.h>
#include <stdbool.h>

void iarray_read(int *a,int n){
	for(int i = 0;i < n;++i) {
		printf("%d> ",i+1);scanf("%d",a+i);
	}
}
void iarray_print(int *a,int n) {
	for(int i = 0;i < n;++i) {
		printf(" %2d",a[i]);
		printf("\n");
	}
}

bool iarray_equal(int *a,int *b,int n){
	for(int i = 0;i < n;++i){
		if(a[i] !=b[i]){return false;}
		}	
		return true;
	
}

void expect_iarray(int *a,int *b,int n,char *msg){
	printf("%s %s\n", iarray_equal(a,b,n)?"OK":"NG",msg);
	iarray_print(a,n);iarray_print(b,n);}

int iadd(int x,int y) {return x+y;}
int imax(int x,int y){return (x > y)?x:y;}
int (*fp)(int,int);

int iarray_inject(int *a,int n,int (*fp)(int,int)){
	int b = 0,m=a[0];
	if((*fp) == iadd){
	for(int i = 0;i < n;++i) {
		b =(*fp)(b,a[i]);
		}
		return b;
	}
	if((*fp) == imax){
	for(int i = 0;i < n;++i){
		if(a[i]> m) {
			m = a[i];
			}
		}
		return m;
	}
	return 0;

}

void expect_int(int b1,int b2,char *msg){
	printf("%s %d:%d %s\n",(b1 == b2)?"OK":"NG",b1,b2,msg);
}
//単体テスト
int main(void){
	int a[] = {8,5,2,4,1},b[]={1,-2,3,4,4};
	expect_int(iarray_inject(a,5,iadd),20,"8+5+2+4+1");
	expect_int(iarray_inject(a,5,imax),8,"8,5,2,4,1");
	expect_int(iarray_inject(b,5,iadd),10,"1-2+3+4+4");
	expect_int(iarray_inject(b,5,imax),4,"1,-2,3,4,4");
	}

説明:配列a[]を受け取り、iarray_injectで二つの関数iadd(配列の全
ての値を合計を返す)とimax(配列内で最大の値の数を返す)とその結果
を格納する変数b,mを用意し、ポインタ変数で前述の2関数を呼び出し,
それぞれの機能を実行させ、結果を返す。

実行例:a[] = {1,2,3,4} -> iarray_inject(a,4,iadd) -> 10
                       -> iarray_inject(a,4,imax) -> 4 

上のプログラムの実行結果
OK 20:20 8+5+2+4+1
OK 8:8 8,5,2,4,1
OK 10:10 1-2+3+4+4
OK 4:4 1,-2,3,4,4

考察

関数を呼ぶことができるというのは、作るプログラムが大変であればあ
るほど重要になってくることが予測される。この機能を使えば、一度あ
ることの処理をするプログラムを作ってしまえば、その処理をするプロ
グラムは全てそれでできるので、大幅にプログラミング時間を短縮でき
ると考えられる。また、関数を呼び出すというのはコマンドとやってい
ることが同じであるからこれを応用することでコマンドを作ることがで
きると考えられる。
  
二つ目の課題の再掲
プログラム番号:2d
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

void iarray_read(int *a,int n) {
	for(int i = 0;i < n;++i) {
		printf("%d> ",i+1);scanf("%d",a+i);
	}
}

void iarray_print(int *a,int n) {
	for(int i = 0;i < n;++i) {
		printf(" %2d",a[i]);
		printf("\n");
	}
}

void *ivec_new(int size) {
	int *a = (int*)malloc((size+1)*sizeof(int));
	a[0] = size;
	return a;
}

bool iarray_equal(int *a,int *b,int n){
	for(int i = 0;i < n;++i){
		if(a[i] !=b[i]){return false;}
		}	
		return true;
	
}

void expect_iarray(int *a,int *b,int n,char *msg){
	printf("%s %s\n", iarray_equal(a,b,n)?"OK":"NG",msg);
	iarray_print(a,n);iarray_print(b,n);}

void ivec_read(int *a) {
	iarray_read(a+1,a[0]);
}
void ivec_print(int *a) {
	iarray_print(a+1,a[0]);
}
int *ivec_concat(int *a,int *b) {
	int *c =ivec_new(a[0]+b[0]),al=1,bl=1;
	c[0] = a[0]+b[0];
	for(int i = 1;i <=a[0]+b[0];++i) {
		if(al <=a[0]){
			if(a[al] < b[bl]){
				c[i] = a[al];al+=1;
			}else{c[i] = b[bl];bl+=1;
			}
		}
		else{
			c[i]=b[bl];bl+=1;
		}
	}
	return c;
}

//単体テスト
int main(void) {
	int a[] = {4,1,2,3,4},b[]={4,3,4,5,6},c[]={8,1,2,3,3,4,4,5,6};
	int f[] = {4,1,1,1,1},d[]={4,2,2,2,2},e[]={8,1,1,1,1,2,2,2,2};
	int k[] ={0},l[]= {0},i[]={0};
	int *p = ivec_concat(a,b);
	expect_iarray(p,c,9,"[1,2,3,4]+[3,4,5,6] -> [1,2,3,3,4,4,5,6]");
	int *q= ivec_concat(f,d);
	expect_iarray(q,e,9,"[1,1,1,1]+[2,2,2,2] -> [1,1,1,1,2,2,2,2]");
	int *w = ivec_concat(k,l);
	expect_iarray(w,i,2,"[]+[1]->[1]");
	return 0;
}

説明:関数ivec_newを使い、二つの配列の長さを足した長さの配列cを作
り、c[0]にa[0]+b[0]を入れ、それぞれの配列から何回新しく作った配
列に入ったかをカウントする関数alとblを作り、それがa[0],b[0]の値
になるまで、それぞれの配列から小さい順にcに入れていく。

実行例:a[] = {4,1,2,3,4},b[] = {4,5,6,7,8} ->expect_iarray(a,b)
	    ->c[] = {8,1,2,3,4,5,6,7,8}}

上のプログラムの実行結果

OK [1,2,3,4]+[3,4,5,6] -> [1,2,3,3,4,4,5,6]
  8
  1
  2
  3
  3
  4
  4
  5
  6
  8
  1
  2
  3
  3
  4
  4
  5
  6
OK [1,1,1,1]+[2,2,2,2] -> [1,1,1,1,2,2,2,2]
  8
  1
  1
  1
  1
  2
  2
  2
  2
  8
  1
  1
  1
  1
  2
  2
  2
  2
OK 
  0
  0
  0
  0
考察

このプログラムを使う時について、作りながら考えていたがよくわから
なかった。作り終わった後どんな使い道があるのか考えてみた。最終的
に、データをまとめるのに使うものだと考えた。また、配列はコンピュー
ターにおいてあらゆるところで使われているので、このプログラムも結
構使われていることが予測される。

アンケート

 Q1.アドレス、ポインタを使うプログラムで注意すべきことはなんだと思いますか。
	メモリの領域を事前に確保しておくこと。
 Q2.ここまでのところで、プログラムを作るときに重要だが自分で身についていないと思  うことはなんですか。
	誤字に気をつける。	
 Q3.リフレクション(今回の課題で分かったこと)・感想・要望
	テストを作るとこでデバックをしやすいようになっていると思った。

n1910507 2b:○ Sun May 24 09:44:52 2020


レポート課題2b
提出日時:5/24

[演習1c]
何らかの整列アルゴリズムで配列を昇順に整列する関数void
iarray_reverse(int *a, int n)を作成する。

[プログラム]
#include <stdio.h>
#include <stdbool.h>
void swap(int *x, int *y){
    int temp;
    temp=*x;
    *x=*y;
    *y=temp;
}
int *iarray_sort(int *a, int n){
    int min_number;
    int min=*a;
    for(int i=0; i<n-1; ++i){
        min=*(a+i);
        for(int j=i; j<n; ++j){
            if(*(a+j)<=min){
                min=*(a+j);
                min_number=j;
            }
        }
        swap(a+i, a+min_number);
    }
    return a;
}
bool iarray_equal(int *a, int *b, int n){
    for(int i=0; i<n; ++i){
        if(a[i]!=b[i]){ return false;}
    }
    return true;
}

void expect_iarray(int *a, int *b, int n, char *msg){
    printf("%s : %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
    for(int i=0; i<n; ++i){
        printf("%d ", a[i]);
    }
    printf("\n");
}

int main(void){
int n, *a;
    printf("n> "); scanf("%d", &n);
    for(int i=0; i<n; ++i){
        printf("%d> ", i+1); scanf("%d", a+i);
    }
    int *aa=iarray_sort(a, n);
    for(int i=0; i<n; ++i){
        printf("%2d ", *(aa+i));
    }
    printf("\n\n単体テスト\n");
    int a1[]={8, 5, 2, 4, 1}, b[]={1, 2, 4, 5, 8};
    int a2[]={5, 2, 4, 1, 8}, a3[]={2, 4, 1, 8, 5}, a4[]={4, 1, 8, 5, 2}, a[5]={1, 8, 5, 2, 4};
    int c[]={9, 8, 7, 6, 5, 4, 3, 2, 1}, d[]={1, 2, 3, 4, 5, 6, 7, 8, 9};
    int e[]={-1, -2, -3, -4, -5, -6, -7, -8, -9}, f[]={-9, -8, -7, -6, -5, -4, -3, -2, -1};
    iarray_sort(a1, 5); expect_iarray(a1, b, 5, "85241 -> 12458");
    iarray_sort(a2, 5); expect_iarray(a2, b, 5, "52418 -> 12458");
    iarray_sort(a3, 5); expect_iarray(a3, b, 5, "24185 -> 12458");
    iarray_sort(a4, 5); expect_iarray(a4, b, 5, "41852 -> 12458");
    iarray_sort(c, 9);
    expect_iarray(c, d, 9, "987654321 -> 123456789");
    iarray_sort(e, 9);
    expect_iarray(e, f, 9, "-1-2-3-4-5-6-7-8-9 -> -9-8-7-6-5-4-3-2-1");
    return 0;
}

[実行結果]
n> 4
1> 6
2> 9
3> -9
4> 0
-9  0  6  9 

単体テスト
OK : 85241 -> 12458
1 2 4 5 8 
OK : 52418 -> 12458
1 2 4 5 8 
OK : 24185 -> 12458
1 2 4 5 8 
OK : 41852 -> 12458
1 2 4 5 8 
OK : 987654321 -> 123456789
1 2 3 4 5 6 7 8 9 
OK : -1-2-3-4-5-6-7-8-9 -> -9-8-7-6-5-4-3-2-1
-9 -8 -7 -6 -5 -4 -3 -2 -1

[説明]

選択ソートを用いて、配列を昇順にソートした。まず、ポインタ型の仮
引数xとyを受け取り、配列の場所を交換する関数swapを作った。関数
iarray_sortは変数minに配列内の要素の最小値を格納し、minと配列の
要素を右から順番に交換する。このとき、minの初期値はiが一周する毎
に配列の要素を右から順に入れている。その後、例題と同じように
iarray_equalとexpect_iarrayを作り、main関数内で単体テストをした。

[考察]

今まで、習ってきたことでは配列をreturnで返すことができなかったが、
ポインタを使用することで別の関数に配列を渡すことができた。また、
テストケースの配列a1からa5は中身は同じだが順番が異なる配列で、配
列cとeはaと異なる要素数でテストした。これらすべてが正しい結果だっ
たのでこのプログラムは正常に動いていると考えられる。

[演習3a]
サンプルプログラムのcdseq.cに、等差数列を初項に戻す機能cdseq_resetを追加する。

[プログラム]
//cdseq.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cdseq.h"
struct cdseq { int value, diff, first;};
struct cdseq *cdseq_new(int s, int d) {
  struct cdseq *r = (struct cdseq*)malloc(sizeof(struct cdseq));
  r->value = s; r->diff = d; r->first=s; return r;
}
int cdseq_get(struct cdseq *r) {
  int v = r->value; r->value += r->diff; return v;
}
int cdseq_reset(struct cdseq *r){
  r->value=r->first; return r->value;
}
void cdseq_free(struct cdseq *r) {
  free(r);
}

// cdseq_demo.c -- cdseq demonstration.
#include <stdio.h>
#include "cdseq.h"
int main(void) {
  struct cdseq *s1 = cdseq_new(1, 2);
  struct cdseq *s2 = cdseq_new(0, 3);
  int i;
  for(i = 0; i < 6; ++i) {
    printf(" %2d", cdseq_get(s1));
    printf(" %2d", cdseq_get(s1));
    printf(" %2d", cdseq_get(s2));
  }
  printf("\n");
  printf(" %2d\n", cdseq_reset(s1));
  printf(" %2d\n", cdseq_reset(s2));
  for(i=0; i<3; ++i){
    printf(" %2d", cdseq_get(s1));
    printf(" %2d", cdseq_get(s2));
  }
  printf("\n"); cdseq_free(s1); cdseq_free(s2);
  return 0;
}

//単体テスト
#include <stdio.h>
#include "cdseq.h"
void expect_int(int i1, int i2, char *msg){
    printf("%s %d:%d %s\n", (i1==i2)?"OK":"NG", i1, i2, msg);
}
int main(void){
    struct cdseq *s1=cdseq_new(2, 3);
    struct cdseq *s2=cdseq_new(-1, 5);
    struct cdseq *s3=cdseq_new(0, -7);
    for(int i=0; i<5; ++i){
        cdseq_get(s1);
        cdseq_get(s2);
        cdseq_get(s3);
    }
    expect_int(cdseq_reset(s1), 2, "2+3*0=2");
    expect_int(cdseq_reset(s2), -1, "-1+5*0=-1");
    expect_int(cdseq_reset(s3), 0, "0-7*0=0");
    return 0;
}

[実行結果]
  1  3  0  5  7  3  9 11  6 13 15  9 17 19 12 21 23 15
  1
  0
  1  0  3  3  5  6

//単体テスト
OK 2:2 2+3*0=2
OK -1:-1 -1+5*0=-1
OK 0:0 0-7*0=0

[説明]
構造体cdseqに初項を保存するためのfirstという新しいメンバを作った。
関数cdseq_resetにはcdseq型の構造体のポインタrを渡し、r.valueのポ
インタにr.firstのポインタを入れている。また、ヘッダファイルにint
cdseq_reset(struct cdseq *r);を追加した。

[考察]
cdseq_getを5回繰り返した後にcdseq_resetを実行した結果、すべて問
題なくテストをクリアしたので、このプログラムは問題なく動いている
と考察できる。また、このような情報隠蔽のコードを実装することで前
回のepsプログラムの仕組みを理解することができた。さらに、このよ
うに情報隠蔽のコードを使うことで、main関数があるファイルがすっき
りして見やすくなり、プログラムが書きやすくなると考えられる。

[アンケート]
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。
プログラムの中で、ポインタ変数が何を表しているのか把握すること。
Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に
付いていないと思うことは何ですか。
ポインタとアドレスをあまりよく理解できていないと思う。特に、構造
体のポインタは身についていないと思う。
Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
ポインタをあまり理解できていないので、時間を取ってしっかり理解したい。

n1910509 2b:○ Sat May 23 21:50:05 2020


n1910509 根古谷勇作 ペア学籍番号 h1910546
書いたプログラム	
演習1b
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include<string.h>
#include<stdbool.h>

  
void iarray_reverse(int*a,int n){
  int c = 0;int x = n;
  for(int s=0;s<n/2;++s){c = a[x-1];a[x-1]=a[s];a[s]=c;x=x-1;}
  
}

void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}


int main(void){
  int a[]={8,5,2,4,1},b[]={1,4,2,5,8},c[]={0,1,0,0,0},d[]={0,0,0,1,0},
    e[]={1,2,3,4,5},f[]={5,4,3,2,1},g[]={1,1,1,1,1,1,1,1,1,2},h[]={2,1,1,1,1,1,1,1,1,1};
  
iarray_reverse(a,5);expect_iarray(a,b,5,"85241->14258");

 iarray_reverse(c,5);expect_iarray(c,d,5,"01000->00010");

 iarray_reverse(e,5);expect_iarray(e,f,5,"12345->54321");

 iarray_reverse(g,10);expect_iarray(g,h,10,"1111111112->2111111111");



 return 0;
 
}





演習2a
// ivec_demo.c --- int vector demonstration.
#include <stdio.h>
#include <stdlib.h>
#include<stdbool.h>
void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  return a;
}
void ivec_read(int *a) { iarray_read(a, 3); }
void ivec_print(int *a) { iarray_print(a, 6); }


int *ivec_concat(int *a, int *b) {

  int *c = ivec_new(6);
  for(int i = 0; i < 3; ++i) { c[2*i] = a[i];c[2*i+1] = b[i]; }
  return c;
}

bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}

void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}

int main(void) {
  int a[] = {3,1,2}, b[] = {2,4,5}, c[] = {3,2,1,4,2,5};
  int d[] = {1,3,5}, e[] = {2,4,6}, f[] = {1,2,3,4,5,6};
  int g[] = {9,7,5}, h[] = {8,6,4}, i[] = {9,8,7,6,5,4};
  int *p = ivec_concat(a, b);
  expect_iarray(p, c, 6, "[3,1,2]+[2,4,5]=[3,2,1,4,2,5]");
  int *r = ivec_concat(d,e);
  expect_iarray(r, f, 6, "[1,3,5]+[2,4,6]=[1,2,3,4,5,6]");
  int *s = ivec_concat(g,h);
  expect_iarray(s, i, 6, "[9,7,5]+[8,6,4]=[9,8,7,6,5,4]");

  return 0;
}

実行例
1b
OK 85241->14258
  1  4  2  5  8
  1  4  2  5  8
OK 01000->00010
  0  0  0  1  0
  0  0  0  1  0
OK 12345->54321
  5  4  3  2  1
  5  4  3  2  1
OK 1111111112->2111111111
  2  1  1  1  1  1  1  1  1  1
  2  1  1  1  1  1  1  1  1  1



2a
OK [3,1,2]+[2,4,5]=[3,2,1,4,2,5]
  3  2  1  4  2  5
  3  2  1  4  2  5
OK [1,3,5]+[2,4,6]=[1,2,3,4,5,6]
  1  2  3  4  5  6
  1  2  3  4  5  6
OK [9,7,5]+[8,6,4]=[9,8,7,6,5,4]
  9  8  7  6  5  4
  9  8  7  6  5  4

説明
1b

iarray_reverseは、1年の時に倣ったswapの機能を用いてプログラムを
作成した。a[0]とa[4],a[1]とa[3]...というように値を変えながら配列
を交換する。cという変数にa[x-1]を入れておき、a[x-1]にa[s]を入れ
てから最後に保存しておいたcをa[s]に代入する。

2a

作成した関数ivec_contatは、あらかじめ配列のさいずが6のものを作
成しておき、forループより1回目はcの0,1番目,2回目は2,3番目に値を
入れていくが、偶数番目(0含む)はb[i],奇数番目はa[i]を代入すること
で配列cを作成した。基本的に配列の要素数はすべてa,bともに3で、
合わせて6個の配列cというようにした。

考察
1b
iarray_reverseを作る際、xという変数をわざわざ作成してforループ
を行っているが、あとから考えればnを指定するのだからn自体をループ
内で使えばよいのではと考えた。また、テストケースを考える際、配列
の個数をメイン関数内でしか指定できず、メイン関数がやや見づらくなっ
てしまったが、ここを演習1aのように配列の一部を指定できるような
プログラムにすればもっと簡単な見た目にできるのではと考えた。

2a
こちらも同様メイン関数が見づらいので改善できるようにすることや、
また自身で作成したivec_concatでも、配列cの要素数をあらかじめ指
定しないとプログラムを作成できなかった。これをもっと柔軟に配列数
を増やすようにできるが今後の課題である。


アンケート
4. 以下のアンケートの回答。
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いま すか。 
配列の要素数などをしっかり把握すること、ポインタの打ち間違えをな
くすこと。

Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に
付いて いないと思うことは何ですか。

頭では理解できても実際に自分で作るのは練習しないと遅かったり謎の
エラーが出るので、数をこなすこと

Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
授業頑張って追いついていきます。

n1910513 2b:○ Mon May 25 23:50:06 2020


提出日:2020/05/25

演習1b. 配列の並び順を逆にする

プログラム:

#include <stdio.h>
#include <stdbool.h>
void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
void iarray_reverse(int *a, int n) {
  int x;
  for(int i = 0; i < n/2; ++i) {
    x = a[i]; a[i] = a[n-1-i]; a[n-1-i] = x;
  }
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  iarray_reverse(a, n);
  int t = 1;
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { t = 0; }
  }
  printf(" %s %s\n", (t==1)?"OK":"NG", msg);
  iarray_reverse(a, n);
}
int main(void) {
  int a[] = {8, 5, 2, 4, 1}, b[] = {1, 4, 2, 5, 8};
  expect_iarray(a, b, 5, "85241 -> 14258");
  expect_iarray(a+1, b+1, 3, "524 -> 425");
  expect_iarray(a+2, b+1, 2, "24 -> 42");
  expect_iarray(a+4, b, 1, "1 -> 1");
  return 0;
}


実行結果:

[n1910513@sol 02]$ ./a.out
 OK 85241 -> 14258
 OK 524 -> 425
 OK 24 -> 42
 OK 1 -> 1

説明:
関数iarray_reverseはforを用いて、n個の値が含まれる配列の0番目か
ら順番にi番目とn-1-i番目の値を入れ替えた。また入れ替えるときに一
時的に値を保存するために変数xを用いた。関数expect_iarrayでは
iarray_reverseが正しく実行されているかを調べるために、まずt=1を
用意して、forを使って2つの配列の値を最初から順番に調べ、1つでも
違っていたらt=0にする。終わったらprintfでt=1なら"OK"、t=0なら
"NG"を出力する。

考察:
expect_iarrayでは、配列が逆順になったか確認したあとに、もう一度
iarray_reverseをしなければいけないことが分かった。そうしないと次
のテストケースでNGが出てしまう。これは、iarray_reverseはmainで作っ
た配列を、そのメモリをそのまま使って動作しているからであると考え
られる。

演習2a. 2つの配列の値を交互に出力する。

プログラム:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
int *ivec_alternately(int *a, int *b) {
  int *c = ivec_new(a[0]+b[0]), s_size, l_size, i;
  if(a[0] < b[0]) { s_size = a[0]; l_size = b[0]; }
  else { s_size = b[0]; l_size = a[0]; }
  for(i = 1; i <= s_size; ++i) { c[2*i-1] = a[i]; c[2*i] = b[i]; }
  for(i; i <= l_size; ++i) {
    if(l_size == a[0]) { c[i + s_size] = a[i]; }
    else { c[i + s_size] = b[i]; }
  }
  return c;
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
  int a[] = {2,1,3}, b[] = {4,2,4,5,6}, c[] = {6,1,2,3,4,5,6};
  int *p = ivec_alternately(a, b);
  expect_iarray(p, c, 7, "[1,3] [2,4,5,6] -> [1,2,3,4,5,6]");
  return 0;
}


実行結果:

[n1910513@sol 02]$ ./a.out
OK [1,3] [2,4,5,6] -> [1,2,3,4,5,6]
  6  1  2  3  4  5  6
  6  1  2  3  4  5  6

説明:
ivec_alternatelyでは、ivec_newで2つの配列の長さを足した長さの配
列を作り、2つの配列の短いほうの長さをs_size、長い方をl_sizeとし
た。1つめのforでl_sizeの分だけ交互に値を入れた。2つめのforで余っ
ている値を入れた。

考察:
ivec_alternatelyの1つめのforでは、i=1を最初に書いたが、2つめの
forではiだけを書いても正常に動作した。これはivec_alternatelyの最
初にintを宣言しているからであるから成り立つと考えられる。また、1
つめのforにおいて++iでi=s_sizeまで増えているから2つめのiはs_size
が最初から入っていると考えられる。

アンケート:
Q1.代入された値と番地を区別することだと思う。
Q2.テストの作り方の工夫が足りないと思う。
Q3.ポインタの理解が足りていないことに気づいた。

n1910519 2b:○ Mon May 25 13:41:12 2020



演習1,b
配列の並び順を逆順にする関数を作成する。

コード
#include<stdio.h>
#include<stdbool.h>

void iarray_read(int *a, int n){
    for(int i=0; i<n; ++i){
        printf("%d> ",i+1); scanf("%d",a+i);
    }
}

void iarray_print(int *a, int n){
    for(int i=0; i<n; ++i){printf(" %2d", a[i]);}
    printf("\n");
}

void iarray_reverse(int *a, int n){
    int x[n],i;
    for(i=n-1; i>=0;--i){
        x[n-i-1]= a[i]; 
    }
    for(i=0;i<n;++i){
        a[i] = x[i];
    }
}

bool iarray_equal(int *a,int *b, int n){
    for(int i=0; i<n; ++i){
        if(a[i] != b[i]){return false;}
    }
    return true;
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}

int main(void){
    int a[]= {8,5,2,4,1}, b[]= {1,4,2,5,8},c[]={0,1,0,2,0},d[]={0,2,0,1,0},e[]= {1,2},f[]={2,1},g[]={1},h[]={1};
    iarray_reverse(a,5);
    expect_iarray(a,b,5, "85241 -> 14258");
    iarray_reverse(c,5); expect_iarray(c,d,5,"01020 -> 02010");
    iarray_reverse(e,2); expect_iarray(e,f,2,"12 -> 21");
    iarray_reverse(g,1); expect_iarray(g,h,1,"1 -> 1");
    return 0;
}

実行例
OK 85241 -> 14258
  1  4  2  5  8
  1  4  2  5  8
OK 01020 -> 02010
  0  2  0  1  0
  0  2  0  1  0
OK 12 -> 21
  2  1
  2  1
OK 1 -> 1
  1
  1

説明
まず、配列の逆順を代入する配列xを用意した。それそれの各要素を代
入するときにfor文を使って xに逆順を代入した。この結果、求めたい
要素のn番目a[n]がx[n]に入っているので、for文を使ってa[n]にx[n]を
代入した。

考察
要素数が1の時は逆順も変わらないので確認しなくても良さそうだった
が、一応確認したところOKだった。しかし、プログラムをテストする時
は多分大丈夫だからテストしないのではなく、試しにテストをしておく
ことが大切だとわかった。
私は配列で前の要素が空であるのに次の要素を代入できるか自信がなかっ
たため、確実に実行できるようにx[n-i-1]= a[i];として最初の要素か
ら代入したが、より良い方法があったかもしれない。


演習1d
2つの同じ長さの配列を受け取り、2番目の各要素の値を1番目の配列
の各要素にたしこむ関数を作成する

コード
#include<stdio.h>
#include<stdbool.h>

void iarray_read(int *a, int n){
    for(int i=0; i<n; ++i){
        printf("%d> ",i+1); scanf("%d",a+i);
    }
}

void iarray_print(int *a, int n){
    for(int i=0; i<n; ++i){printf(" %2d", a[i]);}
    printf("\n");
}

bool iarray_equal(int *a,int *b, int n){
    for(int i=0; i<n; ++i){
        if(a[i] != b[i]){return false;}
    }
    return true;
}

void iarray_add(int *a, int *b, int n){
    int i;
    for(i=0; i<n; ++i){
        *(a+i) = *(a+i) + *(b+i);
    }
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void){
    int a[]={8,5,2,4,1}, b[]= {1,1,2,2,3}, c[]={9,6,4,6,4},d[]={10,7,6,8,7},e[]={99,99,99},f[]={1,1,1},g[]={100,100,100};
    iarray_add(a,b,5); expect_iarray(a,c,5, "85241+11223 -> 96464");
    iarray_add(c,b,5); expect_iarray(c,d,5,"96464+11223 -> 107687");
    iarray_add(e,f,3); expect_iarray(e,g,3,"999999+111 -> 100100100" );
    return 0;
}

実行結果
OK 85241+11223 -> 96464
  9  6  4  6  4
  9  6  4  6  4
OK 96464+11223 -> 107687
 10  7  6  8  7
 10  7  6  8  7
OK 999999+111 -> 100100100
 100 100 100
 100 100 100

説明
配列aのi番目の要素に、配列aのi番目の要素と配列bのi番目の要素の和
を代入した。各要素について同じ操作をするため、for文を使った。

考察
最初は配列をa[i]と書いていたが、今回習ったポインタを使って*(a+i)
= *(a+i) + *(b+i); と書けることを確認して理解することができた。
教科書のテストケースではそれぞれの値が10以上になる場合確認してい
なかったため、自分でテストして正しい結果が得られることを確認した。
この場合、表示が実際の足し算と違うことに違和感を感じたが、配列の
各要素に10以上の値も代入できることが関係していると考えた。

アンケート
Q1,右辺と左辺で働きが違うことです。
Q2,短くわかりやすいコードを書くことです。
Q3,次も頑張ります。

r1910720 2b:△ Mon May 25 19:21:49 2020


プログラミング通論 #2 課題レポート1b 提出日時:2020/5/25

課題1:配列の並び順を逆順にする関数を作成する。

ソースコード:
#include <stdio.h>
void iarray_printf(int *a, int n){
  for(int i = 0; i < n; i++){
    printf("%2d",a[i]);
  }
  printf("\n");
}
void iarray_reverse(int *a,int n){
  int tmp;
  for(int i=0;i<n/2;i++){
    tmp = a[i];
    a[i] = a[n-i-1];
    a[n-i-1] = tmp;
  }
}
void expect_iarray(int *i1, int *i2,int n, char *msg) {
  printf("%s %s\n", (*i1==*i2)?"OK":"NG", msg);
  iarray_printf(i1,n);
  iarray_printf(i2,n);
}
int main(void){
  int a[] = {8,4,2,5,1}, b[] = {1,5,2,4,8};
  iarray_reverse(a, 5);
  expect_iarray(a, b, 5, "84251 -> 15248");
  return 0;
}

実行例:
./a.out
OK 84251 -> 15248
 1 5 2 4 8
 1 5 2 4 8

説明:ある配列を与え、それを逆転するのがiarray_reverse関数の役目
だ。そしてテストして、OKが出ました。

考察:配列の2つの要素を転換するため、今回のreverse関数内でtmpと
いうボックスを使いました。

課題2:配列を昇順に整列する関数を作成する。

ソースコード:
#include <stdio.h>
void iarray_printf(int *a, int n){
  for(int i = 0; i < n; i++){
    printf("%2d",a[i]);
  }
  printf("\n");
}
void iarray_sort(int *a,int n){
  int tmp,i,j;
  for(int i=0;i<n-1;i++){
    int min=i;
    for(j=i+1;j<n;j++){
    if(a[j]<a[min]){
      min=j;
    }
    if(min!=i){
    tmp = a[i];
    a[i] = a[min];
    a[min] = tmp;
    }
    }
  }
}
void expect_iarray(int *i1, int *i2,int n, char *msg) {
  printf("%s %s\n", (*i1==*i2)?"OK":"NG", msg);
  iarray_printf(i1,n);
  iarray_printf(i2,n);
}
int main(void){
  int a[] = {8,5,2,4,1}, b[] = {1,4,2,5,8};
  iarray_sort(a, 5);
  expect_iarray(a, b, 5, "85241 -> 14258");
  return 0;
}

説明:今回ではある配列を転置すること。iarray_sort関数を作りまし
た。i,jは配列の何番目を表すこと。また、tmpは数値を入れるボックス
である。

実行例:
  ./a.out
OK 85241 -> 14258
 1 4 2 5 8
 1 4 2 5 8
  
考察:今回のiarray_sortに2つのfor文を使いました。1つのfor文を
使うと、転置した配列の先頭に0が出て、NGになった。

アンケート:Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。
  *をつけるところが重要だと思う。
Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に付いていないと思うことは何ですか。
  基本的な概念、思考の仕方。
Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
今回の課題では新しい内容で、きちんと理解しないといけない。自分が
まだわからないことがあるため、今後も勉強し続ける。

s1910346 2b:○ Mon May 25 01:35:03 2020


提出日時 2020/5/25/1:34
演習1d  同じ長さの配列を受け取り各要素の和をいれた配列に書き換える
プログラムを作成した.
----------------------------------
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>

char *bool2str(bool b) { return b ? "true" : "false"; }
void expect_bool(bool b1, bool b2, char *msg) {
    printf("%s %s:%s %s\n", (b1==b2)?"OK":"NG",
            bool2str(b1), bool2str(b2), msg);
}

bool iarray_equal(int *a, int *b, int n) {
    for(int i = 0; i < n; ++i) {
        if(a[i] != b[i]) { return false; }
    }
    return true;
}

void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

void expect_iarray(int *a, int *b, int n, char *msg) {
    printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
    iarray_print(a, n); iarray_print(b, n);
}

void iarray_add(int *a, int *b, int n){
    for(int i = 0;i < n; i++){
        a[i] += b[i];
    }
}


int main(void) {
    int a[] = {8,5,2,4,1}, b[] = {1,1,2,2,3}, c[] = {9,6,4,6,4};
    int d[] = {5,5,7,4,-10, 9, 11}, e[] = {1,9,2,-10,3, 4, -9}, f[] = {6, 14, 9, -6, -7, 13, 2};
    iarray_add(a, b, 5); expect_iarray(a, c, 5, "85241+11223 -> 96464");
    iarray_add(d, e, 7); expect_iarray(d, f, 7, "5,5,7,4,-10, 9, 11 + 1,9,2,-10,3, 4, -9-> 6, 14, 9, -6, 7, 13, 2");
}
--------------------------------
実行結果
$ ./a.out
OK 85241+11223 -> 96464
  9  6  4  6  4
  9  6  4  6  4
OK 5,5,7,4,-10, 9, 11 + 1,9,2,-10,3, 4, -9-> 6, 14, 9, -6, 7, 13, 2
  6 14  9 -6 -7 13  2
  6 14  9 -6 -7 13  2

----------------------------------
プログラムの説明
iarray_addは,配列の先頭ポインタを引数として
配列の長さ分繰り返しaに足すことで機能を実現している.
2つのテストケースを用いて機能の正確性について確認した.
考察
最初はテストケースの打ち間違いによるNGを理解できず
苦しんでしまう状況が出た.テストケースの作成は今回の用に
間違えてしまうこともあり得るのでテストケース生成の半自動化を
考える必要があると思った.また自分の使ったテストケースは
長さも少ないのでミスをすぐに発見できたが手作業でテストケースを
制作するのはミスもはらんでいるし小規模にしか行うことができないので
すでに信頼性のあるプログラムを使って新しい機能を試すためのテストケースを
作るというワークフロ-があるのではないかと思った.

----------------------------------
演習3d 複数の値をputできていつの時点でもそれまでの数値の
うちの最大値と最小値がgetできる機能
----------------------------------
#include <stdio.h> //maxmin.c
#include "maxmin.h"

void expect_int(int i1, int i2, char *msg) {
  printf("%s %d:%d %s\n", (i1==i2)?"OK":"NG", i1, i2, msg);
}

int main(){
    int a[] = {-100, 200, 0, 9, 100};
    int b[] = {0};
    int c[] = {7, 7, 6, 6, 2, 90, 0};
    struct mmseq* s1 = mmseq_new();
    struct mmseq* s2 = mmseq_new();
    struct mmseq* s3 = mmseq_new();
    for(int i = 0;i < 7;i++){
        if(i < 5)mmseq_put(s1, a[i]);
        if(i == 0)mmseq_put(s2, b[i]);
        mmseq_put(s3, c[i]);
    }
    expect_int(mmseq_get_max(s1), 200, "-100, 200, 0, 9, 100");
    expect_int(mmseq_get_min(s1), -100, "-100, 200, 0, 9, 100");
    expect_int(mmseq_get_max(s2), 0, "0");
    expect_int(mmseq_get_min(s2), 0, "0");
    expect_int(mmseq_get_max(s3), 90, "7, 7, 6, 6, 2, 90, 0");
    expect_int(mmseq_get_min(s3), 0, "-100, 200, 0, 9, 100");
}
----------------------------------
#include <stdlib.h>//maxmin.c
#include <limits.h>
#include "maxmin.h"

struct mmseq{int count; int max; int min;};
struct mmseq *mmseq_new(){
    struct mmseq *r = (struct mmseq*)malloc(sizeof(struct mmseq));
    r->count = 0;
    return r;
}
int mmseq_get_max(struct mmseq *r){
    if(r->count == 0) return INT_MIN;
    return r->max;
}
int mmseq_get_min(struct mmseq *r){
    if(r->count == 0) return INT_MAX;
    return r->min;
}
void mmseq_put(struct mmseq *r, int num){
    if(r->count == 0)r->max = r->min = num;
    if(r->max < num) r->max = num;
    if(r->min > num) r->min = num;
    r->count++;
    return;
}

----------------------------------
struct mmseq *mmseq_new();//maxmin.h
int mmseq_get_max(struct mmseq *r);
int mmseq_get_min(struct mmseq *r);
void mmseq_put(struct mmseq *r, int num);
----------------------------------
実行結果
$ ./a.out
OK 200:200 -100, 200, 0, 9, 100
OK -100:-100 -100, 200, 0, 9, 100
OK 0:0 0
OK 0:0 0
OK 90:90 7, 7, 6, 6, 2, 90, 0
OK 0:0 -100, 200, 0, 9, 100

----------------------------------

プログラムの説明
    maxmin.hでは,mmseq_new,mmseq_get_max
    ,mmseq_get_min, mmseq_putを定義した.
    mmseq_newでは,macmin.c内で定義している構造体
    mmseqのポインタを作り,countを0にしている.
    mmseq構造体は,count, max, minをメンバとして
    持っていてそれぞれ数を入れた個数,最大値,最小値
    をそれぞれ保持している.
    mmseq_get_maxでは初期値が入っていない場合は,
    intの最大値を,それ以外の場合はmaxの値を返す.
    mmseq_get_minも同様である.mmseq_putでは,mmseq構造体の
    ポインタ,最大値最小値を比較するための数値numを引数として
    持っている.数値が初めてputされる場合は,max,min,ともに
    numで初期化しているそれ以外の場合はmaxを超える場合,
    minを下回る場合でこれらの更新を行っている.
    テストケースでは,
    int a[] = {-100, 200, 0, 9, 100};
    int b[] = {0};
    int c[] = {7, 7, 6, 6, 2, 90, 0};
    を用いておこなった.
    意図としては,正負を含む数列,数字一つの数列,
    同じ数を含む数列の代表として行った.
    評価は最大値,最小値をそれぞれ計算してテストケースの
    予想される答えを満たしているかどうかの確認を行った.
考察
    ポインタを利用するうことで 構造体自体は,Userから秘匿した状態で, 
    データを扱うことができるのが面白いと思った.
    ただ直感的にわかりやすいのは,object指向の言語が用いるクラスの機能
    であり,自由度が高い分プログラマの力に大きくよる言語だと思った.
アンケート
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。
メモリーの確保をまずしてからポインタで指すようにすること.
演習課題を行う仮定でmallocを忘れてしまって実行エラーが起こること
がよくあった
    
Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に
付いていないと思うことは何ですか。
エラーにうまく対応する方法.エラーが起きた時にコメントアウトしていく方法
しかデバッグの方法を知らないのでもう少しうまくデバッグできるようになりたい.

Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ
ポインタがうまく使いこなせるようになるとプログラムの幅が広がって
いくように感じた.
演習を通じてポインタに親しむことができたように思う.
苦手意識のあったポインタの基本的な使い方をしっかりと抑えることが
できてよかった.

t1810743 2b:△ Mon May 25 23:58:14 2020


プログラミング通論 レポート2b

演習2 b.1 つの列を受け取り、その内容を逆順にした列を返すを作成する。

・プログラムコード
int *ivec_reverse(int *a){
    int *c = ivec_new(a[0]);
    for(int i=0; i<c[0]; ++i){
        c[i+1] = a[a[0] - i];
    }
    return c;
}

・プログラムの説明
同じ長さの可変調配列cを用意する。
配列aの後ろから逆順になるように配列cに代入し、cを返す。

・単体テスト
int a[] = {5,8,5,2,4,1}, b[] = {5,1,4,2,5,8};
expect_iarray(ivec_reverse(a) ,b, 5, "85241 -> 14258");
int c[] = {0}, d[] = {0};
expect_iarray(ivec_reverse(c), d, 0, " -> ");
int e[] = {8, 8,5,2,4,1,0,1,5}, f[] = {8, 5,1,0,1,4,2,5,8};
expect_iarray(ivec_reverse(e), f, 8, "85241015 -> 51014258");

・実行結果
OK 85241 -> 14258
  1  4  2  5  8
  1  4  2  5  8
OK  -> 


OK 85241015 -> 51014258
  5  1  0  1  4  2  5  8
  5  1  0  1  4  2  5  8

・考察
大きさ5と8の配列は正確にできた。
大きさ0の配列も正確に実行することができた。

・レビュー課題
友人:for文一個だけでいけると思います。
void iarray_reverse(int *a, int n){
    int tmp;
    for(int i=0; i<n/2; ++i){
        tmp = a[i]
        a[i] = a[n-i-1]
        a[n-i-1] = tmp;
    }
}


演習2 d.2 つの昇順に整列ずみの列を受け取り、両方の列をマージした昇順の列を返す

プログラムコード
int *ivec_merge(int *a, int *b){
    int *c = ivec_new(a[0] + b[0]);
    int i = 1, j = 1, k = 1;

    while(i <= c[0]){
        if(j > a[0]){
            c[i] = b[k];
            i++;
            k++;
            continue;
        }
        if(k > b[0]){
            c[i] = a[j];
            i++;
            j++;
            continue;
        }

        if(a[j] < b[k]){
            c[i] = a[j];
            j++;
        }else{
            c[i] = b[k];
            k++;
        }
        i++;
    }
    return c;
}

・説明
二つの配列の合計の大きさの配列を作る
Aとbを比べて小さい方をcに代入をしていく
配列のインデックスを超えないようにする。

・単体テスト
    int a[] = {3,3,5,9}, b[] = {3,1,4,6}, c[] = {6,1,3,4,5,6,9};
    int *p = ivec_merge(a, b);
    expect_iarray(p, c, 5, "[3,5,9] + [1,4,6] -> [1,3,4,5,6,9]");

    int d[] = {3,3,5,9}, e[] = {5,1,3,4,6,10}, f[] = {8,1,3,3,4,5,6,9,10};
    p = ivec_merge(d, e);
    expect_iarray(p, f, 5, "[3,5,9] + [1,3,4,6,10] -> [1,3,3,4,5,6,9,10]");

・実行結果
OK [3,5,9] + [1,4,6] -> [1,3,4,5,6,9]
  1  3  4  5  6  9
  1  3  4  5  6  9
OK [3,5,9] + [1,3,4,6,10] -> [1,3,3,4,5,6,9,10]
  1  3  3  4  5  6  9 10
  1  3  3  4  5  6  9 10

・考察
配列の先頭にサイズを格納するのを忘れがちになる。


Q1. アドレス、ポインタを使うプログラムで注意すべきこ
とは何だと思いますか。
今アドレスを操作しているのか、ポインタなのか値なのかを明確にするべき
Q2. ここまでのところで、プログラムを作るときに重要だか
゙自分で身に付いていないと思うこ
とは何ですか。
まだアドレスとポインタと配列の関係がごっちゃになる
Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
楽しかった。

w1810714 2b:○ Mon May 25 23:05:06 2020


個人作業, 提出日

演習1b. 配列の並び順を逆順にする。
コード
#include <stdio.h>
#include <stdbool.h>
void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void iarray_reverse(int *a, int n) {
  int t[n];
  for(int i = 0; i < n; ++i) { t[i] = a[n-1-i]; }
  for(int i = 0; i < n; ++i) { a[i] = t[i]; }
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
  int a[] = {8, 5, 2, 4, 1}, b[] = {1, 4, 2, 5, 8};
  int c[] = {2, 6, 7, 3, 9, 5}, d[] = {5, 9, 3, 7, 6, 2};
  iarray_reverse(a, 5); expect_iarray(a, b, 5, "85241 -> 14258");
  iarray_reverse(c, 6); expect_iarray(c, d, 6, "267395 -> 593762");
  return 0;
}

実行例(単体テストを含む)
[w1810714@sol 2]$ ./a.out
OK 85241 -> 14258
  1  4  2  5  8
  1  4  2  5  8
OK 267395 -> 593762
  5  9  3  7  6  2
  5  9  3  7  6  2

説明
iarray_reverseでは、配列t[]に一旦逆並びの配列を入れた後a[]をt[]
と同期することでa[]そのものを逆並びにすることを可能にしている。

考察
「一時配列」t[]を使わないやり方としてはa[i]とa[n-1-i]入れ替える
操作をi = 0からi = n-1までiを1ずつ増やしながら順に行えばよいだろ
う。ただしその場合も一時変数を用いる必要がある。

演習1c. 配列を照準に整列する。
コード
#include <stdio.h>
#include <stdbool.h>
void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i<n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void iarray_sort(int *a, int n) {
  int temp;
  for(int i = 0; i<n-1; ++i) {
    for(int j = i+1; j<n; ++j) {
      if(a[i]>a[j]) {
        temp = a[i];
        a[i] = a[j];
        a[j] = temp;
      }
    }
  }
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
  int a[] = {8, 5, 2, 4, 1}, b[] = {1, 2, 4, 5, 8};
  int c[] = {2, 6, 7, 3, 9, 5}, d[] = {2, 3, 5, 6, 7, 9};
  iarray_sort(a, 5); expect_iarray(a, b, 5, "85241 -> 12458");
  iarray_sort(c, 6); expect_iarray(c, d, 6, "267395 -> 235679");
  return 0;
}

実行例(単体テストを含む)
[w1810714@sol 2]$ ./a.out
OK 85241 -> 12458
  1  2  4  5  8
  1  2  4  5  8
OK 267395 -> 235679
  2  3  5  6  7  9
  2  3  5  6  7  9

説明
配列aのi+1番目(iは0からn-1まで1ずつ増やす)の数について、その数よ
り小さい数字が配列aの添字の大きい側にあれば場所を入れ替える、と
いう操作をしている。

考察
iarray_sort内の一つ目のfor文では繰り返しをi = 0からi = n-2までし
か行なっていない。これは大きさnの配列のn番目の数字には比較すべき
「より添字の大きい数字」が存在しないため比較を行う必要がないから
である。

アンケート
1. どこに格納したのか、取り出したいものは何なのか等を図を描いて確認すること。
2. 図を描く癖がまだついておらず、頭の中だけで処理しようとして詰まることがある。
3. 次は演習2を解きます

w1910722 2b:○ Mon May 25 14:21:44 2020


学籍番号 1910722
個人作業
提出日時 2020年5月25日

一つ目の課題

再掲
演習2のb。一つの列を受け取り、内容を逆順にした列を返すプログラム。
ソースコード
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
int *ivec_reverse(int *a) {
  int *b = ivec_new(a[0]);
  for(int i = 0; i < a[0]; ++i) { 
    b[a[0]-(i+1)] = a[i+1]; 
   }
  return b;
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
  int a[] = {5,1,2,3,4,5}, b[] = {5,4,3,2,1}; 
  int c[] = {3,1,1,3}, d[] = {3,1,1};
  int e[] = {4,2,5,6,6}, f[] = {6,6,5,2};
  int g[] = {2,6,9}, h[] = {9,6};
  
  int *p = ivec_reverse(a);
  expect_iarray(p, b, 5, "[1,2,3,4,5] to [5,4,3,2,1]");
  int *q = ivec_reverse(c);
  expect_iarray(q, d, 3, "[1,1,3] to [3,1,1]");
  int *r = ivec_reverse(e);
  expect_iarray(r, f, 4, "[2,5,6,6] to [6,6,5,2]");
  int *s = ivec_reverse(g);
  expect_iarray(s, h, 2, "[6,9] to [9,6]");
  return 0;
}
実行例
OK [1,2,3,4,5] to [5,4,3,2,1]
  5  4  3  2  1
  5  4  3  2  1
OK [1,1,3] to [3,1,1]
  3  1  1
  3  1  1
OK [2,5,6,6] to [6,6,5,2]
  6  6  5  2
  6  6  5  2
OK [6,9] to [9,6]
  9  6
  9  6
説明

ある一つの配列を受け取り、内容を逆順にするプログラム。今回は、配
列の先頭にいくつの要素が入っているのかどうかを記録した。よって逆
順にするのは配列の要素の2個目以降であり、出力も同様である。int
*ivec_reverseで逆順にするための処理を記述した。最初に、与えられ
た配列a[]と同じ要素数(a[0]の値と同じ要素数)の配列b[]を作成し、
for文によってb[]の最後尾からa[1]以降の値を次々に代入していくこと
によってサイズが同じ、内容が逆順の配列を作ることができた。

考察

for文内ではb[a[0]-(i+1)] = a[i+1]というように記述しているが、bの
[]内にsizeof(a)で取得した値を指定した際、コンパイルはできたもの
の値がおかしくなってしまった。sizeofで得られる値は配列の要素数と
は異なるということは分かったのだが、どのように異なるのかがよく分
からなかったために、配列の先頭に要素数を用意するという少し面倒な
手段を用いた。

2つ目の課題
再掲
演習3のa。等差数列を初項に戻す機能、cdseq_resetを追加した。
ソースコード
// cdseq.h --- constant difference sequence API.
struct cdseq *cdseq_new(int s, int d);
int cdseq_get(struct cdseq *r);
void cdseq_free(struct cdseq *r);
int cdseq_reset(struct cdseq *r);

// cdseq.c -- cdseq implementation.
#include <stdlib.h>
#include "cdseq.h"
struct cdseq { int value, diff,first; };
struct cdseq *cdseq_new(int s, int d) {
  struct cdseq *r = (struct cdseq*)malloc(sizeof(struct cdseq));
  r->value = s; r->diff = d; r->first = s; return r;
}
int cdseq_get(struct cdseq *r) {
  int v = r->value; 
  r->value += r->diff; 
  return v;
}
void cdseq_free(struct cdseq *r) {
  free(r);
}
int cdseq_reset(struct cdseq *r){
   r->value = r->first; return r->value; 
}

// test_cdseq_1.c --- unit test for cdseq.
#include <stdio.h>
#include "cdseq.h"
void expect_int(int i1, int i2, char *msg) {
  printf("%s %d:%d %s\n", (i1==i2)?"OK":"NG", i1, i2, msg);
}
int main(void) {
  struct cdseq *s = cdseq_new(3, 2);
  expect_int(cdseq_get(s), 3, "3+2*0 = 3");
  expect_int(cdseq_get(s), 5, "3+2*1 = 5");
  expect_int(cdseq_reset(s), 3, "reset"); 
  expect_int(cdseq_get(s), 3, "3+2*0 = 3");
  expect_int(cdseq_get(s), 5, "3+2*1 = 5");
  expect_int(cdseq_get(s), 7, "3+2*2 = 7");
  expect_int(cdseq_reset(s), 3, "reset");
  return 0;
}
実行例
OK 3:3 3+2*0 = 3
OK 5:5 3+2*1 = 5
OK 3:3 reset
OK 3:3 3+2*0 = 3
OK 5:5 3+2*1 = 5
OK 7:7 3+2*2 = 7
OK 3:3 reset
説明

等差数列の初項に戻すプログラム。cdseq.cのstruct cdseqに新たに初
項の値を記録するfirstを追加した。r->valueと行っている処理は同じ
だが、r->valueは公差が足されていって値が変動してしまうため、最初
のsの値をずっと保存するためにfirstを追加した。cdseq_resetの内容
は、r->valueに初項の値が代入し、そのままr->valueの値を返すという
シンプルなものである。これによってcdseq_getで公差が足されていっ
たvalueの値を一番最初の値である初項に戻すことができる。

考察
cdseq.hにcdseq_resetを追加せずにコンパイルした際に
 warning: implicit declaration of function 'cdseq_reset'; did you mean 'cdseq_free'? [-Wimplicit-function-declaration]
   expect_int(cdseq_reset(s), 2, "reset");
              ^~~~~~~~~~~
              cdseq_free

という警告文が表示された。しかし、./a.exeを入力するとちゃんとプ
ログラムは動いていたため、どこが警告されているのかよく理解できな
かった。cdseq.hにcdseq_resetを追加するとこの警告文は表示されなく
なったため、解決はしたのだろうがあまり腑には落ちなかった。

アンケート
Q1. なんとなくで扱うのではなく、きちんとどこを参照しているのかを
理解しながら扱うこと。
Q2.ポインタとアドレスへの理解。
Q3.正直、まだポインタが何なのかよく分かっていないため今後が不安。

w1910726 2b:○ Tue May 19 15:29:15 2020


学籍番号 1910726
個人作業
提出日時 2020/5/19

演習1e
ソースコード
#include<stdlib.h>
#include<stdio.h>

int iarray_inject(int *a, int n, int (*fp)(int, int));
void expect_int(int i1, int i2, char *msg);
int iarray_max(int a, int b);
int iarray_sum(int a, int b);

int main()
{
    int (*iadd)(int a, int n) = iarray_sum;
    int (*imax)(int a, int n) = iarray_max;
    int a[] = {8,5,2,4,1};
    expect_int(iarray_inject(a, 5, iadd), 20, "8+5+2+4+1");
    expect_int(iarray_inject(a, 5, imax), 8, "max(8,5,2,4,1)");
    return 0;
}

int iarray_inject(int *a, int n, int (*fp)(int, int))
{
    int answer = a[0];
    for (int i = 1; i < n; i++) {answer = fp(answer, a[i]);}
    return answer;
}

void expect_int(int i1, int i2, char *msg) { printf("%s %d:%d %s\n", (i1==i2)?"OK":"NG", i1, i2, msg);}

int iarray_max(int a, int b) {return (a > b)? a : b;}

int iarray_sum(int a, int b) {return a + b;}

説明

まず、二つの数を比較し大きい方を出力するiarray_max関数を三項演算
子を用いて作成し次に二つの数の和を出力するiarray_sum関数を作成し
た。また、iarray_inject関数を作成し仮変数としてint型のポインタと
その大きさを表すn、上の関数のアドレスが代入できる関数ポインタfp
を用意し、上の関数のどちらが代入されても正常に動作するようにプロ
グラムを作成した。そして、main内で関数ポインタを二つ用意し、それ
ぞれに上で作成した関数を代入し、それを用いてiarray_inject関数を
動作させその結果をexpect_int関数で出力し終了とした。

考察

関数ポインタというものをうまく活用できれば、長々と書かなくてはい
けないプログラムにおいて関数の名前を一つに統一することで分かりや
すく簡潔に書くことが可能になると感じた。また、三項演算子は、短く
コードを書くという点で非常に有用だと感じる。

単体テスト
int main()
{
    int (*iadd)(int a, int n) = iarray_sum;
    int (*imax)(int a, int n) = iarray_max;
    int a[] = {8,5,2,4,1};
    int b[] = {1,9,0,8,3,2,-9,-5};
    int c[] = {0,-1,-4,-8,6,6,6,0,-4};
    int d[] = {0}; 
    expect_int(iarray_inject(a, 5, iadd), 20, "8+5+2+4+1");
    expect_int(iarray_inject(a, 5, imax), 8, "max(8,5,2,4,1)");
    expect_int(iarray_inject(b, 8, iadd), 9, "1+9+0+8+3+2+(-9)+(-5)");
    expect_int(iarray_inject(b, 8, imax), 9, "max(1,9,0,8,3,2,-9,-5)");
    expect_int(iarray_inject(c, 9, iadd), 1, "0+(-1)+(-4)+(-8)+6+6+6+0+(-4)");
    expect_int(iarray_inject(c, 9, imax), 6, "max(0,-1,-4,-8,6,6,6,0,-4)");
    expect_int(iarray_inject(d, 1, iadd), 0, "0");
    expect_int(iarray_inject(d, 1, imax), 0, "max(0)");
    return 0;
}

実行例
PS C:\Users\miang\Desktop\info_uec_tsk> ./a.exe
OK 20:20 8+5+2+4+1
OK 8:8 max(8,5,2,4,1)
OK 9:9 1+9+0+8+3+2+(-9)+(-5)
OK 9:9 max(1,9,0,8,3,2,-9,-5)
OK 1:1 0+(-1)+(-4)+(-8)+6+6+6+0+(-4)
OK 6:6 max(0,-1,-4,-8,6,6,6,0,-4)
OK 0:0 0
OK 0:0 max(0)


演習2a
ソースコード
#include<stdio.h>
#include<stdlib.h>

void AlterArray(int *a, int a_leng, int*b, int b_leng, int *ans_arr);

int main()
{
    int a[] = {1, 2, 3}; int b[] = {4, 5, 6, 7, 8, 9};
    int a_leng = (sizeof(a) / sizeof(int)); int b_leng = (sizeof(b) / sizeof(int));
    int *ans = (int*)malloc(sizeof(int) * (a_leng + b_leng));
    AlterArray(a, a_leng, b, b_leng, ans);
    for (int i = 0; i < a_leng + b_leng; i++) {printf("%d ", ans[i]);}
    free(ans);
    return 0;

}

void AlterArray(int *a, int a_leng, int*b, int b_leng, int *ans_arr)
{
    int ans_index = 0;
    int smaller_leng = (a_leng < b_leng)? a_leng : b_leng;
    int bigger_leng = (a_leng > b_leng)? a_leng : b_leng;
    int *bigger_arr = (bigger_leng == a_leng)? a : b; 

    for (int i = 0; i < smaller_leng; i++)
    {
        ans_arr[ans_index++] = a[i];
        ans_arr[ans_index++] = b[i];
    }
    for (int i = smaller_leng; i < bigger_leng; i++) {ans_arr[ans_index++] = bigger_arr[i];}
}

説明

関数AlterArrayで二つの配列とその大きさ、また交互にしたものを格納
するための配列ans_arrを仮引数とした。ans_arrはmain内であらかじめ
mallocで二つの配列の大きさの合計をその大きさとする配列とした。そ
して、三項演算子を用いてsmaller_lengとbigger_leng、bigger_arrに
それぞれ小さい方の配列の大きさ、大きい方の配列の大きさ、配列の大
きさが大きい方の配列のアドレスを格納した。それらとfor文を用いて
配列ans_arrに配列a,bを交互に代入していく。ans_arrのインデックス
に関してはあらかじめ0を代入したans_indexに後置インクリメントを施
すことによって交互に大きさが小さい配列の大きさの分だけ繰り返した。
そして、変数ans_indexとfor文を用いて大きい方の配列の残りの要素を
配列ans_arrに代入していった。main内で関数AlterArrayに必要な要素
を入力し、その結果である配列ansをprintfで出力した。最後にfreeで
ansを開放し、終了とした。

考察

この課題で後置インクリメントをうまく活用すれば、コードが短く書け
るということが分かった。また、ポインタを配列として用いる場合その
大きさをあらかじめどこかに書くのしておく必要があるため、関数の引
数として用いる場合どうしてもその数が多くなってしまうので、もしポ
インタを配列として多く用いるのならば、その配列と大きさを要素に持
つ構造体を用いて方が管理と引数表示の観点から良いと感じた。

単体テスト
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>

void AlterArray(int *a, int a_leng, int*b, int b_leng, int *ans_arr);
void Checker(int *n1_arr, int n1, int *n2_arr, int n2, bool *isCorrect, int *inp, int *ans);

int main()
{
    int a[] = {1, 2, 3}; int b[] = {4, 5, 6, 7, 8, 9};
    int c[] = {-1, 0, 100}; int d[] = {0, -8, -8, 2, 9, 0};
    int e[] = {0};
    int a_b[] = {1, 4, 2, 5, 3, 6, 7, 8, 9};
    int a_c[] = {1, -1, 2, 0, 3, 100};
    int c_b[] = {-1, 4, 0, 5, 100, 6, 7, 8, 9};
    int b_e[] = {4, 0, 5, 6, 7, 8, 9};

    bool isCorrect = true;

    int *ans_a_b = (int*)malloc(sizeof(int) * 9);
    int *ans_a_c = (int*)malloc(sizeof(int) * 6);
    int *ans_c_b = (int*)malloc(sizeof(int) * 9);
    int *ans_b_e = (int*)malloc(sizeof(int) * 7);

    Checker(a, 3, b, 6, &isCorrect, ans_a_b, a_b);
    Checker(a, 3, c, 3, &isCorrect, ans_a_c, a_c);
    Checker(c, 3, b, 6, &isCorrect, ans_c_b, c_b);
    Checker(b, 6, e, 1, &isCorrect, ans_b_e, b_e);

    free(ans_a_b);
    free(ans_a_c);
    free(ans_c_b);
    free(ans_b_e);
    return 0;

}

void Checker(int *n1_arr, int n1, int *n2_arr, int n2, bool *isCorrect, int *inp, int *ans)
{
    AlterArray(n1_arr, n1, n2_arr, n2, inp);
    for (int i = 0; i < n1 + n2; i++) {printf("%d ", inp[i]);}
    printf("\n");
    for (int i = 0; i < n1 + n2; i++) 
    {
        printf("%d ", ans[i]);
        if(ans[i] != inp[i]) {*isCorrect = !(*isCorrect);}
    }
    printf("\n");
    printf((isCorrect)? "OK" : "NG");
    printf("\n");
}

void AlterArray(int *a, int a_leng, int*b, int b_leng, int *ans_arr)
{
    int ans_index = 0;
    int smaller_leng = (a_leng < b_leng)? a_leng : b_leng;
    int bigger_leng = (a_leng > b_leng)? a_leng : b_leng;
    int *bigger_arr = (bigger_leng == a_leng)? a : b; 

    for (int i = 0; i < smaller_leng; i++)
    {
        ans_arr[ans_index++] = a[i];
        ans_arr[ans_index++] = b[i];
    }
    for (int i = smaller_leng; i < bigger_leng; i++) {ans_arr[ans_index++] = bigger_arr[i];}
}

実行例
PS C:\Users\miang\Desktop\info_uec_tsk> ./a.exe
1 4 2 5 3 6 7 8 9 
1 4 2 5 3 6 7 8 9
OK
1 -1 2 0 3 100
1 -1 2 0 3 100
OK
-1 4 0 5 100 6 7 8 9
-1 4 0 5 100 6 7 8 9
OK
4 0 5 6 7 8 9
4 0 5 6 7 8 9
OK

アンケート
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。

アドレスやポインタを使うときには表記が分かりづらくなるので注意し
てプログラムすることと、ポインタ変数を配列として使う場合許されて
いないメモリへのアクセスを注意すべきだと思いました。

Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に
付いていないと思うことは何ですか。
簡潔に効率よく動くアルゴリズムを考える力が自分に不足していると思います。

Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
引き続き頑張りたいと思います。

w1910727 2b:○ Mon May 25 22:34:59 2020


提出日5/25
課題(1)
演習1-b:配列の並び順を逆順にする関数を作成せよ。

プログラムのソース
#include <stdio.h>
#include <stdbool.h>
int iarray_reverse(int *a, int n) {
   int k[n];
   int i;
  for(int i = 0; i < n; ++i) {
    k[n-1-i] = a[i];	
  }
  for(int i = 0; i < n; ++i) {
    a[i] = k[i];
  }
  }
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
  int a[] = {8,5,2,4,1}, b[] = {1,4,2,5,8};
  iarray_reverse(a, 5); expect_iarray(a, b, 5, "85241 -> 14258");
  int x[] = {1,2,3,2*2,5}, y[] = {5,4,3,3-1,1};
  iarray_reverse(x, 5); expect_iarray(x, y, 5, "12345 -> 54321");
  return 0;
}

プログラムの説明
配列aの要素の並び順を逆にするために、まず新しい配列kを用意し、
forループを用いて配列kを配列aの逆順となるようにする。そして、aの
各要素にkの要素の値を順に代入することで、aの要素を逆順にできるプ
ログラムである。

プログラムの実行例
OK 85241 -> 14258
  1  4  2  5  8
  1  4  2  5  8
OK 12345 -> 54321
  5  4  3  2  1
  5  4  3  2  1

考察及び分析点

新しい配列kを用いることで、シンプルな表現で逆順にできたと考える。
しかし、このプログラムでは2つのfor文と2つの配列を使っているので、
最後のkの要素をaにそのまま代入するといったやや無駄な動作があると
考えられる。for文もしくは用いる配列を減らせる工夫があると考えら
れる。このような差が処理速度に違いとして現れるのではと考えられる。
また、テストケースに計算を含めてみてもOKであったので、計算を含ん
だ配列も逆順にできると考えられる。

課題(2)
演習2-a:2つの列を受け取り、両方の列の内容を交互に並べた列を返す
プログラムを作成せよ。

プログラムのソース
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}

void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", (a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int *alternate(int *a, int *b){
  int *k = ivec_new(sizeof(a)+sizeof(b));
  for(int i = 0;i < sizeof(k); i++) {
    k[2*i+1] = b[i];
    k[2*i] = a[i];
  }
  return k;
}

int main(void) {
  int a[] = {1,2,3}, b[] = {4,5,6},c[]={1,4,2,5,3,6};
  int *x = alternate(a, b);
  expect_iarray(x, c, 6, "[1,2,3][4,5,6] => [1,4,2,5,3,6]");
  int d[] = {6,5,4,3,2,1}, e[] = {7,8,9,10,11,12},f[]={6,7,5,8,4,9,3,10,2,11,1,12};
  int *y = alternate(d, e);
  expect_iarray(y, f, 12, "[6,5,4,3,2,1][7,8,9,10,11,12] => [6,7,5,8,4,9,3,10,2,11,1,12]");
  return 0;
}

プログラムの説明
二つの配列の要素を交互に並べた配列を返すプログラムである。まずは
二つの配列a,bの要素数を足した要素数の配列kを作った。このkに偶奇
を分けてa,bの要素を格納することで、交互にa,bの要素が並んだ配列k
を作成し、それを返すプログラムである。

プログラムの実行例
OK [1,2,3][4,5,6] => [1,4,2,5,3,6]
  1  4  2  5  3  6
  1  4  2  5  3  6
OK [6,5,4,3,2,1][7,8,9,10,11,12] => [6,7,5,8,4,9,3,10,2,11,1,12]
  6  7  5  8  4  9  3 10  2 11  1 12
  6  7  5  8  4  9  3 10  2 11  1 12

考察及び分析点

このような配列絡みのプログラミングでは、配列の要素数でのエラーが
起きやすいと感じたので、注意が必要だと感じた。要素数が足りないこ
とやオーバーしていることによるエラーを防ぐために、先に大きさを考
慮するのが大切であると考えた。テストケースでは、桁数や要素の数を
増やしても、問題なく動作した。

アンケート
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。
二つの違いを区別することと、用いる値と場所の整理をしておくこと。
Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に
付いて いないと思うことは何ですか。
想定外の入力に対する対応をプログラムにあらかじめ書いておくこと。
Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
ポインタについての理解が甘いので、これからも積極的に用いていきたい。

y1810658 2b:○ Mon May 25 22:32:02 2020


学籍番号:1810658
個人作業
提出日時:2020/5/25

課題1ーa
配列の最大値を求める関数

#include <stdio.h>
#include <stdbool.h>

int iarray_max(int *a, int n){
  int max = *a;
  int i = 1; 
   for(i=1; i<n; i++){
     if(max < *(a+i)){
       max = *(a+i);
      }
    }
   return max; 
}

void expect_int(int i1, int i2, char *msg) {
  printf("%s %d:%d %s\n", (i1==i2)?"OK":"NG", i1, i2, msg);
}
int main(void){
int a[] = {9,0,0,1,2,3}, b[] = {-1,-3,-2,-4,-1}, c[] = {5,4,7,-8,7};
expect_int(iarray_max(a, 6), 9, "9 0 0 1 2 3");
expect_int(iarray_max(a+1, 5), 3, "0 0 1 2 3");
expect_int(iarray_max(b, 5), -1, "-1 -3 -2 -4 -1");
expect_int(iarray_max(c+1, 4), 7, "4 7 -8 7");
return 0;
} 

//実行例
OK 9:9 9 0 0 1 2 3
OK 3:3 0 0 1 2 3
OK -1:-1 -1 -3 -2 -4 -1
OK 7:7 4 7 -8 7

方針
配列の先頭をmaxにおき一つずつずらして比較し更新する。さいごにmax
の値を返す。テストは正負を含めいろいろなパターンを作る。

説明
配列の先頭を最大値におき、最大値と一つ次の数値と比較して最大値を
更新するという作業をループさせる。ループが終了したら最終的な最大
値を返す。

考察
この課題に関しては、配列のメンバの先頭を最大値に設定し、一つずつ
次のものと比較して最大値より大きかったものに関して新たに書き換え
ループが終わると最後に値を返すプログラムを書いた。テストについて
は考えうる\\いろんなパターンを網羅するものを考えた。

課題3ーa
等差数列を初項にもどす機能cdseq_reset

#include <stdlib.h>
#include "cdseq.h"
struct cdseq { int value, diff; };
struct cdseq *cdseq_new(int s, int d) {
 struct cdseq *r = (struct cdseq*)malloc(sizeof(struct cdseq));
 r->value = s; r->diff = d; return r;
}
int cdseq_get(struct cdseq *r) {
 int v = r->value; r->value += r->diff; return v;
}
void cdseq_free(struct cdseq *r) {
 free(r);
}
int cdseq_reset(struct cdseq *r){
 r->value = r->value % r->diff;
}

// test_cdseq_1.c --- unit test for cdseq.
#include <stdio.h>
#include "cdseq.h"

void expect_int(int i1, int i2, char *msg) {
  printf("%s %d:%d %s\n", (i1==i2)?"OK":"NG", i1, i2, msg);
}
int main(void){
 struct cdseq *s = cdseq_new(2, 3);
 expect_int(cdseq_get(s), 2, "2+3*0 = 2");
 expect_int(cdseq_get(s), 5, "2+3*1 = 5");
 cdseq_reset(s);expect_int(cdseq_get(s), 2, "2+3*0 = 2");
 return 0;
}

//実行例
OK 2:2 2+3*0 = 2
OK 5:5 2+3*1 = 5
OK 2:2 2+3*0 = 2

方針
等差数列の公差と余りの関係から初項を返すプログラムを作成する。テ
ストについてはresetの実装が正しく行われているか確認するものを用
意する。

説明
cdseq.cについては変更点はないので、追加したものにだけ説明する。
int cdseq_resetは初項と公差の余りを利用して、その関係から初項を
返すものである。

考察
今回のプログラムで用いた構造体による情報隠蔽の利点について考察す
る。プログラムの内部の情報及び実装が隠蔽されることにより外部から
アクセスすることができず、またその書式形態も外部からは見ることが
できなくなる。そのためソフトウェアなどの信頼度を高めることに役
立つのではないかと考えた。また、この様な特性を持つので、利用者が
細かくプログラムの実装について知る必要がある場合には向かないと考
えた。

アンケート
Q1.アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。
定義を理解していること。
Q2.ここまでのところで、プログラムを作るときに重要だが自分で身に
付いていないと思うことは何ですか。
ポインタの定義がまだあやふやなところ。
Q3.リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
ポインタの定義と使用方法の理解を深める。

y1910662 2b:○ Tue May 19 23:57:02 2020


個人作業
提出日時:2020/05/19

【課題1】演習1b 与えられた列を逆順に並べ替えて出力するプログラム
《作成したプログラム(テスト含む)》
#include <stdio.h>
#include <stdbool.h>

void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}

void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

int iarray_reverse(int *a, int n){
    int s[n];
    for(int i=0;i<n;i++){
        s[i] = a[i];
    }
    for(int i=0;i<n;i++){
        a[i] = s[n-1-i];
    }
    return *a;
}

bool iarray_equal(int *a, int *b, int n) {
    for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
    }
    return true;
}

void expect_iarray(int *a, int *b, int n, char *msg) {
    printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
    iarray_print(a, n); 
    iarray_print(b, n);
}

int main(void) {
    int a[] = {8,5,2,4,1}, b[] = {1,4,2,5,8}, c[] = {4,6,9}, d[] = {9,6,4}, e[] = {3,3,7,7}, f[] = {7,7,3,3};
    iarray_reverse(a, 5); 
    expect_iarray(a, b, 5, "85241 -> 14258");
    iarray_reverse(c, 3);
    expect_iarray(c, d, 3, "469 -> 964");
    iarray_reverse(e, 4);
    expect_iarray(e, f, 4, "3377 -> 7733");
  return 0;
}

《実行結果》
OK 85241 -> 14258
  1  4  2  5  8
  1  4  2  5  8
OK 469 -> 964
  9  6  4
  9  6  4
OK 3377 -> 7733
  7  7  3  3
  7  7  3  3

《プログラムの説明》
配列を逆順に並べかえるプログラムである。
iarray_readは配列の入力を行う関数であり、iarray_printは配列の出
力を行う関数である。これらはテキストにもあった通り、for文で配列
の要素を1つ1つ見てscanやprintを行う。iarray_reverseは配列と配列
の要素数を与えると、与えられた配列の要素を逆順に並べ替えて返す関
数である。
まず与えられた配列(ここではa)を格納する要素数nの配列sを作り、for
文を用いてsのそれぞれの要素にaのそれぞれの要素を代入してaのコピー
を作ったあと、再びfor文を用いてaの各要素にsの各要素を今度は番号
が同じところにではなく逆順になるように代入していき、最終的にすべ
ての要素が逆順に代入されたaを返す仕組みになっている。
iarray_equalは、expect_iarrayに使うための関数で、2つの配列のn番
目までの各要素をfor文を用いて順番に比較していき、すべて一致した
場合にはtrueを、そうでなければfalseを返す関数である。
expect_iarrayはテストに用いるための関数で、配列a,bのn番目までの
要素が一致していた場合にOKを、そうでなければNGを表示し、文字列と
してあらかじめ用意されたテストケースを表示した後、aとbそれぞれの
配列を出力する関数である。
main関数では配列a、c、eとそれらを逆順にした場合の想定解である配
列b、d、fがあらかじめ用意されており、それぞれをiarray_reverseで
逆順にしたあと、この関数がきちんと動作しているかを確認するため返っ
てきた配列と想定解をexpect_iarrayに与えたものが出力されるように
なっている。
うまく動いていればOKという表示が出るはずである。

《考察》
C言語では、ポインタを用いることで配列に対して何か操作を加えるよ
うな関数を作成することができる。C言語では値の入力や出力の処理も
自分で書かなければならないが、この時配列は配列として一塊で扱える
わけではなく、for文を用いて要素をひとつひとつ見ていく必要がある。
これはiarray_reverseで与えられた配列のコピーを作る際にも言えるこ
とである。また与えられたものに何かの処理をする関数を作った時、そ
の想定解と、両者を比較して一致しているかどうかを判断する関数を用
意しておけば、テストを作ることができる。テストがあればバグがない
かどうかを確認したり、複数の実行結果を比較してバグを検討すること
が容易になる。

【課題2】演習2b 与えられた列を逆順に並べ替えて出力するプログラム
《作成したプログラム(テスト含む)》
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}

void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}

int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
void ivec_read(int *a) { iarray_read(a+1, a[0]); }
void ivec_print(int *a) { iarray_print(a+1, a[0]); }
int *ivec_reverse(int *a){
  int *c = ivec_new(a[0]);
  for(int i = 0; i <= a[0]; ++i) { c[i] = a[i]; };
  for(int i = 1; i <= a[0]; ++i) { a[i] = c[a[0]+1-i];}
  return a;
}
int main(void) {
    int a[] = {4,3,1,2,4}, b[] = {4,4,2,1,3}, c[] = {3,4,4,5}, d[] = {3,5,4,4}, e[] = {3,1,1,1}, f[] = {3,1,1,1};
    int *p = ivec_reverse(a);
    int *q = ivec_reverse(c);
    int *r = ivec_reverse(e);
    expect_iarray(p, b, 5, "3124 -> 4213");
    expect_iarray(q, d, 4, "445 -> 544");
    expect_iarray(r, f, 4, "111 -> 111");
    return 0;
}

《実行結果》
OK 3124 -> 4213
  4  4  2  1  3
  4  4  2  1  3
OK 445 -> 544
  3  5  4  4
  3  5  4  4
OK 111 -> 111
  3  1  1  1
  3  1  1  1

《プログラムの説明》
配列を逆順に並べかえるプログラムである。
iarray_read、iarray_print、iarray_equal、expect_iarrayは課題1で
説明したものと同様である。
ivec_newは指定した長さの列を作る関数である。mallocを用いることで
メモリを必要に応じて動的に確保できるようにしている。また列のサイ
ズは自分で保持しておいた方が何かと処理が楽であるため、新しく作っ
た配列の0番目にその配列のサイズ(1番目以降の要素の数の合計である
本来の配列の要素数)を代入している。
ivec_readは列を読み込み、ivec_printは列を出力する関数であり、そ
れぞれ前に作っておいたiarray_readとiarray_printを用いているが、
列の先頭は列のサイズを表しており、本来の配列はその次から始まると
いうことを踏まえた引数にしている。
ivec_reverseは与えられた列を逆順にして返す関数である。まず与えら
れた配列aの先頭を見て同じ大きさの配列cを作り、その後for文でaの各
要素をcの対応する要素に順番に代入していきcをaのコピーにする。そ
の後for文でaの1番目から最後までの要素に、Cの1番目から最後までの
要素を逆順に代入していき、最終的に先頭が要素数、その後ろがもとも
とのaの配列の逆順になっている配列aを返す。
main関数の中にはivec_reverseに与える配列a、c、eとこれらをその関
数に与えた時の想定解b、d、f、そして実際に与えた時の解であるp、q、
rを用意しておき、expect_iarrayを用いて両者を比較し一致していれば
OK、そうでなければNGが表示されるようにしている。

《考察》
結果としてやりたいことは1つ目の課題で取り組んだことと同じである。
違いは、こちらの課題2の方ではmallocを用いていることである。プロ
グラムはこちらの方が長く複雑であるため、一見1つ目の方が簡潔でよ
いように思える。
しかしmallocを使っているということは配列を扱う場合にその長さに対
応して動的にメモリを確保できる、つまり課題1のような書き方では要
素数を引数として与えなければ実現できなかったようなことでも、要素
数をいちいち与えなくても配列に対する処理が可能になっているという
ことである。
今回は配列を逆順にしただけであったが、より複雑な処理を行いたい場
合、また長さがバラバラの配列を一度に扱う場合はこのような処理の仕
方のほうが適しているのではないかと考えられる。
また配列内部のある位置に統一して自分の配列の長さを表す要素を保持
しておくと、要素数の参照が楽になり処理する際になにかと便利である
ということもわかった。

アンケート
Q1.そのポインタが何を指し示しているのかを忘れないようにしながら
プログラムを書く必要があると感じた。

Q2.構造体をうまく使うとよりわかりやすいプログラムが書けたり、で
きることが広がるように思うが、このあたりの勉強が不足しているよう
に感じる。

Q3.ポインタには苦手意識があったが、手を動かしながら考えることで
以前よりも理解が深まったように感じた。またmallocやfreeについて1
年生の時はよくわかっていなかったので、過去に書いたコードを今見返
してみるとなかなか危ないことになっていた。今後はうまく利用してい
きたい。

y1910668 2b:△ Tue May 19 16:10:33 2020


学籍番号:1910668
ペア:個人作業

<1つ目>
2-a

(再掲)
二つの配列の値を交互に並べて表示するプログラムを作る。

(ソース)
#include <stdio.h>
#include <stdlib.h>

void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
void ivec_read(int *a) { iarray_read(a+1, a[0]); }
void ivec_print(int *a) { iarray_print(a+1, a[0]); }
int *ivec_concat1(int *a, int *b) {
  int x,j=1;
  int *c = ivec_new(a[0]+b[0]);
  if(a[0]>=b[0]){
    x=a[0];
  }else{
    x=b[0];
  }
  for(int i = 1; i <= x; ++i) {
    if(i<=a[0]){
      c[j++] = a[i];
    }
    if(i<=b[0]){
      c[j++] = b[i];
    }
  } 
  return c;
}
int main(void) {
  int *a, *b, *c;
  a = ivec_new(3); ivec_read(a);
  b = ivec_new(4); ivec_read(b);
  c = ivec_concat1(a,b ); ivec_print(c);
  free(a); free(b); free(c);
  return 0;
}

[単体テストのソース]
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int *ivec_concat1(int *a, int *b) {
  int x,j=1;
  int *c = ivec_new(a[0]+b[0]);
  if(a[0]>=b[0]){
    x=a[0];
  }else{
    x=b[0];
  }
  for(int i = 1; i <= x; ++i) {
    if(i<=a[0]){
      c[j++] = a[i];
    }
    if(i<=b[0]){
      c[j++] = b[i];
    }
  } 
  return c;
}
int main(void) {
  int a[]={3,1,2,3},b[]={3,4,5,6},c[]={6,1,4,2,5,3,6};
  int *p=ivec_concat1(a,b);
  expect_iarray(p,c,7,"[1,2,3]+[4,5,6]=[1,4,2,5,3,6]");
  return 0;
}

(実行例)
1> 1
2> 2
3> 3
1> 9
2> 8
3> 7
4> 6
  1  9  2  8  3  7  6

[単体テスト]
OK [1,2,3]+[4,5,6]=[1,4,2,5,3,6]
  6  1  4  2  5  3  6
  6  1  4  2  5  3  6

(説明)
二つの要素を合わせるので配列cの要素数をa[0]+b[0]とする。要素の
数がaとbで違う場合、余ったものをそのまま配列cに加えるようにする
ためにどちらの要素が多いかでループを回すための回数xを出した。xを
用いてループをし、cにc[0]からa[i],b[i]と入れていくことでcにaとb
が交互に入った配列ができる。ifを使い、配列が終わった場合はもう入
れないようにした。cの中の番号はaやbとは違う進み方をするので、jを
使った。

<2つ目>
2-b

(再掲)
配列の要素を反対にして表示するプログラムを作る。

(ソース)
#include <stdio.h>
#include <stdlib.h>

void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
void ivec_read(int *a) { iarray_read(a+1, a[0]); }
void ivec_print(int *a) { iarray_print(a+1, a[0]); }
int *ivec_concat2(int *a) {
  int *c = ivec_new(a[0]);
  for(int i = 1; i <= a[0]; ++i) { c[i] = a[a[0]+1-i]; }
  return c;
}
int main(void) {
  int *a, *c;
  a = ivec_new(3); ivec_read(a);
  c = ivec_concat2(a); ivec_print(c);
  free(a);
  free(c);
  return 0;
}

[単体テストのソース]
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
int *ivec_concat2(int *a) {
  int *c = ivec_new(a[0]);
  for(int i = 1; i <= a[0]; ++i) { c[i] = a[a[0]+1-i]; }
  return c;
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
  int a[]={3,1,2,3},c[]={3,3,2,1};
  int *p=ivec_concat2(a);
  expect_iarray(p,c,4,"[1,2,3]->[3,2,1]");
  return 0;
}

(実行例)
1> 1
2> 3
3> 5
  5  3  1

[単体テスト]
OK [1,2,3]->[3,2,1]
  3  3  2  1
  3  3  2  1

(説明)
変更後も要素数は変化しないので、cにivec_new(a[0])をした。cの後ろ
からaの値を順にいれていった。cの要素の入れたい番号iをもちいて、a
の入れたい要素の位置を考えるとa[0]+1-i番目ということが分かるので、
c[i] = a[a[0]+1-i];とした。
単体テストは、{3,1,2,3}と{3,3,2,1}の配列を作る。後者は、前者を正
しく変更した後の形であり、前者を作ったプログラムにかけて、その結
果と後者の値が同じかを比較した。

<アンケート>
Q1.ポインタを使っている変数なのかどうかをしっかり管理しておくことだと考える。
Q2.ポインタやアドレスの概念の考え方がまだあまりうまく理解できて
いないのかなと思う。
Q3.今回の内容はかなり難しく感じた。単体テストは理解はしたが、ま
だうまく使えないと感じた。

y1910670 2b:○ Mon May 25 01:20:31 2020


学籍番号:1910670
名前:山口諒
ペアの学籍番号:個人作業
提出日時:

<一つ目の課題>
演習1b
配列の並び順を逆順にする関数void iarray revese(int *a, int n)を作成する。

・プログラムのソース

#include <stdio.h>
#include <stdbool.h>
void iarray_read(int *a, int n) {
    for(int i = 0; i < n; ++i) {
        printf("%d> ", i+1); scanf("%d", a+i);
    }
}
void iarray_print(int *a, int n) {
    for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
    printf("\n");
}
void iarray_reverse(int *a, int n){
    int x;
    for(int i = 0; i < n/2; ++i){
        x = a[i];
        a[i] = a[n-i-1];
        a[n-i-1] = x;
    }
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void){
    int a[] = {8,5,2,4,1}, b[] = {1,4,2,5,8};
    iarray_reverse(a, 5); expect_iarray(a, b, 5, "85241 -> 14258");
    int c[] = {1,2,3,4}, d[] = {4,3,2,1};
    iarray_reverse(c, 4); expect_iarray(c, d, 4, "1234 -> 4321");
    int e[] = {1}, f[] = {1};
    iarray_reverse(e, 1); expect_iarray(e, f, 1, "1 -> 1");
    return 0;
}

・実行例
OK 85241 -> 14258
  1  4  2  5  8
  1  4  2  5  8
OK 1234 -> 4321
  4  3  2  1
  4  3  2  1
OK 1 -> 1
  1
  1
Program ended with exit code: 0


・説明

iarray_reverseに関しては、配列の両端から順に指定して行き、指定し
た同士入れ替えるという作業を行なっている。なお、その時、入れ替え
るのにはどちらか片方をある文字に入れないといけないので、xを用い
て保留としている。
なお、iarray_equalで結果の配列が、予想の配列と同じかを確かめてい
る。その結果が正しかったどうかをexpect_iarrayで表示している。

・考察
これに関しては次の課題も同じだが、課題1aと同様、単体テストで、定
義する配列は同じで、使う要素の範囲を指定して再びテストを行うとい
うテストケースを作ろうとしたが、できなかった。これは、一度、前の
テストケースでその定義した配列に違う配列が代入されてしまっている
ためであると考えられる。それを踏まえると、課題1aのように使う配列
は一緒にするということができないので、テストの効率が悪いなと思っ
た。

<二つ目の課題>
演習1c
何らかの整列アルゴリズムで配列を昇順に整列する関数void iarray
sort(int *a, int n)を作成する。

・プログラムのソース

#include <stdio.h>
#include <stdbool.h>
void iarray_read(int *a, int n) {
    for(int i = 0; i < n; ++i) {
        printf("%d> ", i+1); scanf("%d", a+i);
    }
}
void iarray_print(int *a, int n) {
    for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
    printf("\n");
}
void iarray_sort(int *a, int n){
    for(int i = 0;i < n;++i){
        for(int t = i + 1; t < n; ++t){
            if(a[i] > a[t]){
                int x;
                x = a[i];
                a[i] = a[t];
                a[t] = x;
            }
        }
    }
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void){
    int a[] = {8,5,2,4,1}, b[] = {1,2,4,5,8};
    iarray_sort(a, 5); expect_iarray(a, b, 5, "85241 -> 12458");
    int c[] = {5,1,9}, d[] = {1,5,9};
    iarray_sort(c, 3); expect_iarray(c, d, 3, "519 -> 159");
    int e[] = {1}, f[] = {1};
    iarray_sort(e, 1); expect_iarray(e, f, 1, "1 -> 1");
    int g[] = {1,1,1,1,1,1}, h[] = {1,1,1,1,1,1};
    iarray_sort(g, 6); expect_iarray(g, h, 6, "111111 -> 111111");
    return 0;
}

・実行例
OK 85241 -> 12458
  1  2  4  5  8
  1  2  4  5  8
OK 519 -> 159
  1  5  9
  1  5  9
OK 1 -> 1
  1
  1
OK 111111 -> 111111
  1  1  1  1  1  1
  1  1  1  1  1  1


・説明
iarray_sortに関しては、隣り合う要素を比べて、番号の小さい方の値
が大きい方の値より小さかったら、その二つの数字を入れ替えるという
作業を行なっている。なお、それ以降に関しては演習1bと同じである。

・考察
このような考えられるテストケースが少ないものは、単体テストを行う
のはちょっと効率が悪いなと思った。単純に実行を行なって、それで確
認するだけのものの方が効率がいいなと思った。

<三つ目の課題>
 2 つの配列 (長さは同じ) を受け取り、2 番目の各要素の値を 1 番目
の配列の各要素に足し込む関数 void iarray add(int *a, int *b,
int n) を作成する。

<プログラムのソース>
#include <stdio.h>
#include <stdbool.h>
void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
void iarray_add(int *a, int *b, int n){
    for(int i = 0; i < n; ++i){
        a[i] += b[i];
    }
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void){
    int a[] = {8,5,2,4,1}, b[] = {1,1,2,2,3}, c[] = {9,6,4,6,4};
    iarray_add(a, b, 5); expect_iarray(a, c, 5, "85241+11223 -> 96464");
    int d[] = {10, 7, 6, 8, 7};
    iarray_add(a, b, 5); expect_iarray(a, d, 5, "96464+11223 -> 107687");
}

・実行例
OK 85241+11223 -> 96464
  9  6  4  6  4
  9  6  4  6  4
OK 96464+11223 -> 107687
 10  7  6  8  7
 10  7  6  8  7
Program ended with exit code: 0

・説明

iarray_addは一つ目の要素から順に指定して行き、aとbの数字を順に足
して行くという作業を行なったものです。なお、それ以降は前の課題と
同様。

・考察

今回は、前の二つの課題と変えて、単体テストで使う配列を同じで、テ
ストケースを入れるということをやってみた。ただし、最低限の都合上、
cは改めて新しい配列を定義した。そうすると、成功したので、前の考
察の予測は正しかったということになる。

<4つ目の課題>

2つの列を受け取り、両方の列の内容を交互に並べた列を返す(例: 「1,
2, 3」「4, 5, 6」→「1, 4, 2, 5, 3, 6」。長さが異なる場合の扱い
は好きに決めてよい)。

・プログラムのソース
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <stdbool.h>
void iarray_read(int *a, int n) {
    for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
void ivec_read(int *a) { iarray_read(a+1, a[0]); }
void ivec_print(int *a) { iarray_print(a+1, a[0]); }
int *ivec_alter(int *a, int *b){
    int *c = ivec_new(a[0]+b[0]);
    if(a[0] < b[0]){
        for(int i = 1;i <= a[0];++i){
            c[2*i-1] = a[i];
            c[2*i] = b[i];
        }
        for(int t = 1;t <= b[0];++t){
            c[2*a[0]+t] = b[a[0]+t];
        }
    }
    if(a[0] > b[0]){
        for(int i = 1;i <= b[0];++i){
            c[2*i-1] = a[i];
            c[2*i] = b[i];
        }
        for(int t = 1;t <= a[0];++t){
            c[2*b[0]+t] = a[b[0]+t];
        }
    }
    return c;
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void){
    int a[] = {3,1,2,3}, b[] = {2,4,5}, c[] = {5,1,4,2,5,3};
    int *p = ivec_alter(a, b);
    expect_iarray(p, c, 6, "[1,2,3] <- [4,5] = [1,4,2,5,3]");
    int d[] = {7,4,1,5,4,2,5,3};
    int *q = ivec_alter(b, c);
    expect_iarray(q, d, 8, "[4,5] <- [1,4,2,5,3]");
    return 0;
}

・説明
ivvex_alterで、与えられた二つの配列を比べて、要素数の大小で場合
わけをした。要素数の大きい方の配列に小さい方の配列を入れて行くと
考えると、小さい方の配列をの要素を入れきると、それ以降、もう入れ
る要素がなくなってしまうので、それによって、場合わけが必要だと考
えた。

・考察
可変長配列の特徴として、配列の要素数を先頭に置いたことで、要素の
番号と、配列の先頭から数えた時の番号が一緒になるので、それがすご
く扱いやすいと思った。それによって、ivec_alterの中での、要素番号
の指定がより効率的になった。

<アンケート>
Q1.*と&の勘違い
Q2.細かいところへの注意
Q3.なし

y1910671 2b:○ Sun May 24 03:04:16 2020


課題B

個人作業
提出日時: 2020/05/24

No.1
演習1-b

[課題の再掲]
配列の並び順を逆にする関数void iarray_reverse(int *a,int n)
を作成する。

[ソースコード]
#include <stdio.h>
#include <stdbool.h>

void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}

// 1b
void iarray_reverse(int *a, int n){
  int tmp,i;
  for(i = 0; i < (n/2); i++){
    tmp = *(a+i); *(a+i) = *(a+n-1-i); *(a+n-1-i) = tmp; 
  }
}

int main(void){
    int a[] = {8,5,2,4,1};
    int b[] = {1,4,2,5,8};
    int c[] = {0,1,2,3,4,5,6,7,8,9};
    int d[] = {9,8,7,6,5,4,3,2,1,0};
    
    iarray_reverse(a,5);
    expect_iarray(a,b,5, "85241 -> 14258");
    expect_iarray(b,a,5, "14258 -> 85241");

    iarray_reverse(c,10);
    expect_iarray(c,d,10,"0123456789 -> 9876543210");
    iarray_reverse(d,10);
    expect_iarray(c,d,10,"9876543210 -> 0123456789");


    return 0;
}

[実行例]
[y1910671@sol #2]$ ./a.out
OK 85241 -> 14258
  1  4  2  5  8
  1  4  2  5  8
OK 14258 -> 85241
  1  4  2  5  8
  1  4  2  5  8
OK 0123456789 -> 9876543210
  9  8  7  6  5  4  3  2  1  0
  9  8  7  6  5  4  3  2  1  0
NG 9876543210 -> 0123456789
  9  8  7  6  5  4  3  2  1  0
  0  1  2  3  4  5  6  7  8  9

[説明]
main関数から、引数として渡した配列の要素の順番を逆にできるような
関数iarray_reverseを作った。iarray_reverseでは配列のポインタを
渡して、関数内ではa[i]というような要素の参照ではなく、*演算子による
参照を行うことで要素を入れ替えた。main関数部では、偶数と奇数の配列の組
を用意して、作成した関数が機能するのかを試した。テストケースの4つ目では、
比較対象の配列の順番を逆にすることでOKだったものがNGになることを確認した。


[考察]
単純な変数を用いていた時と異なり、ポインタを使うとそのアドレスに
含まれる値そのものやアドレス自体を操作することが可能になる。
この特性を用いることで、変数のみを用いていたときに起こった、別関数内の
ローカル変数にアクセスできないときにも変数のアドレスを知ることで
アクセスできるようになってしまうと考えた。

No.2
演習2-a
[課題の再掲]
2つの列を受け取り、両方の列の内容を交互に並べた列を返す関数を作る。
[ソースコード]

#include <stdio.h>
#include <stdlib.h>

void iarray_read(int *a, int n){
  for(int i = 0; i < n; ++i){
    printf("%d > ", i+1); scanf("%d",a+i);
  }
}
void iarray_print(int *a, int n){
  for(int i = 0; i < n; ++i){ printf(" %2d",a[i]);}
  printf("\n");
}
int *ivec_new(int size){
  int *a = (int*)malloc((size+1)*sizeof(int));
  a[0] = size; return a;
}
void ivec_read(int *a){ iarray_read(a+1,a[0]); }
void ivec_print(int *a){ iarray_print(a+1,a[0]);}

int *ivec_two(int *a, int *b, int n){
  int *c = ivec_new(n);
  int i; //a,bのインデックス
  int j = 1; //cのインデックス
  for(i = 1; j < n; i++){{
      c[j] = a[i];
      j++;
      c[j] = b[i];
      j++;
    }
  }
  return c;
}

int ivec_check(int *x, int *y, int len, char *msg){
  for(int i = 1; i <= len; i++){
    if(x[i] != y[i]){
      printf(msg);
      printf(" [NG] \n");
      return -1;
    }
  }
  printf(msg);
  printf(" [OK] \n");
  return 0;
}

int main(void){
  int *a, *b, *c;
  int d[] = {6,1,4,2,5,3,6};
  a = ivec_new(3); ivec_read(a);
  b = ivec_new(3); ivec_read(b);
  c = ivec_two(a,b, 6);
  ivec_print(c);
  ivec_check(c, d,6, "123 + 456 -> 142536");
  free(a); free(b); free(c);
  return 0;
}

[実行例]

[y1910671@sol #2]$ ./a.out
1 > 1
2 > 2
3 > 3
1 > 4
2 > 5
3 > 6
  1  4  2  5  3  6
123 + 456 -> 142536 [OK] 
[y1910671@sol #2]$ ./a.out
1 > 1
2 > 1
3 > 1
1 > 1
2 > 1
3 > 1
  1  1  1  1  1  1
123 + 456 -> 142536 [NG] 

[説明]
2つの配列の要素を順に新たな配列に格納する関数ivec_twoを作った。
ループで用いる変数をi,jの2つに設定して、代入したい配列と代入される方の
配列で変数を分けた。また、テストケースはivec_check関数を新たに作成し、
設定した数字の列と異なる場合はNG、合致した場合にOKと出力した。

[考察]
mallocを用いた配列作成では、その長さを覚えておく必要があることから配列の
0番目の要素に要素数を入力していたが、プログラムを作成している途中でその
ことを忘れていて、ivec_twoで作った配列を出力したときに要素がうまく出力
されないことがあった。これははじめに定めた配列のインデックスを飛び出して
代入を行ってしまっているからだと考えた。

~アンケート~
Q1.どこをさしているかわからないアドレスの値は使わないようにすること。
Q2.配列の添字を意識すること。
Q3.だんだん難しくなってきた。

y1910673 2b:○ Tue May 19 19:55:22 2020


プログラム通論第2回B課題
学籍番号:1910673
個人作業
提出日時:5月19日

[取り組んだ課題‐1つ目]
演習1.c 何らかの整列アルゴリズムで配列を昇順に整列する関数 void
iarray sort(int *a, int n) を作成する。

[作成したプログラム]
#include <stdio.h>
#include <stdbool.h>
void iarray_sort(int *a, int n) {
    int i, j, t;
    printf("---ソート前---\n");
    for (i = 0; i < n; i++) {
        printf("%d", *(a + i));
    }
    printf("\n");
    for (i = 0; i < n; i++) {
        for (j = i + 1; j < n; j++) {
            if (a[i] > a[j]) {
                t = a[i];
                a[i] = a[j];
                a[j] = t;
            }
        }
    }
    printf("---ソート後---\n");
    for (i = 0; i < n; i++) {
        printf("%d", *(a + i));
    }
    printf("\n");
}
void iarray_print(int* a, int n) {
    int i;
    for (i = 0; i < n; ++i) { printf(" %2d", a[i]); }
    printf("\n");
}
bool iarray_equal(int* a, int* b, int n) {
    int i;
    for (i = 0; i <= n; ++i) {
        if (a[i] != b[i]) { return false; }
    }
    return true;
}
void expect_iarray(int* a, int* b, int n, char* msg) {
    printf("%s %s\n", iarray_equal(a, b, n) ? "OK" : "NG", msg);
    iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
    int a[] = { 8,5,2,4,1 }, b[] = { 1,2,4,5,8 };
    iarray_sort(a, 5); expect_iarray(a, b, 5, "85241 -> 12458");
    int c[] = { 10,2,66,-3,5 }, d[] = { -3,2,5,10,66 };
    iarray_sort(c, 5); expect_iarray(c, d, 5, "10,2,66,-3,5 -> -3,2,5,10,66");
    return 0;
}

[実行例]
---ソート前---
85241
---ソート後---
12458
OK 8,5,2,4,1 -> 1,2,4,5,8
  1  2  4  5  8
  1  2  4  5  8
---ソート前---
10266-35
---ソート後---
-3251066
OK 10,2,66,-3,5 -> -3,2,5,10,66
 -3  2  5 10 66
 -3  2  5 10 66

[プログラムの説明]
int iarray_sortを作成した。配列を昇順にするプログラムの前後に実
行前と実行後の配列を分かりやすく見るためにそれぞれfor文で打ち出
した。配列を昇順にするプログラム自体はfor文の中にfor文を書き、配
列の各要素がほかのすべての要素と比べられるようにした。そして比較
する際は整数型の変数tを使って小さい数値が配列の前の方にいくよう
にした。テストケースは正常に起動した。

[考察]
比較したときの値を入れ替えるやり方は1年生の頃にもあった単純なや
り方だが、ここで一番注意しなければいけないのがポインタ型の値を変
えるということは参照元の値を変えることであると感じた。
このプログラム書いていた時、はじめ昇順にする部分を
for(a[i] > a[n-1-i]) {
    t = a[i];
    a[i] = a[n-1-i];
    a[n-1-i] = t;
}
としてしまっていたため予想と違った配列が出力されてしまったから気
を付けるべきだと感じた。

[取り組んだ課題‐2つ目]
演習2 a. 2つの列を受け取り、両方の列の内容を交互に並べた列を返す。 

[作成したプログラム]
#include <stdio.h> 
#include <stdlib.h> 
#include <stdbool.h>

void iarray_read(int* a, int n) {
    for (int i = 0; i < n; ++i) {
        printf("%d> ", i + 1); scanf("%d", a + i);
    }
}
void iarray_print(int* a, int n) {
    for (int i = 0; i < n; ++i) {
        printf(" %2d", a[i]);
    } printf("\n");
}
int* ivec_new(int size) {
    int* a = (int*)malloc((size + 1) * sizeof(int));
    a[0] = size;
    return a;
}
void ivec_read(int* a) {
    iarray_read(a + 1, a[0]);
}
void ivec_print(int* a) {
    iarray_print(a + 1, a[0]);
}
bool iarray_equal(int* a, int* b, int n) {
    for (int i = 0; i < n; ++i) { 
        if (a[i] != b[i]) { return false; } 
    }
    return true; 
}
void expect_iarray(int* a, int* b, int n, char* msg) {
    printf("%s %s\n", iarray_equal(a, b, n) ? "OK" : "NG", msg);
    iarray_print(a, n); iarray_print(b, n); 
}

int* ivec_mix(int* a, int* b) {
    int j = 1;
    int* c = ivec_new(a[0] + b[0]);
    for (int i = 1; i <= a[0]; i++) {
        c[j] = a[i];
        j = j + 2;
    }
    j = 1;
    for (int i = 1; i <= b[0]; ++i) {
        c[j + 1] = b[i];
        j = j + 2;
    }
    if( a[0] >= b[0] * 2 + 1 ){
        for (int i = 0; i <= a[0] - b[0] - 1; ++i) {
            c[b[0] * 2 + 2 + i] = a[b[0] * 2 + i];
        }
    }
    else if(b[0] >= a[0] * 2 + 1) {
        for (int i = 0; i <= b[0] - a[0] - 1; ++i) {
            c[a[0] * 2 + 2 + i] = b[a[0] * 2 + i];
        }
    }
    return c;
}
int main(void) {
    int* x, * y, * z;
    x = ivec_new(5); ivec_read(x);
    y = ivec_new(2); ivec_read(y);
    z = ivec_mix(x, y); ivec_print(z);
    free(x); free(y); free(z);
    printf("・単体テスト\n");
    printf("ーーーテスト1ーーー\n");
    int a[] = { 3,1,2,3 }, b[] = { 3,4,5,6 }, c[] = { 6,1,4,2,5,3,6 }; 
    int* p = ivec_mix(a, b);
    expect_iarray(p, c, 7, "「1,2,3」「4,5,6」→「1,4,2,5,3,6」");
    printf("ーーーテスト2ーーー\n");
    int d[] = { 5,1,2,3,4,5 }, e[] = { 2,6,7 }, f[] = { 7,1,6,2,7,3,4,5 };
    int* q = ivec_mix(d, e);
    expect_iarray(q, f, 8, "「1,2,3,4」「5,6,7」→「1,5,2,6,3,7,4」");
    return 0;
}

[実行例]
1> 1
2> 2
3> 3
4> 4
5> 5
1> 6
2> 7
  1  6  2  7  3  4  5
・単体テスト
ーーーテスト1ーーー
OK 「1,2,3」「4,5,6」→「1,4,2,5,3,6」
  6  1  4  2  5  3  6
  6  1  4  2  5  3  6
ーーーテスト2ーーー
OK 「1,2,3,4」「5,6,7」→「1,5,2,6,3,7,4」
  7  1  6  2  7  3  4  5
  7  1  6  2  7  3  4  5

[プログラムの説明]
int* ivec_mixを作成した。入力された2つの配列のポインタのそれぞ
れの要素数の要素をもつ配列のポインタ*cをivec_newで作成し、2つ
のfor文で1つ目の配列は配列*cの奇数番目に代入していき、2つ目の
配列は配列の*cの1以上の偶数番目に代入していくことで互い違いに代
入した。しかし、このやり方では例えば要素数5の配列と要素数2の配
列で実行した場合、空欄のアドレスができてしまうので、次の2つのif
文でその場合に空欄のアドレスを左につめるようにした。テストケース
は正常に動いた。

[考察]
今回の1つ目の配列を奇数番目に2つ目の配列を偶数番目に代入してい
くことで2つの要素数が違っても互い違いにしている。空欄のアドレス
左につめるプログラムでは番地の指定をきちんと書けたと思うが汚く仕
上がったのがマイナスな点だと思うのでこのようなプログラムを少ない
文字数できれいに書く書き方を見つけるべきである。

[アンケート]
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。 
ポインタ型の値が変わるということは参照しているが変わるかいうこと。
Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に
付いていないと思うことは何ですか。
プログラムが実行されるときの構造がぼんやりとしか理解できていないこと。
Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
特にありません。

y1910677 2b:○ Mon May 25 18:58:18 2020


情報領域演習第二:#02b
提出日付 2020/5/25

【課題の再掲(演習1d)】
同じ長さの二つの配列を受け取り、二番目の各要素の値を一番目の配列
の各要素に足しこむ関数を作成する。

【実施した内容】
[プログラム]
//02-1d
#include <stdio.h>
#include <stdbool.h>
void iarray_add(int *a, int *b){
  for(int i=1;i< a[0]+1 ;++i){a[i]+=b[i];}
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void iarray_print(int *a, int n) {
  for(int i = 1; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
  //test1
  int a1[] = {3,1,2,3}, b1[] = {3,4,5,6}, c1[] = {3,5,7,9};
  iarray_add(a1, b1);
  expect_iarray(a1, c1, 4, "[1,2,3]+[4,5,6]=[5,7,9]");
  //test2
  int a2[] = {0}, b2[] = {0}, c2[] = {0};
  iarray_add(a2, b2);
  expect_iarray(a2, c2, 1, "[]+[]=[]");
  //test3
  int a3[] = {3,-1,-2,-3}, b3[] = {3,1,2,3}, c3[] = {3,0,0,0};
  iarray_add(a3, b3);
  expect_iarray(a3, c3, 4, "[-1,-2,-3]+[1,2,3]=[0,0,0]");
  //test4
  int a4[] = {4,-1,-2,-3,-4}, b4[] = {4,-1,-2,-3,-4}, c4[] = {4,-2,-4,-6,-8};
  iarray_add(a4, b4);
  expect_iarray(a4, c4, 5, "[-1,-2,-3,-4]+[-1,-2,-3,-4]=[-2,-4,-6,-8]");
  return 0;
}

[実行結果]
OK [1,2,3]+[4,5,6]=[5,7,9]
  5  7  9
  5  7  9
OK []+[]=[]


OK [-1,-2,-3]+[1,2,3]=[0,0,0]
  0  0  0
  0  0  0
OK [-1,-2,-3,-4]+[-1,-2,-3,-4]=[-2,-4,-6,-8]
 -2 -4 -6 -8
 -2 -4 -6 -8

[説明]
今回のプログラムではexample codeにiarray_addという関数と,メイン
関数の中身のみを変更したため,他の部分の説明に関しては省略する.
iarray_addという関数では,for文を使用して配列の1番目から順に配列
aとbの中の数値を足し合わせている.メイン関数ではa,b,cという配列
のセットを4つ用意した.a,bを足し合わせた正しい配列をcとしている.
今回は,すべて正の数,要素数が0の場合,負の数と正の数,負の数同
士の足し合わせに関してテストケースを用意し単体テストを行った.

【考察】
この課題ではポインタの使い方について確認することができた.はじめ,
for文の条件のところをa[0]としてしまっていたのが,今回は0にはその
後の要素数が入っていることに気が付き1を足した.また,0番目同士を
足し合わせてNGになってしまうこともあった.そのため,0番目に配列
の長さが入っている場合は注意しなくてはならないと感じた.
今回使用したiarray_addという関数の中身は一行で済んでしまう内容で
あったが,関数にすることで,メイン関数内で何をしているのかがわか
り,途中でエラーが出たときにどこの関数がいけないのかが分かりやす
くなったと思う.

【課題の再掲(演習2a)】
二つの列を受けとり,両方の列の内容を交互に並べた列を返す.
【実施した内容】
[プログラム]
// 02-2a
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}

int *ivec_alter(int *a, int *b){//aの方が短い
  int *mix = ivec_new(a[0]*2);
  for(int i=1; i < a[0]*2+1; i++){
    if(i%2!=0){
      mix[i]=a[i/2+1];
    }else{
      mix[i]=b[i/2];
    }
  }
  return mix;
}

bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}

void iarray_print(int *a, int n) {
  for(int i = 1; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}

int main(void) {
  //test1
  int a1[] = {3,1,2,3}, b1[] = {3,4,5,6}, c1[] = {6,1,4,2,5,3,6};
  int *p1 = ivec_alter(a1, b1);
  expect_iarray(p1, c1, 7, "[1,2,3]+[4,5,6]=[1,4,2,5,3,6]");
  //test2
  int a2[] = {1,1}, b2[] = {3,4,5,6}, c2[] = {2,1,4};
  int *p2 = ivec_alter(a2, b2);
  expect_iarray(p2, c2, 3, "[1]+[4,5,6]=[1,4]");
  //test3
  int a3[] = {4,-1,-2,-3,-4}, b3[] = {4,1,2,3,4}, c3[] = {8,-1,1,-2,2,-3,3,-4,4};
  int *p3 = ivec_alter(a3, b3);
  expect_iarray(p3, c3, 9, "[-1,-2,-3,-4]+[1,2,3,4]=[-1,1,-2,2,-3,3,-4,4]");
  //test4
  int a4[] = {0}, b4[] = {0}, c4[] = {0};
  int *p4 = ivec_alter(a4, b4);
  expect_iarray(p4, c4, 1, "[]+[]=[]");
  return 0;
}


[実行結果]
OK [1,2,3]+[4,5,6]=[1,4,2,5,3,6]
  1  4  2  5  3  6
  1  4  2  5  3  6
OK [1]+[4,5,6]=[1,4]
  1  4
  1  4
OK [-1,-2,-3,-4]+[1,2,3,4]=[-1,1,-2,2,-3,3,-4,4]
 -1  1 -2  2 -3  3 -4  4
 -1  1 -2  2 -3  3 -4  4
OK []+[]=[]

[説明]
今回のプログラムではexample codeにivec_alterを加えメイン関数に変
更を行ったため,それ以外の関数についての説明は省略する.
ivec_alterでは,もし配列の長さが異なる場合短い方をaに,長い方をb
に渡す前提の関数になっている.(配列の長さが違う場合,短い方に合
わせ長い方の余りは無視するとした.)まず,mixというaの長さの2倍
の長さの配列を用意する.for文を使用して,mixの偶数番にはaの数値
を前から順に,奇数番にはbの数値を前から順に代入しmixを返している.
メイン関数ではa,b,cという配列と,pという整数へのポインタを,4セッ
ト用意した.pにはivec_alterにa,bを渡した結果を代入し,pとcを比較
して単体テストを行った.

【考察】
今回の課題を行って,関数へのポインタというものを学び自分で作って
みることができた.読んだだけではあまり理解しきていなかったが,実
際に手を動かしながら確かめることで納得ができた.今まで配列を返す
方法が分からず,メイン関数の中で配列をいじっていたが,今回学んだ
ことで関数に分けてすっきりとさせられると感じた.

【アンケート】
Q1.アドレス,ポインタを使うプログラムで注意すべきことは何だと思いますか.
自分がどのアドレスにどんな値をいれているのかわかりやすくしないといけないと感じた.
Q2.ここまでのところで,プログラムを作るときに重要だが自分で身に付いて
いないと思うことは何ですか.
やりたいことをどうプログラムにするかを考える力.
Q3.リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ.
ポインタなどについては混乱していたが,前より少し理解できたと思う.

y1910679 2b:○ Mon May 25 21:59:32 2020


学籍番号;1910679(個人作業)
提出日時;2020/5/25

1つ目の課題(演習1.b)
 課題の再掲
配列の並びを逆順にする関数void iarray_reverse(int *a, int n)を作成する。
 プログラミングのソースコード
#include <stdio.h>
#include <stdbool.h>
void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void iarray_reverse(int *a, int n){
 int x;
 for(int i = 0; i < n/2; ++i){
    x = a[i];   
    a[i] = a[n-i-1];
    a[n-i-1]= x;
  }
}

void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
int a[]={8,5,2,4,1}, b[]={1,4,2,5,8} , c[]={7,2,1,6,9}, d[]={9,6,1,2,7}; 
iarray_reverse(a,5); expect_iarray(a,b,5,"85241->14258");
iarray_reverse(c,5); expect_iarray(c,d,5,"72169->96127");
  return 0;
}
実行例
OK 85241->14258
  1  4  2  5  8
  1  4  2  5  8
OK 72169->96127
  9  6  1  2  7
  9  6  1  2  7

プログラミングの説明
配列の並びを逆順にする関数iarray_reverseを作った。この関数ではポ
インタを使うことで配列の要素を入れ替えた。関数iarray_equalは2つ
の配列が等しいかを確認した。関数expect_iarrayでは、プログラムが
正しく動作するものなのか関数iarray_equalをつかって確かめた。main
では引数を渡して実際に作った関数を動かし、2つのテストケースを実
行させた。

考察
iarray_reverseを作る際に a[i] = a[n-i-1];を入れていなかったため
配列の前半部分と後半部分が同じ(8,5,2,5,8)になってしまった。原因
としては、逆順にする手順を2度やってしまったことである。プログラ
ミングを動かして思うような結果が出なかったときに、共通してテスト
ケースにでるエラーから原因を考える重要性が分かった。

1つ目の課題(演習2.b)
 課題の再掲
1つの列を受け取り、その内容を逆順にした列を返す関数を作る。
 プログラミングのソースコード
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
void ivec_read(int *a) { iarray_read(a+1, a[0]); }
void ivec_print(int *a) { iarray_print(a+1, a[0]); }
int *ivec_reverse(int *a) {
  int *b = ivec_new(a[0]);
  for(int i = 1; i <= a[0]; ++i) { 
  b[i] = a[a[0] + 1 - i];  
  }
  return b;
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i <= n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  ivec_print(a); ivec_print(b);
}

int main(void) {
  int a[] = {8,1,2,3,4,5,6,7,8}, b[] = {8,8,7,6,5,4,3,2,1}; 
  int c[] = {4,1,0,2,4}, d[] = {4,4,2,0,1};
  int *p = ivec_reverse(a);
  int *q = ivec_reverse(c);
  expect_iarray(p, b, 9, "[1,2,3,4,5,6,7,8] to [8,7,6,5,4,3,2,1]");
  expect_iarray(q, d, 5, "[1,0,2,4] to [4,2,0,1]");
  return 0;
}

実行例
OK [1,2,3,4,5,6,7,8] to [8,7,6,5,4,3,2,1]
  8  7  6  5  4  3  2  1
  8  7  6  5  4  3  2  1
OK [1,0,2,4] to [4,2,0,1]
  4  2  0  1
  4  2  0  1
プログラムの説明
与えられた列を逆順に返す関数関数*ivec_reverseを作った。1つ目の
課題と同様なテストケース(関数iarray_equal、関数expect_iarrayを
用いて)を作った。mainでは可変長配列を受け取り、テストケースを実
行させた。

考察
演習1.bと似たプログラムだと思った。この2つの違いを考察する。
演習2では教科書に書いてある通りサイズを与え、計算していることが
1番の違いであると思う。コンピューターの出力では演習1、2は変わ
らないが、データの無駄づかいをしないことは大切であることが演習2
からわかった。

アンケート
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いま すか。 
どの部分を示しているのか注意することが大切だと思った。
Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に
付いて いないと思うことは何ですか。
for文などの条件など細かい部分だと思う。
 Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
わからない部分を明確にして、より理解を深めたいとおもった。

y1910681 2b:○ Sun May 24 23:20:06 2020



1 つ目の課題の再掲(演習2c)
引数として受け取った整数型の配列の順番を昇順にした配列を返す。

ソース

#include<stdio.h>
#include<stdbool.h>

void uptoarray(int *a, int n){
    int i, j, sub;
    for(i = 0; i < n - 1; i++){
            for(j = i; j < n; j++){
                    if(a[i] < a[j]){
                            sub = a[i];
                            a[i] = a[j];
                            a[j] = sub;
                    }
            }
    }
}

bool equal(int *a, int *b, int n){
        for(int i = 0; i < n; i++)
        {
                if(a[i] != b[i]){
                        return false;
                }
        }
        return true;
}

void expect_uptoarray(int *a, int *b, int n, char *msg){
        uptoarray(a, n);
        printf("%s\t%s\n", equal(a, b, n)?"OK":"NG", msg);
}


int main(){
    int a1[] = {2, 1, 3}, a2[] = {3, 2, 1}, b1[] = {0}, b2[] = {0}, c1[] = {4, 3, 5, 2, 1}, c2[] = {5, 4, 3, 2, 1}, d1[] = {-4, 0, 2}, d2[] = {-4, 2, 0};
    expect_uptoarray(a1, a2, 3, "213 -> 321");
    expect_uptoarray(b1, b2, 1, "0 -> 0");
    expect_uptoarray(c1, c2, 3, "43521 -> 54321");
    expect_uptoarray(d1, d2, 3, "-402 -> 20-4"); //これはequal()関数が正しいか確認のため異なった配列を代入した.課題2aを提出したときにこの点について書いてあったのでいちよう
    return 0;
}

実行例
OK      213 -> 321
OK      0 -> 0
OK      43521 -> 54321
NG -402 -> 20-4 <= これはequal()関数が正しいか確認のため異なっ
た配列を代入した.課題2aを提出したときにこの点について書いてあっ
たのでいちよう

説明

void uptoarray(int *a, int n)
外側forでaの配列のどの要素を比較しているか示す.内側のforでjを0か
らnまで値を変えながら外側のforで示しているa[i]とa[j]を比較する。
a[i] < a[j]があるならsubにa[i]ポインタが示す整数を代入.そして
a[i]ポインタの示すアドレスをa[j]ポインタが示しているアドレスに変
更する.a[j]ポインタの示すアドレスをsubのアドレスに変更する.これ
を配列の最後まで繰り返し、結果的に一番値の大きい整数が配列のi番
目に来る。ここで内側のforが終了.これらを外側のforの関数が配列の0
からn-2まで繰り返す。一つ手前まででいいのは内側のforが最後の配列
まで比較してくれるから。

bool equal(int *a, int *b, int n)
forでiを0からn-1までa[i]=b[i]ならtrueそうでないならfalseを出力する.

void expect_uptoarray(int *a, int *b, int n, char *msg)
uptoarray(a, n)を実行する.これによりaの配列は昇順になるはず、こ
こでprintf関数で(equal(a, b, n)がtrueなら"OK"、falseなら
"NG")(msg)を出力する.つまり昇順された配列aと配列bをn番目まで正し
いなら"OK"そうでないなら"NG"が出力されるということ.またmsgは"(配
列a)->(昇順にされた場合の配列a)"である

int main()
int a1[] = {2, 1, 3}, a2[] = {3, 2, 1}, b1[] = {0}, b2[] = {0},
c1[] = {4, 3, 5, 2, 1}, c2[] = {5, 4, 3, 2, 1}, d1[] = {-4, 0,
2}, d2[] = {-4, 2, 0};
配列の変数の最初のアルファベットは組を示すため2文字目は
expect_uptoarray(int *a, int *b, int n, char *msg)のうち1のほう
をaのほうに2のほうをbの位置に引数とするためである.また,d2がd1の
昇順になっていないのはequal()関数があっているかの確認のためであ
る.また,3つめの引数は何番目まで昇順するかを指定するものである。
4つ目の引数は"(昇順される前の配列a) -> (昇順になった場合の配列
a)"


考察
例えば

void plactice(int *a){ printf("%d,%d\n", sizeof a, sizeof(int));}
void main(){
    int a[] = {1,2,3};
    plactice(a);
}
としコンパイルして実行すると4,4と出た。関数外で行うと12,4と出る
はずどうしてこのような現象が起きるのか考えてみた。なにがおかしい
かは一目瞭然sizeof(a)が思ったように12にならないのだ、おそらく配
列を作るときに要素数がどこかに記憶されていたが関数にはアドレスの
みしか引数として渡せないのでa[0]のみしか数えられなかったと考えた。

2つめの課題の再掲(演習3a)
テキストに載っている例題を使って等差数列の初項にもどす関数を作る

ソース

--a3.c--

#include <stdio.h>
#include "eps.h"


int main(void) {
expect_cdseq(1, 2);
expect_cdseq(0, 0);   
expect_cdseq(-5, 4);
expect_cdseq(9, -2);
return 0;
}

--eps.c--

#include<stdio.h>
#include <stdlib.h>
#include "eps.h"

struct cdseq { int value, ori, diff;};
struct cdseq *cdseq_new(int s, int d) {
struct cdseq *r = (struct cdseq*)malloc(sizeof(struct cdseq));
r->value = s; r->ori = s; r->diff = d; return r;
}
int cdseq_get(struct cdseq *r) {
int v = r->value; r->value += r->diff; return v;
}
void cdseq_free(struct cdseq *r) {
free(r);
}
int cdseq_reset(struct cdseq *r){
    r->value = r->ori;
    int i = r->value;
    return i;
}

void expect_cdseq(int n, int d){
    int i ;
    struct cdseq *a = cdseq_new(n, d);
    for(i = 0; i < 3 ; i++){
        printf("%2d ",cdseq_get(a));
    }
    printf(" -- %s ",(n == cdseq_reset(a))?"OK":"NG"); printf(" --> ");
    for(i = 0; i < 3 ; i++){
        printf("%2d ",cdseq_get(a));
    }
    printf("\n");
    cdseq_free(a);
}

--eps.h--

struct cdseq *cdseq_new(int s, int d);
int cdseq_get(struct cdseq *r);
void cdseq_free(struct cdseq *r);
int cdseq_reset(struct cdseq *r);
void expect_cdseq(int n, int d);

出力結果
 1  3  5  -- OK  -->  1  3  5 
 0  0  0  -- OK  -->  0  0  0 
-5 -1  3  -- OK  --> -5 -1  3 
 9  7  5  -- OK  -->  9  7  5 

説明

--eps.c--

struct cdseq *cdseq_new(int s, int d)
例題とほぼ同じだがstructのメンバにoriを追加した。初項を記憶するためのメンバ

int cdseq_reset(struct cdseq *r)
整数i、r->valueに初項のr->oriを代入する.iを返す

void expect_cdseq(int n, int d)
cdseq_new(n, d)でstruct cdseq構造体のアドレスをaに代入した.
for関数でcdseq_get(a)をよんで返ってきた値をprintfで出力しこれを3
回繰り返した。等差数列になっているかの確認.その次にprintfで" --
(cdseq_reset(a)を実行しかえってきた値とnが等しければOKそうでなけ
ればNG) --> "をよんで帰ってきた値をprintfで出力した。初項になっ
ているか確認。
for関数でcdseq_get(a)をよんで返ってきた値をprintfで出力しこれを3
回繰り返した。初項にした後、等差数列になっているか確認。

--eps.h--
ヘッダファイル

--a3.c--

初項,公差を(1, 2),(0, 0),(-5, 4),(9, -2)としexpect_cdseqの実引数
としてexpect_cdseqと実行した.


考察
作成するときにoriを初項の値の記憶のために作ったが公差を足した回
数を記憶したほうが応用に聞いたかもなって思った。expect_cdseq関数
内の出力方法についてOKやNGを一番最初に出力したかったが
cdseq_resetを複数において使用しなくてはならず効率をとった、テス
トの出力は一目でわかるように一番最初にOKまたはNGを出力したほうが
よいのだろうか。この点を疑問に思った。

アンケート
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。
A1. 代入と違い関数内で変更しても変わってしまうところ
Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に
付いていないと思うことは何ですか。
A2. 基礎的な理解やどういう仕組みかを理解できていない。また、プロ
グラム構成がうまくいかない特に再帰
Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
A3. テストを考えるのって難しくてテストを作ってもそのテストも必要
になってきて無数に続いてしまう。どこで妥協するかが大事とおもった。

y1910684 2b:○ Sun May 24 23:27:07 2020


レポート2B
提出日時 2020/05/24

[課題1]

<演習1e>
配列に加えて整数2つを受け取り1つを返す関数へのポインタを受け取り、
それを用いて配列の値を集約した結果を返す関数
int iarray inject(int*a, int n, int (*fp)(int, int))を作成する。
また、それを用いて「配列の合計値」「配列の最大値」を求める。

<ソースコード>
#include <stdio.h>
#include <stdbool.h>
int iadd(int x, int y) { return x + y; }
int imax(int x, int y) { return (x > y) ? x : y; }
int iarray_inject(int *a, int n, int (*fp)(int, int)){
if(fp == iadd){
    int sum=0;
    for(int i=0;i<n;++i){sum=(*fp)(sum,a[i]);}
    return sum;
}
if(fp == imax){
    int max=a[0];
    for(int i=1;i<n;++i){max = (*fp)(max,a[i]);}
    return max;
}
}

void expect_int(int i1, int i2, char *msg) {
  printf("%s %d:%d %s\n", (i1==i2)?"OK":"NG", i1, i2, msg);
}
int main(void) {
 int a[] = {8,5,2,4,1};int b[] = {5,8,2,4,1};
 int c[] = {2,5,8,4,1};int d[] = {4,5,2,8,1};
 int e[] = {1,5,2,4,8};int f[] = {0,0,0,0,-1};
 int g[] = {-5,-4,-3,-2,-1};
  expect_int(iarray_inject(a, 5, iadd), 20, "8+5+2+4+1");
  expect_int(iarray_inject(a, 5, imax), 8, "max(8,5,2,4,1)");
  expect_int(iarray_inject(b, 5, iadd), 20, "5+8+2+4+1");
  expect_int(iarray_inject(b, 5, imax), 8, "max(5,8,2,4,1)");
  expect_int(iarray_inject(c, 5, iadd), 20, "2+5+8+4+1");
  expect_int(iarray_inject(c, 5, imax), 8, "max(2,5,8,4,1)");
  expect_int(iarray_inject(d, 5, iadd), 20, "4+5+2+8+1");
  expect_int(iarray_inject(d, 5, imax), 8, "max(4,5,2,8,1)");
  expect_int(iarray_inject(e, 5, iadd), 20, "1+5+2+4+8");
  expect_int(iarray_inject(e, 5, imax), 8, "max(1,5,2,4,8)");
  expect_int(iarray_inject(f, 5, iadd), -1, "0+0+0+0+(-1)");
  expect_int(iarray_inject(f, 5, imax), 0, "max(0,0,0,0,-1)");
  expect_int(iarray_inject(g, 5, iadd), -15, "-5+(-4)+(-3)+(-2)+(-1)");
  expect_int(iarray_inject(g, 5, imax), -1, "max(-5,-4,-3,-2,-1)");    
  return 0;
}

<説明>
関数iarray_inject内ではまずfpがiaddかimaxかによってif文で操作を
分けた。iaddの時はsumを使い、iadd関数を使って
要素数の分だけ配列の数値をsumに足していき、sumを出力した。imaxの
時はmaxを配列の先頭として置き、配列の最後まで
imax関数で数を比べて配列の最大値maxを返した。main関数では5個の配
列で最大値の位置を5通り試し、負の数や0の数も
含んだときの場合もテストした。

<単体テストと実行例>
[y1910684@sol L02]$ ./a.out
OK 20:20 8+5+2+4+1
OK 8:8 max(8,5,2,4,1)
OK 20:20 5+8+2+4+1
OK 8:8 max(5,8,2,4,1)
OK 20:20 2+5+8+4+1
OK 8:8 max(2,5,8,4,1)
OK 20:20 4+5+2+8+1
OK 8:8 max(4,5,2,8,1)
OK 20:20 1+5+2+4+8
OK 8:8 max(1,5,2,4,8)
OK -1:-1 0+0+0+0+(-1)
OK 0:0 max(0,0,0,0,-1)
OK -15:-15 -5+(-4)+(-3)+(-2)+(-1)
OK -1:-1 max(-5,-4,-3,-2,-1)

<考察>
単体テストを見ると正しく動作してることが分かる。はじめは、関数
iarray_inject内のiaddとimaxで操作を分けないでひとつの操作に
まとめようとしたが、sumとmaxの初期数が異なるために同じ操作で行う
と混乱を招く恐れがあると判断したために操作を分割して作成した。
また、一つにまとめるとiaddのみ知りたいときに同時にimaxも求めてい
ることとなり、無駄な動作をしてしまうこととなるので必要な操作
のみ行うために2つに操作を分けた方が良いのではないかと考えた。こ
のプログラムに例えば関数の最小値を求めるプログラムを追加しようと
したときにsub関数を追加してiarray_inject内のif文にif (fp ==
sub){}などと付け加えるとsubの時の動作を決められるので関数の追加
や削除が簡潔にできると考えた。

[課題2]

<演習2a>
2つの列を受け取り、両方の列の内容を交互に並べた列を返す。長さが
異なる場合ははみ出した分をそのまま配列に加える。

<ソースコード>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
int *ivec_concat(int *a, int *b) {
  int *c = ivec_new(a[0]+b[0]),j=1;
  int min = (a[0]>b[0])?b[0]:a[0];
  for(int i = 1; i <= a[0];++i) { if(i<=min){c[i*2-1] = a[i];}
  else{c[min*2+j] = a[i];++j;} }
  for(int i = 1; i <= b[0]; ++i) { if(i<=min){c[i*2] = b[i];}
  else{c[min*2+j] = b[i];++j;} }
  return c;
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
  int a[] = {3,1,2,3}, b[] = {4,6,4,5,6}, c[] = {7,1,6,2,4,3,5,6};
  int *p = ivec_concat(a, b);
  expect_iarray(p, c, 8, "後者の配列の方が長いとき");
  int d[] = {5,1,2,3,4,5}, e[] = {3,6,7,8}, f[] = {8,1,6,2,7,3,8,4,5};
  int *q = ivec_concat(d, e);
  expect_iarray(q, f, 9, "前者の配列の方が長いとき");
  int g[] = {5,1,2,3,4,5}, h[] = {5,6,7,8,9,10}, i[] = {10,1,6,2,7,3,8,4,9,5,10};
  int *r = ivec_concat(g, h);
  expect_iarray(r, i, 11, "配列の長さが同じとき");
  return 0;
}

<説明>
関数ivec_concat内では、まず新たに配列cをaとbの配列の長さの和分
用意してaとbの配列の長さを比較して少ない方をminに代入した。
次にfor文でまずaの配列をcに入れていくとき、aは先行なのでcの
1,3,5,7…番目、つまり奇数個目に入れていった。この時もしminの数
よりaの配列の個数が多かった時その後は(min*2+1)番目から順に入れて
いった。次にbの配列を入れていくときに今度は後攻のためcの
偶数個目に入れていき、aと同じくもしminの数よりbの配列の個数が多
かった時その後は(min*2+1)番目から順に入れていった。そして最後に
cを返した。
 main関数では、配列の長さがaの方が長いときとbのほうが長いときと
 どちらも等しいときで単体テストを作成した。
 
<単体テストと実行例>
[y1910684@sol L02]$ ./a.out
OK 後者の配列の方が長いとき
  7  1  6  2  4  3  5  6
  7  1  6  2  4  3  5  6
OK 前者の配列の方が長いとき
  8  1  6  2  7  3  8  4  5
  8  1  6  2  7  3  8  4  5
OK 配列の長さが同じとき
 10  1  6  2  7  3  8  4  9  5 10
 10  1  6  2  7  3  8  4  9  5 10

<考察>
単体テストで3パターン行ったがどれも正常に動いている事が分かる。a
とbの配列を交互にcに入れていくところまではすぐにできたが、配列の
長さが異なる際にはみ出す部分をどのようにcに入れていくのかが苦戦
した。はじめj=1をelse{c[min*2+j] = a[i];++j;}の中に入れてしまっ
て、else{j=1;c[min*2+j] = a[i];++j;}としてしまった。1回目は大丈
夫だが、最後に++jをしても次の繰り返しでまたj=1としてしまうため2
回目以降がcに入れられなくてエラーを出してしまった。このことから、
変数の定義をするときはwhileやforの中に入れてしまうと繰り返したと
きに定義をし続けてしまうので、最初に定義するか、それを考慮したう
えでプログラムを作成する必要があると考えた。このようなエラーは単
体テストを行うことですぐに発見できたため単体テストの重要性を改め
て感じることができた。

<アンケート>
Q1.アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。
変数を扱うときにポインタ型なのかそうでないかを理解して代入すると
きになにを代入するか(アドレスなのか値なのか)を考える必要がある。

Q2.ここまでのところで、プログラムを作るときに重要だが自分で身に
付いていないと思うことは何ですか。
ポインタとアドレスの使い分けがまだ身についていないと思う。ポイン
タ型をどの時に使うのかの判断がまだちゃんとできていない。

Q3.リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
ポインタ型を学んだが、やはりテキストを見て仕組みを理解しただけで
は不十分でプログラムでポインタを使っていかないと慣れていかないの
が実感した。
まだまだ慣れない場面が多いが構造体などポインタを使う場面がでてく
るので演習を重ねて慣れていきたい。

y1910685 2b:○ Mon May 25 17:31:37 2020


学籍番号:1910685
ペア:個人作業
日付:5/25

[実行した課題]
演習2.b(受け取った可変長配列を逆順にする)
演習2.c(受け取った可変長配列を昇順にソートする)

[プログラムのソース (2.b)]
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>

#define MAXSIZE 10
#define CASE 5

bool vec_equ(int *a, int *b);
int tester(int a[][MAXSIZE+1], int b[][MAXSIZE+1],int m);
void test_result(int a, int n);
void ivec_reves(int *a);
int *ivec_new(int n);
void print_vec(int *a);

int test_pro[CASE][MAXSIZE + 1] = {
  {5,1,2,3,4,5,0,0,0,0,0},
  {6,1,4,5,67,1,2,0,0,0,0},
  {7,61,5,7,1,5,3,4,0,0,0},
  {8,8,45,3,4,3,6,7,9,0,0},
  {9,5,2,5,3,23,1,2,3,4,0}
};

int test_ans[CASE][MAXSIZE + 1] = {
  {5,5,4,3,2,1,0,0,0,0,0},
  {6,2,1,67,5,4,1,0,0,0,0},
  {7,4,3,5,1,7,5,61,0,0,0},
  {8,9,7,6,3,4,3,45,8,0,0},
  {9,4,3,2,1,23,3,5,2,5,0}
};

int main(){
  for(int n = 0;n<CASE;n++){
    ivec_reves(test_pro[n]);
  }
  test_result(tester(test_pro,test_ans,CASE),CASE);
  return 0;
}

bool vec_equ(int *a, int *b){
  if(a[0] != b[0])return false;
  for(int k = 1;k<=a[0];k++){
    if(a[k] != b[k])return false;
  }
  return true;
}

//配列の長さnのm個のテストケースを試行する
int tester(int a[][MAXSIZE+1], int b[][MAXSIZE+1], int m){
  int ans = 0;
  for(int k = 0;k < m;k++){
    if(vec_equ(a[k],b[k])){
      printf("Case%02d: OK\n",k+1);
    }else{
      ans = ans | (1 >> k);
      printf("Case%02d: NG\n",k+1);
    }
    printf("Pro:");print_vec(a[k]);
    printf("Ans:");print_vec(b[k]);
    printf("\n");
  }
  return ans;
}

void test_result(int a, int n){
  printf("*が正解、/が不正解で順に表示します。\n");
  for(int k = 0;k < n;k++){
    printf("%c", (a & (1 >> k)) ? '/' : '*');
  }
  printf("\n");
}

void ivec_reves(int *a){
  int dump = 0;
  int n = a[0];
  printf("before:");
  print_vec(a);
  for(int k = 0;k < n/2;k++){
    dump = a[k+1];
    a[k+1] = a[n-k];
    a[n-k] = dump;
  }
  printf("After :");
  print_vec(a);
  printf("\n");
}

void print_vec(int *a){
  for(int k = 1;k <= a[0];k++)printf("%d ",a[k]);
  printf("\n");
}

int *ivec_new(int n){
  int *ary = (int *)malloc((n+1) * sizeof(int));
  ary[0] = n;
  return ary;
}

[実行例 (2.b)]
before:1 2 3 4 5
After :5 4 3 2 1

before:1 4 5 67 1 2
After :2 1 67 5 4 1

before:61 5 7 1 5 3 4
After :4 3 5 1 7 5 61

before:8 45 3 4 3 6 7 9
After :9 7 6 3 4 3 45 8

before:5 2 5 3 23 1 2 3 4
After :4 3 2 1 23 3 5 2 5

Case01: OK
Pro:5 4 3 2 1
Ans:5 4 3 2 1

Case02: OK
Pro:2 1 67 5 4 1
Ans:2 1 67 5 4 1

Case03: OK
Pro:4 3 5 1 7 5 61
Ans:4 3 5 1 7 5 61

Case04: OK
Pro:9 7 6 3 4 3 45 8
Ans:9 7 6 3 4 3 45 8

Case05: OK
Pro:4 3 2 1 23 3 5 2 5
Ans:4 3 2 1 23 3 5 2 5

*が正解、/が不正解で順に表示します。
*****

[ソースの説明 (2.b)]

与えられた可変長配列(先頭にその配列の長さが格納されている)を受け
取って、それを逆順にする関数ivec_revesを作成した。また、テストケー
スの入力をtest_pro配列に、答えをtest_ans配列に入れておき、
test_pro配列を関数ivec_revesで処理したのちに、test_pro配列と、
test_ans配列が一致するかを確かめることによって単体テストを行った。

[考察等 (2.b)]

通常にテストを行う場合には、mallocを使用することで任意の長さの配
列を新しく作り、テストを行えば良いが、今回は異なる長さの配列を含
んだ多くのテストケースに対してもテストが行えるように、MAXSIZEで
配列の最大の長さをdefineしておき、それより短い長さの配列は末尾の
部分を0で埋めるようにしてテストケースの入力を作成した。配列を扱
う関数では先頭の値から長さを読み取り、それ以上先にはアクセスしな
いため、最長の長さが分かっていれば他は0で埋めても問題ないと判断
したからである。

[プログラムのソース (2.c)]
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>

#define MAXSIZE 10
#define CASE 5

bool vec_equ(int *a, int *b);
int tester(int a[][MAXSIZE+1], int b[][MAXSIZE+1],int m);
void test_result(int a, int n);
void ivec_sort(int *a);
int *ivec_new(int n);
void print_vec(int *a);

int test_pro[CASE][MAXSIZE + 1] = {
  {5,1,2,3,4,5,0,0,0,0,0},
  {6,1,4,5,67,1,2,0,0,0,0},
  {7,61,5,7,1,5,3,4,0,0,0},
  {8,8,45,3,4,3,6,7,9,0,0},
  {9,5,2,5,3,23,1,2,3,4,0}
};

int test_ans[CASE][MAXSIZE + 1] = {
  {5,1,2,3,4,5,0,0,0,0,0},
  {6,1,1,2,4,5,67,0,0,0,0},
  {7,1,3,4,5,5,7,61,0,0,0},
  {8,3,3,4,6,7,8,9,45,0,0},
  {9,1,2,2,3,3,4,5,5,23,0}
};

int main(){
  for(int n = 0;n<CASE;n++){
    ivec_sort(test_pro[n]);
  }
  test_result(tester(test_pro,test_ans,CASE),CASE);
  return 0;
}

bool vec_equ(int *a, int *b){
  if(a[0] != b[0])return false;
  for(int k = 1;k<=a[0];k++){
    if(a[k] != b[k])return false;
  }
  return true;
}

//配列の長さnのm個のテストケースを試行する
int tester(int a[][MAXSIZE+1], int b[][MAXSIZE+1], int m){
  int ans = 0;
  for(int k = 0;k < m;k++){
    if(vec_equ(a[k],b[k])){
      printf("Case%02d: OK\n",k+1);
    }else{
      ans = ans | (1 >> k);
      printf("Case%02d: NG\n",k+1);
    }
    printf("Pro:");print_vec(a[k]);
    printf("Ans:");print_vec(b[k]);
    printf("\n");
  }
  return ans;
}

void test_result(int a, int n){
  printf("*が正解、/が不正解で順に表示します。\n");
  for(int k = 0;k < n;k++){
    printf("%c", (a & (1 >> k)) ? '/' : '*');
  }
  printf("\n");
}

void ivec_sort(int *a){
  printf("before:");
  print_vec(a);
  for(int k = 1;k < a[0];k++){
    for(int l = 1;l < a[0] - (k-1);l++){
      if(a[l] > a[l+1]){
        int dump = a[l+1];
        a[l+1] = a[l];
        a[l] = dump;
      }
    }
  }
  printf("After :");
  print_vec(a);
  printf("\n");
}

void print_vec(int *a){
  for(int k = 1;k <= a[0];k++)printf("%d ",a[k]);
  printf("\n");
}

int *ivec_new(int n){
  int *ary = (int *)malloc((n+1) * sizeof(int));
  ary[0] = n;
  return ary;
}

[実行例 (2.c)]
before:1 2 3 4 5
After :1 2 3 4 5

before:1 4 5 67 1 2
After :1 1 2 4 5 67

before:61 5 7 1 5 3 4
After :1 3 4 5 5 7 61

before:8 45 3 4 3 6 7 9
After :3 3 4 6 7 8 9 45

before:5 2 5 3 23 1 2 3 4
After :1 2 2 3 3 4 5 5 23

Case01: OK
Pro:1 2 3 4 5
Ans:1 2 3 4 5

Case02: OK
Pro:1 1 2 4 5 67
Ans:1 1 2 4 5 67

Case03: OK
Pro:1 3 4 5 5 7 61
Ans:1 3 4 5 5 7 61

Case04: OK
Pro:3 3 4 6 7 8 9 45
Ans:3 3 4 6 7 8 9 45

Case05: OK
Pro:1 2 2 3 3 4 5 5 23
Ans:1 2 2 3 3 4 5 5 23

*が正解、/が不正解で順に表示します。
*****

[ソースの説明 (2.c)]

受け取った可変長の配列を昇順にソートする関数ivec_sortを作成した。
2.bと同様に入力と答えに分けた配列が一致するかによって単体テスト
を行った。なお、ivec_sort関数でのソートは隣合う数の大小が降順な
らば入れ替えることを繰り返して、n番目に大きい数の探索を繰り返す
ことによって行った。

[考察等 (2.c)]

単体テストについては2.bと同じなので、省略します。RubyやC#、java
といった他のさらに高級な言語ではvector型であったり、そもそも配列
が初めから可変長であったりして、メモリを動的に確保することで可変
長の配列を作るのが簡単だったが、C言語では開放したメモリのアドレ
スを渡すことによって作成されており、オブジェクト指向の言語との差
を感じた。また配列の先頭に長さを格納する方法は、定義外のメモリに
アクセスするリスクもなく、非常に簡潔に配列の長さを取得できるため
有用だと感じた。

[アンケート]
Q1. アドレス、ポインタについて納得しましたか。
しました。

Q2. 「構造体を用いた情報隠蔽」について納得しましたか。
しました。

Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
C++は触っていましたが、C言語はあまり触っていなかったので、特有の
様式が面白かったです.

y1910688 2b:○ Fri May 22 18:11:14 2020


プログラミング通論(久野先生)
第2回B課題

個人作業
提出日時 2020/5/22

一つ目の課題 演習2b

・作成したプログラム
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
int *ivec_concat(int *a) {
  int *c = ivec_new(a[0]);
  int count=a[0];
  for(int i = 1; i <= a[0]; i++) {
    c[i]=a[count];
    count--;
  }
  return c;
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
  int a[] = {6,1,2,3,4,5,6}, b[] = {6,6,5,4,3,2,1};
  int c[] = {5,2,5,7,3,8}, d[]={5,8,3,7,5,2};
  int *p = ivec_concat(a);
  int *q=ivec_concat(c);
  expect_iarray(p, b, 7, "[1,2,3,4,5,6]->[6,5,4,3,2,1]");
  expect_iarray(q, d, 6, "[2,5,7,3,8]->[8,3,7,5,2]");  
  return 0;
}


・実行例
[y1910688@sol ~/pp20]$ ./a.out
OK [1,2,3,4,5,6]->[6,5,4,3,2,1]
  6  6  5  4  3  2  1
  6  6  5  4  3  2  1
OK [2,5,7,3,8]->[8,3,7,5,2]
  5  8  3  7  5  2
  5  8  3  7  5  2


・説明
1つの列の内容を受け取り逆順にした列を返すプログラム。
*ivec_concat内では配列aの最終項から値を遡って
新しく作った配列cに代入することで逆順の列を作った。
テストケースは1つ目が偶数個の場合、2つ目が奇数個の場合とした。

・考察
今回はcountという変数を用いて疑似的に逆順に要素を入れ替えていったが、
for文自体をデクリメントで回せばより簡潔なプログラムになると感じた。
また、テストケースに関しては今回の課題には2個で
基本的にすべてのパターンに対応しているため十分といえる。

二つ目の課題 演習2c

・作成したプログラム
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
int *ivec_concat(int *a) {
  int *c = ivec_new(a[0]);
  int count=a[0],temp;
  for(int i = 1; i <= a[0]; i++) {
    for(int j=1;j<=a[0];j++){
      if(a[j]>a[i]){
	temp=a[i];
	a[i]=a[j];
	a[j]=temp;
      }
    }
  }
  return a;
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
  int a[] = {6,6,5,2,3,4,1}, b[] = {6,1,2,3,4,5,6};
  int c[] = {5,2,8,5,1,2}, d[]={5,1,2,2,5,8};
  int *p = ivec_concat(a);
  int *q=ivec_concat(c);
  expect_iarray(p, b, 7, "[6,5,2,3,4,1]->[1,2,3,4,5,6]");
  expect_iarray(q, d, 6, "[2,8,5,1,2]->[1,2,2,5,8]");  
  return 0;
}
  
・実行例
[y1910688@sol ~/pp20]$ ./a.out
OK [6,5,2,3,4,1]->[1,2,3,4,5,6]
  6  1  2  3  4  5  6
  6  1  2  3  4  5  6
OK [2,8,5,1,2]->[1,2,2,5,8]
  5  1  2  2  5  8
  5  1  2  2  5  8


・説明
与えられた列を昇順にソートするプログラム。
今回は逐一、値の大小を確認して交換を繰り返すソート方法を用いた。
2重ループの中でa[1]から順にa[1],a[2],...a[a[0]]まで
if文で大小関係をチェックしている。
テストケースは1つ目が数値のかぶりなし、2つ目が数値のかぶりありとした。

・考察
ソートには以前の情報系授業でバブルソートを始め様々なソートを学んできたので、
他のソート方法でも書いてみたい。
今回使用したソートは2つ目のケースのように同じ要素があっても特に問題なく動いた。
また、テストケースに関しては今回のプログラムでは基本的にこの2つで十分といえる。

・アンケート
Q1のA 
ポインタの参照先とポインタと配列との関係を確認すること。
Q2のA
我流より効率の良いプログラムでの書き方
Q3のA
単位テストの大切さを理解できた。

y1910692 2b:○ Wed May 20 00:32:19 2020


#2 B課題レポート
学籍番号:1910692
ペアの学籍番号個人作業:個人作業
提出日時:5/20

[課題の再掲1]
 演習2-C:1つの列を受け取り、その内容を昇順に整列した列を返す(例:
 「3,1,4」→ 「1,3,4」)。
 
[実際に書いたコードとその説明1]
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
void iarray_read(int*a,int n){
  for(int i=0;i<n;++i){
    printf("%d>",i+1);scanf(" %d",a+i);
  }
}

void iarray_print(int *a,int n){
  for(int i=0;i<n;++i){printf(" %d",a[i]);}
  printf(" ");
}

int *ivec_new(int size){
  int *a=(int*)malloc((size+1)*sizeof(int));
  a[0]=size;
  return a;
}

void ivec_read(int *a){iarray_read(a+1,a[0]);}
void ivec_print(int *a){iarray_print(a+1,a[0]);}

void ivec_reverse(int *a){
  int x;
  for(int i=1;i<=a[0];++i){
    for(int j=i;j<=a[0];++j){
      if(a[i]<a[j]){
        x=a[i];
        a[i]=a[j];
        a[j]=x;
      }
    }
  }
}

bool ivec_equal(int *a,int *b){
  for(int i=1;i<=a[0];++i){
    if(a[i]!=b[i]){return false;}
  }
  return true;
}

void expect_reverse(int *a,int *b,char *msg){
  ivec_reverse(a);
  printf("%s",ivec_equal(a,b)?"OK":"NO");
  ivec_print(a);
  printf(":");
  ivec_print(b);
  printf("%s\n",msg);
}

int main(){
  int a[]={9,1,2,3,4,5,6,7,8,9};
  int b[]={9,9,8,7,6,5,4,3,2,1};
  int c[]={3,-4,0,6};
  int d[]={3,6,0,-4};
  int e[]={1,8};
  expect_reverse(a,b,"1 2 3 4 5 6 7 8 9->9 8 7 6 5 4 3 2 1");
  expect_reverse(c,d,"-4 0 6->6 0 -4");
  expect_reverse(e,e,"8->8");
  return 0;
}

配列の中身を昇順にする関数void ivec_reverse(int *a)を製作した。
まず、引数の配列aを受け取った後、2つのfor文を使い1番小さいもの
をa[j]の中で特定し、a[i]に代入しそれを繰り返していくことで配列の
順番を昇順にすることができる。テストケースは、void
expect_reverse(int *a,int *b,char *msg)で行った。また、テストケー
スを行うのに際し、bool ivec_equal(int *a,int *b)を使用した。テス
トケース関数内ではまず、昇順にしたい配列aをivec_reverse(a);で逆
順にしてbと一致するかどうかの判定を行う。(ivec_equal(a,b))で一
致すれば"OK"、そうでなければ"NO"を表示するようにした。その後は、
ivec_print(a),ivec_print(b)を使って

 aを逆順にした配列:そうなるはずの結果 のように表示する。これら
 を使いmain関数内でテストケースを行った。行ったテストケースは、・
 いくつもの要素が入ったもの・要素が一つしかないものでテストを行
 い、その結果は下のようになった。
 
OK 9 8 7 6 5 4 3 2 1 : 9 8 7 6 5 4 3 2 1 1 2 3 4 5 6 7 8 9->9 8 7 6 5 4 3 2 1
OK 6 0 -4 : 6 0 -4 -4 0 6->6 0 -4
OK 8 : 8 8->8 


[考察1]
使用する配列の一番最初に、配列の大きさを入れるという方法は、新し
く変数を宣言したりせずに済むのでとても便利であると思った。また、
普段自分たちが組んでいるプログラムでさえ少しだけ要素数の大きな配
列を扱うことがあるのに将来とても大きなデータを扱う際に
malloc,freeの事を知らないととても大変なことになるのではないかと
いうのがプログラムを組んでる最中に思ったことである。

[課題の再掲2]
演習3-a: 上のcdseq.cに、等差数列を初項に戻す機能cdseq_resetを追加してみよ。 
[実際に書いたコードとその説明2]
 まず、cdseq_resetを入れたcdseq.cを記載する。
// cdseq.c -- cdseq implementation.
#include <stdlib.h>
#include "cdseq.h"
struct cdseq { int value, diff,count; };
struct cdseq *cdseq_new(int s, int d) {
  struct cdseq *r = (struct cdseq*)malloc(sizeof(struct cdseq));
  r->value = s;r->diff = d;r->count=0; return r;
}
int cdseq_get(struct cdseq *r) {
  int v = r->value; r->value += r->diff;r->count+=1 ;return v;
}
void cdseq_free(struct cdseq *r) {
  free(r);
}

int  cdseq_reset(struct cdseq *r){
  while(r->count!=0){
    r->value-=r->diff;
    r->count-=1;
  }
 int v=r->value;
 return v;
}

例題に手を加えた箇所は主に2か所である。cdseq_getをだんだん行っ
ていくと初項s,交差dの等差数列ではa+d*x(最初x=0である。)におけるx
の値が1ずつ増加していく。この数を把握しておけば、いつのタイミン
グでも初項に戻れる関数を製作することができるので、まず、struct
cdeqにint countを追加した。また、cdseq_getをするたびにcountも1増
えるように修正をした。そして、cdseq_resetに関してだが、たまって
いるcountに対して、valueからdiffをcountが0になるまで引き続ける
といずれ初項になるのでfor文を用いて引き算を行うようにし。最後に
初項の値になったr->valueを返すようにした。

次にこれを基にmain関数のあるプログラムでテストを行った。

#include <stdio.h>
#include "cdseq.h"
void expect_int(int i1,int i2,char *msg){
  printf("%s %d : %d %s\n",(i1==i2)?"OK":"NO",i1,i2,msg);
}

int main(void) {
  struct cdseq *s = cdseq_new(24, 3);
  int i;
  expect_int(cdseq_get(s),24,"24+3*0");
  expect_int(cdseq_get(s),27,"24+3*1");
  expect_int(cdseq_reset(s),24,"24+3*0");
  expect_int(cdseq_get(s),24,"24+3*0");
  expect_int(cdseq_get(s),27,"24+3*1");
  expect_int(cdseq_get(s),30,"24+3*2");
  expect_int(cdseq_get(s),33,"24+3*3");
  expect_int(cdseq_get(s),36,"24+3*4");
 expect_int(cdseq_reset(s),24,"24+3*0");
  return 0;
}

テストのために関数void expect_int(int i1,int i2,char *msg)を作っ
た。数列の計算結果であるi1と出るはずであろう結果i2
が等しければ"OK"、そうでなければ"NO"と表示するようにした。その後
ろは 数列の計算結果 :出るはずであろう結果 詳しい計算
とテストの結果が見やすくなるように表示させた。main関数内で何回か
等差数列の計算をさせた後初項に戻すというテストを行ったところ下の
ようになり、正しく動いているのが分かる

OK 24 : 24 24+3*0
OK 27 : 27 24+3*1
OK 24 : 24 24+3*0
OK 24 : 24 24+3*0
OK 27 : 27 24+3*1
OK 30 : 30 24+3*2
OK 33 : 33 24+3*3
OK 36 : 36 24+3*4
OK 24 : 24 24+3*0。
[考察2]
ヘッダファイルの構造体についての定義を書かなくていいのは、手間が
省けてよいなと思った。main関数にも書かなくてよいのでコード自体が
少しすっきりした感じのようにも見える。また、ポインタを使う頻度が
とても多いかつ知識的にも複雑なのでポインタを理解してないとこの分
野に取り組むのには少し難しいと感じた。つまりは、プログラミングを
学ぶ上で、優先して抑えておくべき項目の一つであると考える。

[アンケート]
Q1.定義をしっかり抑えられえいるかどうかがまず大事だと思います(*
pで宣言と引数で*pとして渡すときの違いなど)

Q2.良く確認せずエラーだらけになってしまう所である。数学の微積に
似た感じがあるので1行1行確認しながらコードを組んでいきたい。

Q3.2回目にして少しキャパを超えてきたので復習を重点的に行いたいと思う。

y1910693 2b:△ Mon May 25 13:14:46 2020


課題2b



[課題1-b]
#include<stdio.h>
#include<string.h>
#include<stdbool.h>

void iarray_reverse(int *b, int n){
    int a, i;
    if(n != 0){ 
        for(i = 0; i < n/2; i++){
            a = b[i];
            b[i] = b[n - i - 1];
            b[n - i - 1] = a;
        }
    }
}

bool judge(int *a, int *b, int n){
    int i = 0;
    iarray_reverse(a, n);
    for(i = 0; i < n; i++){
        if(a[i] != b[i]){ return false;}
    }
        return true;
}

void expect_iarray(int *a, int *b, int n, char *array){
    printf("%s\t%s\n",judge(a, b, n)?"OK":"NG", array);
}

int main(){
  int a[] = {8,5,2,4,1}, ra[] = {1,4,2,5,8}, b[] = {1,2,3,4,5}, rb[] = {5,4,3,2,1}, c[] = {9}, rc[] = {9}, d[] = {-3,2,-5}, rd[] = {-5,2,-3};
    expect_iarray(a, ra, 5, "85241 -> 14258");
    expect_iarray(b, rb, 5, "12345 -> 54321");
    expect_iarray(c, rc, 1, "9 -> 9");
    expect_iarray(d, rd, 3, "-32-5 -> -52-3");
    return 0;
}

[1-bについて]
配列の並びを逆順で表示させるプログラムを作成した。
配列の定義を一から全部おこなわなくても、もともとb[i]が入っていたところを
b[n-i-1]と入れ替えることでより簡潔なプログラムを作成できることが分かった。


[課題2-a]
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
int *ivec_kougo(int *a, int *b) {
  int *c = ivec_new(a[0]+b[0]);
  if(a[0] >= b[0]){
  for(int i = 0; i <= a[0]; ++i) { c[2*i] = a[i]; }
  for(int i = 0; i <= b[0]; ++i) { c[2*i + 1] = b[i]; }
  return c;
  }
  else{
    for(int i = 0; i <= b[0]; ++i) { c[2*i] = b[i]; }
    for(int i = 0; i <= a[0]; ++i) { c[2*i + 1] = a[i];
    }
    return c;
  }
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
  int a[] = {3,1,2,3}, b[] = {2,4,5}, c[] = {3,2,1,4,2,5,3};
  int d[] = {2,4,5}, e[] = {3,1,2,3}, f[] = {3,2,1,4,2,5,3};
  int g[] = {4,9,8,7,6}, h[] = {4,5,4,3,2}, i[] = {4,4,9,5,8,4,7,3,6,2};
  int *p = ivec_kougo(a, b);
  int *q = ivec_kougo(d, e);
  int *r = ivec_kougo(g, h);
  expect_iarray(p, c, 7, "[3,1,2,3]+[2,4,5]=[3,2,1,4,2,5,3]");
  expect_iarray(q, f, 7, "[2,4,5]+[3,1,2,3]=[3,2,1,4,2,5,3]");
  expect_iarray(r, i, 10, "[4,9,8,7,6]+[4,5,4,3,2]=[4,4,9,5,8,4,7,3,6,2]");
  return 0;
}


[2-aについて]
2つの配列を受け取り、両方の配列の値を交互に並べた列を返すようなプログラム
を作成した。
それぞれの配列を合わせるときもともとのサイズが大きいほうを偶数番目、
小さいほうを奇数番目とすれば指定されたプログラムが成り立つと分かった。
今回の場合で一番大事なことは、それぞれの配列のサイズが一個目に来ると
自分自身でしっかりと把握しておくことであると考える。

[アンケート]
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いま
すか。
サイズと場所の把握

Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に付いて
いないと思うことは何ですか。
NULL使い方や二次配列の考え方など

Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
いままであやふやなまま放置してきたアドレスやポインタなどを理解で
きた気がします

y1910695 2b:○ Mon May 25 15:06:14 2020


個人作業
提出日時:2020/05/25

一つ目
演習2-b
[コード]
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
void iarray_read(int *a, int n)
{
    for(int i = 0; i < n; ++i)
    {
        printf("%d> ",i+1);
        scanf("%d",a+i);
    }
}
void iarray_print(int *a, int n)
{
    for(int i = 0; i < n; ++i)
    {
        printf("%2d",a[i]);
    }
    printf("\n");
}
int *ivec_new(int size)
{
    int *a = (int*)malloc((size+1) * sizeof(int));
    a[0] = size;
    return a;
}
void ivec_read(int *a)
{
    iarray_read(a+1, a[0]);
}
void ivec_print(int *a)
{
    iarray_print(a+1, a[0]);
}
bool iarray_equal(int *a, int *b, int n)
{
  for(int i = 0; i < n; ++i)
  {
    if(a[i] != b[i])
    {
        return false;
    }
  }
  return true;
}
void expect_iarray(int *a, int *b, int n, char *msg)
{
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n);
  iarray_print(b, n);
}
int *ivec_b(int *a)
{
    int *b = ivec_new(a[0]);
    for(int i = 0; i < a[0]; ++i)
    {
        b[a[0]-i] = a[i+1];
    }
    return b;
}
int main(void)
{
    int a1[] = {3,1,2,3};
    int b1[] = {3,3,2,1};
    int *p1 = ivec_b(a1);
    expect_iarray(p1, b1, 4, "[1,2,3] => [3,2,1]");
    int a2[] = {4,1,3,5,7};
    int b2[] = {4,7,5,3,1};
    int *p2 = ivec_b(a2);
    expect_iarray(p2, b2, 5, "[1,3,5,7] => [7,5,3,1]");
    int *c, *d;
    c = ivec_new(3);
    ivec_read(c);
    d = ivec_b(c);
    ivec_print(d);
    free(c);
    free(d);
    return 0;
}
[実行例]
OK [1,2,3] => [3,2,1]
 3 3 2 1
 3 3 2 1
OK [1,3,5,7] => [7,5,3,1]
 4 7 5 3 1
 4 7 5 3 1
1> 2
2> 4
3> 6
 6 4 2
[説明]
*ivec_bに与えられた配列を逆順に出力するプログラムが書いてある。
新しく同じ長さの配列bを作り、aの最初の要素から順番に、bに後ろか
ら入れていった。
[考察]
配列の要素数が配列の最初に配列の最初に記録してあると同じ長さの配
列を作ることが簡単なため、とても簡単になったと感じた。配列の要素
数が記録してあると、他にもテキストにあったように複数の配列を合わ
せたりするなど、色々と工夫ができて良いと感じた。

二つ目
演習2-d
[コード]
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
void iarray_read(int *a, int n)
{
    for(int i = 0; i < n; ++i)
    {
        printf("%d> ",i+1);
        scanf("%d",a+i);
    }
}
void iarray_print(int *a, int n)
{
    for(int i = 0; i < n; ++i)
    {
        printf("%2d ",a[i]);
    }
    printf("\n");
}
int *ivec_new(int size)
{
    int *a = (int*)malloc((size+1) * sizeof(int));
    a[0] = size;
    return a;
}
void ivec_read(int *a)
{
    iarray_read(a+1, a[0]);
}
void ivec_print(int *a)
{
    iarray_print(a+1, a[0]);
}
bool iarray_equal(int *a, int *b, int n)
{
  for(int i = 0; i < n; ++i)
  {
    if(a[i] != b[i])
    {
        return false;
    }
  }
  return true;
}
void expect_iarray(int *a, int *b, int n, char *msg)
{
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n);
  iarray_print(b, n);
}
int *ivec_d(int *a, int *b)
{
    int i = 1;
    int j = 1;
    int k = 1;
    int *c = ivec_new(a[0] + b[0]);
    while(i <= a[0] && j <= b[0])
    {
        if(a[i] < b[j])
        {
            c[k] = a[i];
            i += 1;
        }else{
            c[k] = b[j];
            j += 1;
        }
        k += 1;
    }
    while(i <= a[0])
    {
        c[k] = a[i];
        i += 1;
        k += 1;
    }
    while(j <= b[0])
    {
        c[k] = b[j];
        j += 1;
        k += 1;
    }
    return c;
}
int main(void)
{
    int a1[] = {3,1,3,5};
    int b1[] = {3,2,4,6};
    int c1[] = {6,1,2,3,4,5,6};
    int *p1 = ivec_d(a1,b1);
    expect_iarray(p1, c1, 7, "[1,3,5]+[2,4,6]=[1,2,3,4,5,6]");
    int a2[] = {3,1,100,1000};
    int b2[] = {1,10};
    int c2[] = {4,1,10,100,1000};
    int *p2 = ivec_d(a2,b2);
    expect_iarray(p2, c2, 5, "[1,100,1000]+[10]=[1,10,100,1000]");
    int *d, *e, *f;
    d = ivec_new(3);
    ivec_read(d);
    e = ivec_new(3);
    ivec_read(e);
    f = ivec_d(d,e);
    ivec_print(f);
    free(d);
    free(e);
    free(f);
    return 0;
}
[実行例]
OK [1,3,5]+[2,4,6]=[1,2,3,4,5,6]
 6  1  2  3  4  5  6
 6  1  2  3  4  5  6
OK [1,100,1000]+[10]=[1,10,100,1000]
 4  1 10 100 1000
 4  1 10 100 1000
1> 3
2> 5
3> 6
1> 1
2> 8
3> 9
 1  3  5  6  8  9
[説明]
*ivec_dに両方の配列をマージし昇順の配列を返すプログラムが書かれている。
最初に二つの要素数を足した要素数を持つ配列cをつくった。
二つの配列の最初の要素から順番に見ていき、小さいを方を配列cに最
初から入れた。次に値を比較するときは、cに入れた値の次の要素を比
較した。
これを順番にやっていくと、片方の配列だけが最後の要素までcに入れ
られ、もう片方の配列は最後までcに入れられてない。そのため、forの
後に、whileを用いて、cに入れられてない要素をcにいれた。

[考察]
一つ目でも書いたように要素数が記録されていると、二つの要素数の和
の要素数を持つ新しい配列を作るのが簡単に感じた。
このプログラムを作っているときに、参照してはいけないところを参照
してしまい、エラーになることがあった。それは配列の要素がない場所
と、もう一つの配列の要素を比較してしまったため、エラーになった。
エラーにならないように、片方の配列の最後の要素まで比較したら、
forを終わりにし、whileを用いてもう一つの配列の最後の要素までcに
いれるようにした。

[アンケート]
Q1.変数に入れるときに型が同じになるようにすること。
Q2.プログラムを後から見返したときに、どのようなプログラムを書い
ていたか分かりづらいところ。
Q3.理解が足りないところがあるのでしっかり復習して理解を深めたい。

y1910697 2b:○ Sun May 24 20:04:49 2020


学籍番号 1910697
個人作業
5月24日提出

演習2a 二つの列を受け取り交互に並べた列を返す。
プログラム
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}

void ivec_read(int *a) { iarray_read(a+1, a[0]); }
void ivec_print(int *a) { iarray_print(a+1, a[0]); }


bool ivec_equal(int *a,int *b){
        int i;
        for (i=0;i<a[0];i++){
            if(a[i]!=b[i]){
                return false;
            }
        }
        return true;
}
int *ivec_cross(int *a, int *b) {
    int i;
  if(a[0]>b[0]){
    int *c = ivec_new(a[0]+b[0]);
    for(i=0;i<b[0];i++){
        c[2*i+1]=a[i+1];
        c[2*i+2]=b[i+1];
    }
    for(i=0;i<a[0]-b[0];i++){
        c[2*b[0]+i+1]=a[b[0]+i+1];
    }
    return c;
  }else if (a[0]<b[0]){
      int *c = ivec_new(a[0]+b[0]);
    for(i=0;i<a[0];i++){
        c[2*i+1]=a[i+1];
        c[2*i+2]=b[i+1];
    }
    for(i=0;i<b[0]-a[0];i++){
        c[2*a[0]+i+1]=b[a[0]+i+1];
    }
    return c;
  }else{
      int *c = ivec_new(2*a[0]);
    for(i=0;i<a[0];i++){
        c[2*i+1]=a[i+1];
        c[2*i+2]=b[i+1];
    }
    return c;
  }
  
  }

void expect_ivec(int *a,int *b,char *msg){
printf("%s %s\n",ivec_equal(a,b)?"OK":"NG",msg);
ivec_print(a);ivec_print(b);
}

int main(){
 int a[]={3,2,3,4},b[]={3,6,7,8},c[]={1,0},
    d[]={5,2,4,6,8,0},e[]={0};
    {
     int z[]={6,2,6,3,7,4,8};
     expect_ivec(ivec_cross(a,b),z,"{6,2,6,3,7,4,8}");}
    {
     int z[]={4,2,0,3,4};
     expect_ivec(ivec_cross(a,c),z,"{4,2,0,3,4}");}
 {
    int z[]={4,0,6,7,8};
    expect_ivec(ivec_cross(c,b),z,"{4,0,6,7,8}");
 }
 {
    int z[]={6,0,2,4,6,8,0};
    expect_ivec(ivec_cross(c,d),z,"{6,0,2,4,6,8,0}");
 }
 {
    int z[]={3,2,3,4};
    expect_ivec(ivec_cross(a,e),z,"{3,2,3,4}");
 }
    return 0;
}
説明
ivec_newなどはテキストのexample codeのままです。
ivec_crossが受け取った配列の要素を交互に並べる関数です。長さが違
うときには長い側の配列の要素を後ろにそのまま付けます。
ivec_equalはテストケースでの正誤確認用です。配列の全要素をfor文
を用いてチェックしていきます。
 
実行結果
OK {6,2,6,3,7,4,8}
  2  6  3  7  4  8
  2  6  3  7  4  8
OK {4,2,0,3,4}
  2  0  3  4
  2  0  3  4
OK {4,0,6,7,8}
  0  6  7  8
  0  6  7  8
OK {6,0,2,4,6,8,0}
  0  2  4  6  8  0
  0  2  4  6  8  0
OK {6,0,2,4,6,8,0}
  2  3  4
  2  3  4

考察
渡された配列の要素数0の時のチェックのやり方が思いつきませんでし
た。nullでない場合を使えばよかったのかなと思いますがアドレスに何
かデータが残っていた時にエラーを出力してくれない可能性を考えてし
まいました。

演習3
a 等差数列を初項に戻すcdseq_resetを追加
b getの変更、cdseq_fwdの追加、cdseq_numの追加
c 等比数列の実装

プログラム
// cdseq.h --- constant difference sequence API.
struct cdseq *cdseq_new(int s, int d);
int cdseq_get(struct cdseq *r);
void cdseq_free(struct cdseq *r);
void cdseq_reset(struct cdseq *r);
void cdseq_fwd(struct  cdseq *r);
int cdseq_num(struct cdseq *r);
struct cddif *cddif_new(float s, float d);
float cddif_get(struct cddif *r);
void cddif_fwd(struct cddif *r);
void cddif_reset(struct cddif *r);
int cddif_num(struct cddif *r);

// cdseq.c -- cdseq implementation.
#include <stdlib.h>
#include "cdseq.h"
struct cdseq { int value, diff,number; };
struct cdseq *cdseq_new(int s, int d) {
  struct cdseq *r = (struct cdseq*)malloc(sizeof(struct cdseq));
  r->value = s; r->diff = d;r->number=0; return r;
}
int cdseq_get(struct cdseq *r) {
  int v = r->value; return v;
}
void cdseq_free(struct cdseq *r) {
  free(r);
}
void cdseq_reset(struct cdseq *r){
  r->value=r->value-(r->number*r->diff);
  r->number=0;
}
void cdseq_fwd(struct  cdseq *r){
  r->value += r->diff;
  r->number++;
}
int cdseq_num(struct cdseq *r){
  int n = r->number;return n; 
}
struct cddif {float value, diff;int number;};
struct cddif *cddif_new(float s, float d){
  struct cddif *r=(struct cddif*)malloc(sizeof(struct cddif));
  r->value=s;r->diff=d;r->number=0;
  return r;
}
float cddif_get(struct cddif *r) {
  float v = r->value; return v;
}
void cddif_fwd(struct  cddif *r){
  r->value =r->value*r->diff;
  r->number++;
}
void cddif_reset(struct cddif *r){
  int i;
  for (i=0;i<r->number;i++){
  r->value=r->value/r->diff;
  }
  r->number=0;
}
int cddif_num(struct cddif *r){
  int n = r->number;return n; 
}

// test_cdseq_1.c --- unit test for cdseq.
#include <stdio.h>
#include "cdseq.h"
void expect_int(int i1, int i2, char *msg) {
  printf("%s %d:%d %s\n", (i1==i2)?"OK":"NG", i1, i2, msg);
}
void expect_float(float i1, float i2, char *msg) {
  printf("%s %g:%g %s\n", (i1==i2)?"OK":"NG", i1, i2, msg);
}

int main(void) {
  struct cdseq *s = cdseq_new(2, 3);
  expect_int(cdseq_get(s), 2, "2+3*0 = 2");
  expect_int(cdseq_num(s), 0, "2+3*0 = 5");
  expect_int(cdseq_get(s), 2, "2+3*0 = 2");
  expect_int(cdseq_num(s), 0, "2+3*0 = 5");
  cdseq_fwd(s);
  expect_int(cdseq_get(s), 5, "2+3*1 = 5");
  expect_int(cdseq_num(s), 1, "2+3*1 = 5");cdseq_fwd(s);
  expect_int(cdseq_get(s), 8, "2+3*2 = 8");
  expect_int(cdseq_num(s), 2, "2+3*2 = 8");
  cdseq_reset(s);
  expect_int(cdseq_get(s), 2, "2+3*0 = 5");
  expect_int(cdseq_num(s), 0, "2+3*0 = 5");
 struct cddif *t = cddif_new(10, 0.5);
 expect_float(cddif_get(t),10,"10*(0.5)^0=10");
 expect_int(cddif_num(t), 0, "10*(0.5)^0 = 10");
 cddif_fwd(t);
 expect_float(cddif_get(t),5,"10*(0.5)^1=5");
 expect_int(cddif_num(t), 1, "10*(0.5)^1 = 5");
 cddif_fwd(t);
 expect_float(cddif_get(t),2.5,"10*(0.5)^2=2.5");
 expect_int(cddif_num(t), 2, "10*(0.5)^2 = 2.5");
 cddif_reset(t);
 expect_float(cddif_get(t),10,"10*(0.5)^0=10");
 expect_int(cddif_num(t), 0, "10*(0.5)^0 = 10");
 
  return 0;
}
説明

構造体による情報隠蔽を利用したコードです。上から順にAPIのヘッダ
ファイル、構造体の実装ファイル、テスト用のファイルのプログラムで
す。cdseqはサンプルに現在第何項目の値をvalueに記録しているかを示
す整数numberを追加した以外はそのままです。cdseq_resetは等差数列
を初項に戻すプログラムです。numberの値とdiffの値の積を減算して初
項の値に戻します。cdseq_fwdはcdseq_getの次の項にvalueの値を変更
するコードを利用しています。numberの値を1ずつ増やします。
cdseq_numはnumberの値をそのまま返します。

cddifは等比数列を表す構造体です。cdseqのvalue,diffの型を実数型に
してnumberは整数型のままで実装しました。reset,fwd,get,numはcdseq
のものをほぼそのまま利用して実装しました。cddif_resetはfor文を用
いて初項に戻しています。

実行結果
OK 2:2 2+3*0 = 2
OK 0:0 2+3*0 = 5
OK 2:2 2+3*0 = 2
OK 0:0 2+3*0 = 5
OK 5:5 2+3*1 = 5
OK 1:1 2+3*1 = 5
OK 8:8 2+3*2 = 8
OK 2:2 2+3*2 = 8
OK 2:2 2+3*0 = 5
OK 0:0 2+3*0 = 5
OK 10:10 10*(0.5)^0=10
OK 0:0 10*(0.5)^0 = 10
OK 5:5 10*(0.5)^1=5
OK 1:1 10*(0.5)^1 = 5
OK 2.5:2.5 10*(0.5)^2=2.5
OK 2:2 10*(0.5)^2 = 2.5
OK 10:10 10*(0.5)^0=10
OK 0:0 10*(0.5)^0 = 10

考察

等比数列を実装するときにcddif_resetを作るときに一度に除算を行わ
なかったのは割ろうとしたときに割る数に整数型を要求われたからです。
コンピューターでの実数での除算について理解できていない部分がある
ことがわかりました。構造体のアドレスだけを渡すことでテスト用のファ
イルからは構造体の中身はわからなくなっていることがわかりました。
また、引数が一つに見えても複数の値を同時に引数にしたり、それらの
値を操作できるのは便利だと思いました。

アンケート
Q1  アドレス、ポインタを使うプログラムで注意すること
A1 複雑になりやすいと感じたので極力簡潔にすること。
メモリ上のデータを直接触れるので大事なデータに触らないように気を
付ける。ポインタの型や長さ。
Q2 プログラムを作るときに重要だが身についていないこと
A2 他人とのコミュニケーション能力、調べる能力
Q3 リフレクション
A3 ポインタやアドレスについてある程度は使い方がわかったと思いま
す。構造体のアドレスを用いる便利さがよくわかりました。少し難しかっ
たですが便利なので積極的に使って行きたいと思います。

y1910698 2b:○ Wed May 20 03:27:04 2020


プログラミング通論レポート#2b
提出日付:2020年5月20日

[取り組んだ課題と作成したプログラム:1つ目]
課題:演習1b
・実際に作成したプログラム
//逆順配列

#include <stdio.h>
#include <stdbool.h>

void iarray_print(int *a, int n)
 {
     for(int i = 0; i < n; ++i) 
     { 
         printf(" %2d", a[i]); 
         } 
         printf("¥n"); 
         }

bool iarray_equal(int *a, int *b, int n) 
{
 for(int i = 0; i < n; ++i) 
{
 if(a[i] != b[i]) 
  {
 return false;
   }
 } 
 return true;
   }
 void expect_iarray(int *a, int *b, int n, char *msg) 
 { 
     printf("%s %s¥n", iarray_equal(a, b, n)?"OK":"NG", msg); 
     iarray_print(a, n); iarray_print(b, n); 
 }

 void iarray_revese(int*a, int n)
 {
     int z;
     for (int i=0; i<n/2; i++)
     {
      z = a[i];
      a[i] = a[n-1-i];
      a[n-1-i] = z;
    }
 }

 int main (void)
 {
     int a[] = {8,5,2,4,1};
     int b[] = {1,4,2,5,8};
     //新しいテストケース
     int c[] = {2,3,4,5};
     int d[] = {5,4,3,2};
     int e[] = {-2,-5,2,5,2,5};
     int f[] = {5,2,5,2,-5,-2};
     //ここからテスト
     iarray_revese(a,5);
     expect_iarray(a,b,5, "85241 -> 14258");
     iarray_revese(c,4);
     expect_iarray(c,d,4, "2345 -> 5432");
     iarray_revese(e,6);
     expect_iarray(e,f,6, "-2-52525-> 5252-5-2");

     return(0);

 }

[プログラムの実行例]
OK 85241 -> 14258
  1  4  2  5  8
  1  4  2  5  8
OK 2345 -> 5432
  5  4  3  2
  5  4  3  2
OK -2-52525-> 5252-5-2
  5  2  5  2 -5 -2
  5  2  5  2 -5 -2
[プログラムの説明]
iarray_print関数は与えられた配列を表示します。iarray_equal関数は
二つの配列を各要素ごとに比べていき、その配列が等しいかどうかを判
別します。expect_iarray関数はこれらの二つの関数を用いることで、
プログラムが正しく動作しているかのテストを行うものです。
iarray_revese関数で配列の要素を逆順にします。main関数では
iarray_reverse関数が正しく動作しているかexpect_iarray関数を用い
て3つのテストケースを実行させます。 正しく動作している場合にはOK、
正しく動作していない場合にはNGと表示され、逆順になる前の配列と変
換後の配列も表示されます。また、テキストにないテストケースとして
配列c,d,e,fを加えてテストケースを二つ増やしています。

[考察]
iarray_revese関数はとても簡潔にかけたのでとても良いと思う。nの値
に配列の長さを受け取らないと正しく動かない関数なので、関数内でそ
の長さも計算してしまえるように改良すればさらに扱いやすい関数にな
るのではないかと思った。また、実行例を見てみてOKと書かれる行と配
列の変化を表す行の間には改行を入れたほうがわかりやすいので次に作
成するプログラムではそのような点に配慮したい。

[取り組んだ課題と作成したプログラム:2つ目]
課題:演習1c
・実際に作成したプログラム
//配列を昇順に

#include <stdio.h>
#include <stdbool.h>

void iarray_print(int *a, int n)
 {
     for(int i = 0; i < n; ++i) 
     { 
         printf(" %2d", a[i]); 
         } 
         printf("¥n"); 
         }

bool iarray_equal(int *a, int *b, int n) 
{
 for(int i = 0; i < n; ++i) 
{
 if(a[i] != b[i]) 
  {
 return false;
   }
 } 
 return true;
   }
 void expect_iarray(int *a, int *b, int n, char *msg) 
 { 
     printf("%s %s¥n", iarray_equal(a, b, n)?"OK":"NG", msg); 
     iarray_print(a, n); iarray_print(b, n); 
 }

void iarray_sort(int *a, int n)
{
    int t;
    for (int i=0; i < n; i++)
    {
        for (int x = i+1; x < n ;x++)
        {
            if (a[i] > a[x])
            {
                t = a[i];
                a[i] = a[x];
                a[x] = t;
            }
        }
    }
}

int main (void)
{
    int a[] = {8,5,2,4,1};
    int b[] = {1,2,4,5,8};
    iarray_sort(a,5);
    expect_iarray(a,b,5, "85241 -> 12458");
//追加のテストケース
    int c[] = {-1,-2,-1,-8,-5};
    int d[] = {-8,-5,-2,-1,-1};
    iarray_sort(c,5);
    expect_iarray(c,d,5, "-1-2-1-8-5 -> -8-5-2-1-1");
    int e[] = {50,20,40};
    int f[] = {20,40,50};
    iarray_sort(e,3);
    expect_iarray(e,f,3, "50 20 40 -> 20 40 50");    
    return(0);
}

[プログラムの実行例]
OK
 85241 -> 12458
  1  2  4  5  8
  1  2  4  5  8
OK
 -1-2-1-8-5 -> -8-5-2-1-1
 -8 -5 -2 -1 -1
 -8 -5 -2 -1 -1
OK
 50 20 40 -> 20 40 50
 20 40 50
 20 40 50

[プログラムの説明]
iarray_print関数、iarray_equal関数、expect_iarray関数は演習1bで
作成したものと同様の働きをする関数です。iarray_sort関数は配列の
隣り合う要素同士をどちらの方が大きいかを比べていき、小さい要素を
添字の小さな方へと移動させていくという仕組みで配列の要素を昇順に
変換する関数です。main関数では実際にiarray_sort関数を用いて3つの
テストケースを実行して、正しく動作した場合にはOK、そうでない場合
にはNGと表示させるようにしています。なお、//追加のテストケースと
いう文以下のコードがテキストにはない、追加で考えたテストケースで
す。

[考察]
for文の中にfor文を用いてループするプログラムをiarray_sortではう
まく利用できたと思う。初めはiを0から1ずつnまで増やしていきその都
度配列の最小値を求めて、a[i]番目に代入するループを考えたが、それ
ではプログラムに行わせる演算量が配列の長さが長くなった時には膨大
になってしまうと思い他の方法はないかと模索した結果上記のような方
法を思いついた。初めに思いついた方法よりも多くの動作をコンピュー
タに要求しない軽いプログラムを製作できたと考えられる。また、配列
の要素を昇順に変更するプログラムはとても実用的であると思った。具
体的には、学生のテストの点数を管理する際にテストの点を昇順に変更
するプログラムがあると便利であると考えられる。

[アンケート]
Q1. ポインタ変数がどのアドレスを指しているかをよく理解すること。
自分でプログラムを作成している間にわからなくなってしまって混乱し
ないこと。

Q2.条件分岐を含むループをするプログラムを作成する能力。条件の設
定が不十分(穴がある)である場合が時々ある。

Q3. 配列を扱うのがもともとあまり得意ではなかったうえにアドレスま
で入ってくると脳内処理が追い付かなくなってとても大変だと思った。

y1910699 2b:○ Sat May 23 21:39:26 2020


プログラミング通論 第二回レポート課題(b)
2020/5/23

【プログラム1(配列の並び順を逆順にする)】

1  #include <stdio.h>
2  #include <stdbool.h>
3
4  void iarray_read(int *a, int n) {
5    for(int i=0; i<n; ++i) {
6      printf("%d> ", i+1);
7      scanf("%d", a+i);
8    }
9  }
10
11 void iarray_print(int *a, int n) {
12   for(int i=0; i<n; ++i) {
13     printf(" %2d", a[i]);
14   }
15   printf("\n");
16 }
17
18 void iarray_reverse(int *a, int n) {
19   int b[n-1];
20   for(int i=0; i<n; ++i) {
21     b[i]=a[n-i-1];
22   }
23   for(int i=0; i<n; ++i) {
24     a[i]=b[i];
25   }
26 }
27
28 bool compare_iarray(int *a, int *b, int n) {
29   for(int i=0; i<n; ++i) {
30     if (a[i] != b[i]){
31     return false;
32     }
33   }
34   return true;
35 }
36
37 void expect_iarray(int *a, int *b, int n, char *msg) {
38   printf("%s: %s\n", compare_iarray(a, b, n)? "OK":"NG", msg);
39 }
40
41 int main(void) {
42   int a[] = {8,5,2,4,1};
43   int b[] = {1,4,2,5,8};
44   int c[] = {0,1,2,0};
45   int d[] = {0,1,2,0};
46   iarray_reverse(a, 5);
47   expect_iarray(a, b, 5, "85241 -> 14258");
48   iarray_reverse(c, 4);
49   expect_iarray(c, d, 4, "0120 -> 0120");
50   return 0;
51 }


【説明】

1行目では、stdio.hをインクルードしている。

2行目では、stdbool.hをインクルードしている。

4行目~9行目では、iarray_read関数を定義している。
この関数によって、入力された数値を配列に格納している。

11行目~16行目では、iarray_print関数を定義している。
この関数によって、格納された配列をターミナルに表示している。

18行目~26行目では、iarray_reverse関数を定義している。
新たに用意した空の配列bに、配列aに格納された値を逆順に格納している。
配列bへの格納が全て終わったら、配列aの内容を配列bの内容に一致さ
せることで、最終的に配列aの並び順を逆順にしている。

28行目~35行目では、compare_iarray関数を定義している。
ある二つの配列を比較し、完全に一致していればtrue,そうでなければ
falseを返している。

37行目~39行目では、expect_iarray関数を定義している。
compare_iarray関数によって返されたbool値がtrueであれば「OK」、
そうでなければ「NG」をターミナルに表示し、さらに引数*msgの内容も表示している。

41行目~51行目はmain関数である。
まずあらかじめ配列a~dが用意されている。iarray_reverse関数によって配列aを逆転させ、
それと配列bをexpext_iarray関数内のcompare_iarray関数で比較している。比較の結果を
expect_iarray関数で表示している。配列c,dについても同様である。
もしも今回組んだプログラムが正常に機能しているなら、配列aを逆転させたものと
配列bは一致しているので「OK」、配列cを逆転させたものと配列dは一
致していないので「NG」と表示されることが想定される。

【実行結果】

OK: 85241 -> 14258
NG: 0120 -> 0120

【考察】

expect_iarray関数で表示するmsgに何を入力すればいいのか、悩んだ。
つまり、正しい結果を表示する際は例えば1123→3211とすればよいのだ
ろうが、間違った結果を表示する場合、

1123→(正しく反転させた場合の配列)
とするべきか、

1123→(反転後の配列と一致していない、想定された配列)
とするべきかで悩んだ。

If it was flipped correctly, it should have been "", but the
result of executing the program was "".
とでも表示させるのが良いのだろうが、テストケースのためのプログラ
ムは変更できないので、今回は

1123→(反転後の配列と一致していない、想定された配列)
と表示させた。

【プログラム2(2つの配列を受け取り、2番目の各要素の値を1番目
の配列の各要素に足し込む)】

1  #include <stdio.h>
2  #include <stdbool.h>
3
4  void iarray_read(int *a, int n) {
5    for(int i=0; i<n; ++i) {
6      printf("%d> ", i+1);
7      scanf("%d", a+i);
8    }
9  }
10 
11 void iarray_print(int *a, int n) {
12   for(int i=0; i<n; ++i) {
13     printf(" %2d", a[i]);
14   }
15   printf("\n");
16 }
17
18 void iarray_add(int *a, int *b, int n) {
19   int c[n-1];
20   for(int i=0; i<n; ++i) {
21     c[i] = a[i] + b[i];
22   }
23   for(int i=0; i<n; ++i) {
24     a[i] = c[i];
25   }
26 }
27 
28 bool compare_iarray(int *a, int *b, int n) {
29   for(int i=0; i<n; ++i) {
30     if (a[i] != b[i]){
31     return false;
32     }
33   }
34   return true;
35 }
36
37 void expect_iarray(int *a, int *b, int n, char *msg) {
38   printf("%s: %s\n", compare_iarray(a, b, n)? "OK":"NG", msg);
39 }
40
41 int main(void) {
42   int a[] = {8,5,2,4,1};
43   int b[] = {1,1,2,2,3};
44   int c[] = {9,6,4,6,4};
45   int d[] = {1,1,2,0,9};
46   int e[] = {2,6,0,9,7};
47   int f[] = {2,4,3,5,6};
48   iarray_add(a, b, 5);
49   expect_iarray(a, c, 5, "85241+11223 -> 96464");
50   iarray_add(d, f, 5);
51   expect_iarray(d, f, 5, "11209+26097 -> 24356");
52   return 0;
53 }

【説明】

1行目では、stdio.hをインクルードしている。

2行目では、stdbool.hをインクルードしている。

4行目~16行目はプログラム1と全く同じであるので説明を割愛する。

18行目~26行目では、iarray_add関数を定義している。
空の配列cを用意し、配列aと配列bのi番目の数字の合計を配列cのi番目に格納している。
配列cへの格納が全て終わったら、配列aの内容を配列cの内容に一致さ
せることで、2つの配列を受け取り、
2番目の各要素の値を1番目の配列の各要素に足し込んだ配列を生成している。

28行目~39行目はプログラム1と全く同じであるので説明を割愛する。

41行目から53行目はmain関数である。
まずあらかじめ配列a~fが用意されている。iarray_add関数によって配
列aと配列bの各要素を加え、それと配列cをexpext_iarray関数内の
compare_iarray関数で比較している。比較の結果をexpect_iarray関数
で表示している。配列d,e,fについても同様である。もしも今回組んだ
プログラムが正常に機能しているなら、配列aと配列bの各要素を加えた
配列と配列cは一致しているので「OK」、配列dと配列eの各要素を加え
た配列と配列fは一致していないので「NG」と表示されることが想定さ
れる。

【実行結果】

OK: 85241+11223 -> 96464
NG: 11209+26097 -> 24356

【考察】

このプログラムは同じ長さの配列どうしで計算を行うことが前提となっ
ているが、もしも違う長さの配列で計算を行ったらどうなるのか、疑問
に思った。そこで、実際に行ってみた。main関数内の配列aの要素数を
4つにしてコンパイルしたところ、意外なことにエラーにならなかった。
そこでプログラムを実行したところ、次のような結果になった。

[y1910699@sol 02]$ ./a.out
NG: 5241+11223 -> 96464
NG: 11209+26097 -> 24356

このように、両方とも正しく「NG」が表示された。そこで今度は、配列
cを「5241+11223」の正しい解である16464にして再びプログラムを実行
した。

NG: 5241+11223 -> 16464
NG: 11209+26097 -> 24356

この結果から、このプログラムは「5241+11223」の計算は行っていない
ことが分かった。そこで今度は、配列aの5つ目の要素として0が代入さ
れているのではないかと考えた。つまり、「52410+11223」の計算を行っ
ているのではないかと考え、配列cをその計算の正しい解である63633に
してプログラムを実行した。

OK: 52410+11223 -> 63633
NG: 11209+26097 -> 24356

驚くことに、「OK」が表示された。これらの結果から、C言語では、要
素数がn個しかない配列のn+1目以降の要素を使用しようとすると、その
要素を0として扱うのではないか、と考察した。

【アンケート】

Q1:アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。
 →引数として使用するのが配列全体*aなのか、その中の要素の一つa
であるのかを区別することが大切だと思います。

Q2:ここまでのところで、プログラムを作るときに重要だが自分で身に
付いていないと思うことは何ですか。
 →「ポインタのポインタ」という考え方がまだしっくりときません。

Q3:リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
 →次回も頑張ります。

y1910705 2b:○ Mon May 25 01:13:15 2020



[課題1]
演習1b:配列の並び順を逆順にする関数

~作成したプログラム~
#include <stdio.h>
#include <stdbool.h>
void iarray_reverse(int *a, int n){
  int m;
  for(int i = 0; i < n / 2; i++){
     m = a[i];
     a[i] = a[n-i-1];
     a[n-i-1] = m;
  }
}

void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}

int main(void){
  int a[] = {8,5,2,4,1}, b[] = {1,4,2,5,8};
  int c[] = {1,3,5,4}, d[] = {4,5,3,1};
  int e[] ={9}, f[] = {9};
  int g[] ={0,1,2,0,9,5,9} ,h[] = {9,5,9,0,2,1,0}; 
  iarray_reverse(a, 5); expect_iarray(a, b, 5, "85241 -> 14258");
  iarray_reverse(c, 4); expect_iarray(c, d, 4, "1354 -> 4531");
  iarray_reverse(e, 1); expect_iarray(e, f, 1, "9 -> 9");
  iarray_reverse(g, 7); expect_iarray(g, h, 7, "0120959 -> 9590210");
  return 0;
}

~実行例~
OK 85241 -> 14258
  1  4  2  5  8
  1  4  2  5  8
OK 1354 -> 4531
  4  5  3  1
  4  5  3  1
NG 9 -> 9
  4
  9
OK 0120959 -> 9590210
  9  5  9  0  2  1  0
  9  5  9  0  2  1  0
[y1910705@sol c2]$ gcc8 1b.c
[y1910705@sol c2]$ ./a.out
OK 85241 -> 14258
  1  4  2  5  8
  1  4  2  5  8
OK 1354 -> 4531
  4  5  3  1
  4  5  3  1
OK 9 -> 9
  9
  9
OK 0120959 -> 9590210
  9  5  9  0  2  1  0
  9  5  9  0  2  1  0

~プログラムの説明~
void iarray_reverse(int *a, int n)では、配列aと配列の長さnを受け
取り、受け取った配列の並び順を逆順にする。for文を用いて、配列の
長さが偶数の場合はn/2まで、奇数の場合はn-1/2までiを1ずつ増やして
いき、配列を逆順にする。i番目の値をmに保存し、i番目にn-i-1番目の
値を入れる。そしてその逆を行う作業をループで行った。main関数では
テストケースとして二つの配列を使って、プログラムによる値と元の配
列、逆順の配列の値が一致しているかを判定するようにした。

~考察~
プログラムを作成する際、配列の長さが奇数か偶数かで場合分けをする
ことを考えたが、配列の長さnはint型であるため、場合分けをしなくて
も奇数の時は余りとしてなくなるため、if文を使用しないで作成した。
実際、テストケースでは配列の長さが奇数、偶数関係なく正常に作動し
ていることが分かった。また、テストケースではプログラムの動作を調
べるためのパターンをシンプルに扱えばよいと分かった。

[課題2]
演習2a:2つの列を受け取り、両方の列の内容を交互に並べた列を返す

~作成したプログラム~

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>  
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}

int *kogo(int *a, int *b){
  int *c = ivec_new(a[0]+b[0]);
  if(a[0]>= b[0]){
    for(int i = 1; i <=a[0]; ++i){
     c[2*i-1]=a[i];
     if(b[0]< i){
       c[2*i]=0;
       ++c[0];
     }else{
       c[2*i]=b[i];
     }
    }
  }else{
    for(int j = 1; j <=b[0]; ++j){
      if(a[0]< j){
	c[2*j-1]=0;
	++c[0];
      }else{
	c[2*j-1]=a[j];
      }
      c[2*j]=b[j];
    }
  }
  return c;
}

bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
  int a[] = {3,1,2,3}, b[] = {3,4,5,6}, c[] = {6,1,4,2,5,3,6};
  int *p = kogo(a, b);
  expect_iarray(p, c, 7, "[1,2,3],[4,5,6]=>[1,4,2,5,3,6]");
  int d[] = {4,2,5,3,1}, e[] = {3,7,5,6}, f[] = {8,2,7,5,5,3,6,1,0};
  int *q = kogo(d,e);
  expect_iarray(q, f, 9, "[2,5,3,1],[7,5,6]=>[2,7,5,5,3,6,1,0]");
  int g[] = {2,1,5}, h[] = {3,4,3,4}, i[] = {6,1,4,5,3,0,4};
  int *r =kogo(g,h);
  expect_iarray(r, i, 7, "[1,5],[4,3,4]=>[1,4,5,3,0,4]");
  int j[] ={1,1}, k[]={0}, l[]={2,1,0};
  int *s =kogo(j,k);
  expect_iarray(s, l, 3, "[1],[]=>[1,0]");
  return 0;
}

~実行例~
OK [1,2,3],[4,5,6]=>[1,4,2,5,3,6]
  6  1  4  2  5  3  6
  6  1  4  2  5  3  6
OK [2,5,3,1],[7,5,6]=>[2,7,5,5,3,6,1,0]
  8  2  7  5  5  3  6  1  0
  8  2  7  5  5  3  6  1  0
OK [1,5],[4,3,4]=>[1,4,5,3,0,4]
  6  1  4  5  3  0  4
  6  1  4  5  3  0  4
OK [1],[]=>[1,0]
  2  1  0
  2  1  0

~プログラムの説明~

int*kogoでは、配列a,bを受け取り、両方の列の内容を交互に並べた配
列を返すプログラムとなっている。今回の場合、長さが異なる場合は0
を入れるようにした。配列の長さがa>bの時は、[・・・n, 0]、a<bの時
は、[・・・0, m]となる。最初に返す配列のサイズを決めるが、0を入
れることによりサイズが変わるため、後で調整する。その後if文により
二つの配列の長さで場合分けをする。次にfor文で返す配列に二つの配
列を交互に入れていく。この際、配列の長さが短いほうに配列の最後ま
で行ったら0を入れるような条件を加える。この際、返す配列の長さを1
増やすようにしてある。このようにして交互に要素を入れていく配列を
作った。
テストケースにおいては二つの配列の長さの違いでパターンを作った。

~考察~
両方の配列の要素を交互に入れていくループは簡単にできたが、長さが
異なる場合の扱いに関しては少し難しかった。今回は0を入れるだけで
あったが、左に詰めるなどの操作はどのようにやるのかわからなかった。
また、テストケースをして初めて0を入れたことによる返す配列の要素
数が変化していることに気づけたため、テストケースの重要さがよく分
かった。

[アンケート]
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。
値を直接指定しているわけではないというところ。
Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に
付いていないと思うことは何ですか。
どこから手を付けていいかわからないところ。
Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
次回の課題も頑張りたい。

y1910707 2b:○ Thu May 21 16:10:14 2020



演習2.a
// ivec_demo.c --- int vector demonstration.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 1; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void ivec_read(int *a) { iarray_read(a+1, a[0]); }
void ivec_print(int *a) { iarray_print(a+1, a[0]); }
int *ivec_concat(int *a, int *b) {
  int *c = ivec_new(a[0]+b[0]);
  for(int i = 1; i*2 <= c[0]; ++i) {
    c[i*2] = a[i];
    c[i*2-1] = b[i];
  }
  return c;
}

void expect_iarray(int *a, int *b){
  printf("%s\n", (iarray_equal(a, b, a[0]))?"OK":"NG");
  ivec_print(a); ivec_print(b);
}

int main(void) {
  int *a, *b, *c;
  a = ivec_new(3); ivec_read(a);
  b = ivec_new(3); ivec_read(b);
  free(a); free(b); free(c);
  c = ivec_concat(a, b);
  ivec_print(c);
  // int a[] = {5, 0, 8, 100, 7, 5}, b[] = {5, 3, 4, 5, 6, 8}, c[] = {10, 0, 3, 8, 4, 100, 5, 7, 6, 5, 8};
  // int *p = ivec_concat(b, a);
  // expect_iarray(p, c);
  // free(p);
  return 0;
}

単体テスト
  OK
   0  3  8  4 100  5  7  6  5  8
   0  3  8  4 100  5  7  6  5  8

実行例
  1> 2
  2> 8
  3> 4
  1> 0
  2> 14
  3> 7
    0  2 14  8  7  4

説明
  要素の編み込みに関してはivec_concat内で行っている. 内容として
  は, 配列cのサイズをa+bとし, 偶数番目にaの要素を, 奇数番目にbの
  要素を代入している. なおコメントの部分は単体テストとなっている.
  

考察
 プログラムそのものとしては分かりやすく十分なものだろう. for文内
 の[]内を変えることで交互以外の並びも作れそうだ.
 実際は, 数列を作るというよりはbに仕切りとなる要素を入れ, 配列の
 整理に使えそうである.

演習2.c
// ivec_demo.c --- int vector demonstration.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 1; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void ivec_read(int *a) { iarray_read(a+1, a[0]); }
void ivec_print(int *a) { iarray_print(a+1, a[0]); }
int *sortup(int *a) {
  int c;
  for(int i=1; i <= a[0]; ++i) {
    for(int j=i+1; j <= a[0]; ++j){
    if (a[i] > a[j]){
      c = a[i];
      a[i] = a[j];
      a[j] = c;
    }
   }
  }
  return a;
}

void expect_iarray(int *a, int *b, char *msg){
  printf("%s %s\n", (iarray_equal(a, b, a[0]))?"OK":"NG", msg);
  ivec_print(a, a[0]); ivec_print(b, b[0]);
}
int main(void) {
  int *a;
  a = ivec_new(3); ivec_read(a); ivec_print(a); sortup(a); ivec_print(a);
  free(a);
  // int a[] = {7, 0, 8, 100, 7, 5, 90, 5}, b[] = {7, 0, 5, 5, 7, 8, 90, 100};
  // sortup(a);
  // expect_iarray(a, b, "0, 8, 100, 7, 5, 90, 5 -> 0, 5, 5, 7, 8, 90, 100");
  return 0;
}

単体テスト
  OK 0, 8, 100, 7, 5, 90, 5 -> 0, 5, 5, 7, 8, 90, 100
    0  5  5  7  8 90 100
    0  5  5  7  8 90 100

実行例
  1> 3
  2> 7
  3> 13
    3  7 13
    3  7 13

説明
 sortup()内でi番目がj番目より大きければ入れ替えるという処理を繰
 り返している. これにより最終的には昇順にソートされるというわけ
 である. なおj=i+1と置くことそれ以前の処理を行わないようにしてい
 る.

考察
 for文を二重で使っているが, プログラムとしては分かりやすいのでこ
 れで良いだろう.本来はこういった関数は外部から呼び出して使うもの
 だが, 配列の整理そのものはよく使うので, こうしたプログラムは自
 分で作れたほうが良いだろう.

演習2.d
// ivec_demo.c --- int vector demonstration.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 1; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void ivec_read(int *a) { iarray_read(a+1, a[0]); }
void ivec_print(int *a) { iarray_print(a+1, a[0]); }
int *sortup(int *a) {
  int c;
  for(int i=1; i <= a[0]; ++i) {
    for(int j=i+1; j <= a[0]; ++j){
    if (a[i] > a[j]){
      c = a[i];
      a[i] = a[j];
      a[j] = c;
    }
   }
  }
  return a;
}

int *ivec_concat(int *a, int *b) {
int *c = ivec_new(a[0]+b[0]);
for(int i = 1; i <= a[0]; ++i) { c[i] = a[i]; }
for(int i = 1; i <= b[0]; ++i) { c[i + a[0]] = b[i]; }
return c;
}

void expect_iarray(int *a, int *b, char *msg){
  printf("%s %s\n", (iarray_equal(a, b, a[0]))?"OK":"NG", msg);
  ivec_print(a); ivec_print(b);
}
int main(void) {
  int *a, *b, *c;
  a = ivec_new(3); ivec_read(a); sortup(a); ivec_print(a);
  b = ivec_new(3); ivec_read(b); sortup(b); ivec_print(b);
  c = ivec_concat(a, b); ivec_print(c);
  sortup(c); ivec_print(c);
  free(a); free(b); free(c);
  // int a[] = {3, 5, 5, 2}, b[] = {3, 3, 0, 14}, c[] = {6, 0, 2, 3, 5, 5, 14};
  // sortup(a); sortup(b);
  // int *p = ivec_concat(a, b);
  // ivec_print(p);
  // sortup(p);
  // expect_iarray(p, c, "2, 5, 5, 0, 3, 14 -> 0, 2, 3, 5, 5, 14");
  // free(p);
  return 0;
}


単体テスト
  2  5  5  0  3 14
  OK 2, 5, 5, 0, 3, 14 -> 0, 2, 3, 5, 5, 14
  0  2  3  5  5 14
  0  2  3  5  5 14

実行例
  1> 4
  2> 2
  3> 8
  2  4  8
  1> 9
  2> 2
  3> 8
  2  8  9
  2  4  8  2  8  9
  2  2  4  8  8  9

説明
 演習2.cおよび例題を組み合わせている. a, bを演習2.c同様に並び替
 えた後, ivec_concatでこれらを連結し, それから再びsortupを実行し
 ている.

考察
 ソートの方法として, 今回はソート→連結→ソートを取ったが, a, b
 をそれぞれソートした後, これらを昇順に連結することもできただろ
 う.
ただしその場合, 配列の数が増えた場合にだいぶ余計な手間がかかって
 しまうため, やはり今回の順番で処理を進めるのが最善だろう.

アンケート
Q.1 これらを使うメリットを常に考えるべきである.
Q.2 エラーを処理する能力.
Q.3 今後ともポインタの利用は考えていきたい.

y1910708 2b:○ Sat May 23 11:38:21 2020


1910708 個人作業 2020/5/19

1-b
int iarray_reverse(int *a, int n){
    int c[5];
    for(int i = 0; i <= n - 1; i++){
        c[i] = a[n-i-1];
    }
    for(int i = 0; i <= n - 1; i++){
      a[i] = c[i];
    }
}

bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) {
      return false;
      }
  }
  return true;
}

void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n);
  iarray_print(b, n);
}

void expect_int(int i1, int i2, char *msg) {
  printf("%s %d:%d %s\n", (i1==i2)?"OK":"NG", i1, i2, msg);
}

int main(void) {
  int a[] = {8,5,2,4,1}, b[] = {1,4,2,5,8};
  int a2[] = {5,4,3,2,1}, b2[] = {1,2,3,4,5};
  iarray_reverse(a, 5);
  iarray_reverse(a2, 5);
  expect_iarray(a, b, 5, "85241 -> 14258");
  expect_iarray(a2, b2, 5, "54321 -> 12345");
  return 0;
}

配列の並びを逆順にするコード
iarray_reverseでは一度空の配列c[]を作り、そこに
配列a[]を後ろから当てはめていき、最後にa[]にc[]全体を
代入した。


単体テストと実行例
C:\Users\81909\Documents\Code\2020 演習2>a.exe
OK 85241 -> 14258
  1  4  2  5  8
  1  4  2  5  8
OK 54321 -> 12345
  1  2  3  4  5
  1  2  3  4  5

初めは置き換えずに配列c[]を出力しようと試みた。
prinfではc[0],c[1]...の値は正しく出力されたが、
テストではそれとは異なる値で判定されていたため、
置き換える形をとった。また、for文が無限ループになってしまうこともあった。
このコードを改めてみると、わざわざ空の配列c[]を用いずとも
書けるように思われる。また、1度置きかえるためfor文を
2回使っていることで見栄えも悪くなっているように感じる。

1-c
int iarray_sort(int *a, int n){
    int s;
    for(int i = 0; i < n ; i++){
        for(int i2 = i + 1; i2 < n ; i2++){
            if(a[i] > a[i2]){
                s = a[i];
                a[i] = a[i2];
                a[i2] = s;
            }
        }
    }
}

void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n);
  iarray_print(b, n);
}

void expect_int(int i1, int i2, char *msg) {
  printf("%s %d:%d %s\n", (i1==i2)?"OK":"NG", i1, i2, msg);
}

int main(void) {
  int a[] = {8,5,2,4,1}, b[] = {1,2,4,5,8}; 
  int a2[] = {6,4,8,2,1}, b2[] = {1,2,4,6,8}; 
  iarray_sort(a, 5); 
  iarray_sort(a2, 5); 
  expect_iarray(a, b, 5, "85241 -> 12458");
  expect_iarray(a2, b2, 5, "64821 -> 12468");
  return 0;
}

配列を昇順に並べ替えるコード
iarray_sortでfor文を2回用いて、2つの値を比べて
より小さい値を左に持ってくるという形で昇順に並べ替えた。

単体テストと実行例
C:\Users\81909\Documents\Code\2020 演習2>a.exe
OK 85241 -> 12458
  1  2  4  5  8
  1  2  4  5  8
OK 64821 -> 12468
  1  2  4  6  8
  1  2  4  6  8

このコードは比較的簡潔に書くことが出来たと思われる。
しかし、単体テストで
  1  2  4  5  8
  1  2  4  5  8 のように正しいはずなのにNGが出る場合があったが、
コードを詳しく見ると、
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {     
のi < n部分が修正前のi<=nだったというミスであった。

1-d
void iarray_add(int *a, int *b, int n){
    for(int i = 0; i < n ; i++){
        a[i] = a[i] + b[i];
    }
}

void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n);
  iarray_print(b, n);
}

void expect_int(int i1, int i2, char *msg) {
  printf("%s %d:%d %s\n", (i1==i2)?"OK":"NG", i1, i2, msg);
}

int main(void) {
  int a[] = {8,5,2,4,1}, b[] = {1,1,2,2,3}, c[] = {9,6,4,6,4};
  int a2[] = {1,2,3,4,5}, b2[] = {1,1,1,1,1}, c2[] = {2,3,4,5,6};
  iarray_add(a, b, 5); 
  iarray_add(a2, b2, 5);
  expect_iarray(a, c, 5, "85241+11223 -> 96464");
  expect_iarray(a2, c2, 5, "12345+11111 -> 23456");
}

2つの配列を受け取り、それらを足して1つの配列にするコード
iarray_addではfor文で配列のそれぞれの要素において
a[i]をa[i]+b[i]の値で更新した。

単体テストと実行例
C:\Users\81909\Documents\Code\2020 演習2>a.exe
OK 85241+11223 -> 96464
  9  6  4  6  4
  9  6  4  6  4
OK 12345+11111 -> 23456
  2  3  4  5  6
  2  3  4  5  6

このコードについても比較的簡潔に書くことが出来たと思われる。
iarray_addについては1年次にも同じようなコードを書いたことや
1-a,1-bを通して、アドレス・ポインタに慣れたことが要因に思われる。

アンケート
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いま
すか。
配列の数はn個でも、配列の最後の値はn-1番目であること

Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に付いて
いないと思うことは何ですか。
なんでもかんでも色々な変数を宣言してしまい、自分でも分からなくなってしまう。

Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
次回も頑張りたい。

y1910709 2b:○ Fri May 22 19:55:39 2020


学籍番号:1910709
@@氏名:吉田昂太
個人作業
提出日時:5/22

<1つ目の課題(演習1b)>
課題の再掲:
配列の並び順を逆順にする関数void iarray revese(int *a, int n)
を作成する。

プログラムのソース:
#include <stdio.h>
#include <stdbool.h>

void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

int iarray_reverse(int *a,int n) {
   int k;
 for(int i = 0; i < n/2; ++i) {
   k = a[i];
   a[i] = a[n-i-1];
   a[n-i-1] = k;
  }
}

char *judg(int *b1, int *b2,int n) {
   for(int i=0;i<n;i++) {
     if(b1[i] != b2[i]) {return "NG";}
   }
   return "OK";
}

void expect_iarray(int *b1, int *b2,int n, char *msg) {
  printf("%s :%s\n", judg(b1,b2,n), msg);
  iarray_print(b1,n);
  iarray_print(b2,n);
}
int main(void) {
  int a[] = {8,5,2,4,1}, b[] = {1,4,2,5,8};
  int c[] = {6,3,2,8,2,1,9}, d[] = {9,1,2,8,2,3,6};
  int e[] = {1},  f[] = {1};
  iarray_reverse(a, 5); expect_iarray(a, b, 5, "85241 -> 14258");
  iarray_reverse(c, 7); expect_iarray(c, d, 7, "6328219 -> 9128236");
  iarray_reverse(e, 1); expect_iarray(e, f, 1, "1 -> 1");
  return 0;
}

プログラムの実行結果:
[y1910709@sol ~/pro]$ ./a.out
OK :85241 -> 14258
  1  4  2  5  8
  1  4  2  5  8
OK :6328219 -> 9128236
  9  1  2  8  2  3  6
  9  1  2  8  2  3  6
OK :1 -> 1
  1
  1

説明:

最初に定義した配列a,c,eを関数iarray_reverse()に入れる。一つ目の
引数には配列を、二つ目の引数には要素数を渡す。iarray_reverse()で
はfor文を用いてn/2回配列のi番目とn-i-1番目を交換する。そうするこ
とで配列を逆順にできる。逆順にした配列をそれぞれ配列b,d,fと比較
する関数expect_iarray()に渡す。expect_iarray()内で渡した2つの配
列が等しいかを確認する関数judg()を呼び、等しければ文字列OK、異な
ればNGを返す。返ってきた文字列をprintfで表示する。今回は3つのテ
ストケースを実行している。

考察:

テストケースを実行することでバグを発見することは、プログラムが複
雑になるほど重要になってくると感じた。テストケースでは同じような
配列を渡すのではなく様々な状況を考慮して設定したほうがいいだろう。

<2つ目の課題(演習2c)>
課題の再掲:
1つの列を受け取り、その内容を昇順に整列した列を返す。

プログラムのソース:
#include <stdio.h>
#include <stdbool.h>

void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

int iarray_sort(int *a,int n) {
   int top;
 for(int i = 0; i < n; ++i) {
  for(int j=i+1; j < n; j++ ) {
     if(a[i] > a[j]){
       top=a[i];
       a[i]=a[j];
       a[j]=top;     
     }    
  }
 }
}

char *judg(int *b1, int *b2,int n) {
   for(int i=0;i<n;i++) {
     if(b1[i] != b2[i]) {return "NG";}
   }
   return "OK";
}

void expect_iarray(int *b1, int *b2,int n, char *msg) {
  printf("%s :%s\n", judg(b1,b2,n), msg);
  iarray_print(b1,n);
  iarray_print(b2,n);
}
int main(void) {
  int a1[] = {3,4,1,2},a2[] = {1,2,3,4}; 
  int b1[] = {3,4,7,1,8},b2[] = {1,3,4,7,8};
  int c1[] = {6,3,2,4,7,1,5},c2[] = {1,2,3,4,5,6,7};
  iarray_sort(a1, 4); expect_iarray(a1,a2, 4, "3412 -> 1234");
  iarray_sort(b1, 5); expect_iarray(b1,b2, 5, "34718 -> 13478");
  iarray_sort(c1, 7); expect_iarray(c1,c2, 7, "6324715 -> 1234567 ");
  return 0;
}

プログラムの実行結果:
[y1910709@sol ~/pro]$ ./a.out
OK :3412 -> 1234
  1  2  3  4
  1  2  3  4
OK :34718 -> 13478
  1  3  4  7  8
  1  3  4  7  8
OK :6324715 -> 1234567
  1  2  3  4  5  6  7
  1  2  3  4  5  6  7

説明:

最初に設定した配列a1,b1,c1を関数iarray_sort()に渡し、昇順に並べ
替える。iarray_sort()ではバブルソートで配列を昇順に並べ替える。i
番目の要素を他の要素と比較しi番目の方が大きかったら交換する。並
べ替えた配列を配列a2,b2,c2と比較する関数expect_iarray()に入れる。
expect_iarray()では受け取った2つの配列を比較する関数judg()を呼び
2つの配列をfor文を使って比較する。等しければ文字列OK,異なればNG
を返す。返ってきた文字列をprintfで表示する。今回はテストケースを
3つ実行している。


考察:

配列を昇順にするプログラムのアルゴリズムは1年のころに学んでいた
ので割と簡単に書けた。自力で思いつくのは難しそうだと感じた。単体
テストを実行するプログラムは一度つくってしまえば使い回せるので便
利だと思った。

アンケート:
Q1 左辺値として使用する場合と右辺値で使用する場合で異なることを意識すること
Q2 プログラムの実行速度を考えず、自由にコードを書いている所
Q3 正常に動くか不安なプログラムを作るときは、積極的にテストケー
スと実行しようと思う。

このファイルUTF8で出せているでしょうか? 変え方がよくわかりません

y1910714 2b:○ Mon May 25 23:08:04 2020


個人作業
提出日時:5月25日23時頃


1b:入力した配列を逆順にする
【ソースコード】
#include <stdio.h>
#include <stdbool.h>

bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
void iarray_reverse(int *a, int n) {
  for (int i = 0; i < n/2; i++) {
    int b = a[i]; a[i] = a[n-1-i]; a[n-1-i] = b;
  }
}
int main(void) {
  int a[] = {8,5,2,4,1}, b[] = {1,4,2,5,8}, c[] = {3,5,6,4}, d[] = {4,6,5,3};
  iarray_reverse(a, 5); expect_iarray(a, b, 5, "85241 -> 14258");
  iarray_reverse(c, 4); expect_iarray(c, d, 4, "3564 ->4653");
  return 0;
}
【単体テストの実行例】
$ ./a.out
OK 85241 -> 14258
  1  4  2  5  8
  1  4  2  5  8
OK 3564 ->4653
  4  6  5  3
  4  6  5  3
【説明】

問題の関数はiarray_reverseである。配列aとその長さnを受け取り、
a[i]とa[n-1-i]の値を交換すればよい。
交換は、これまで通り、媒介となる変数を導入して行った。また、iは0
以上n/2未満にした。そうでないと2度交換される変数がでてきてしまう
からである。
単体テストには、テキストのもの以外に{3,5,6,4}の場合を加えた。長
さが偶数のときでも問題ないことを確認するためである。

【考察など】
上に述べたように、配列の逆順(さらには交換swap)は既にやっている。
新しいのは単体テストの導入である。
expect_iarrayでprintすることがらはテスト結果として必要なもので、
1.操作の内容、2.「実行結果」と「想定される結果」、および3.それら
が一致しているか
を示している。実際、ここには掲載していないが、単体テストでNGが出
たが、実行結果が想定とどう違うか一目でわかりすぐにバグを取り除け
た。

1d:配列の足し算
【ソースコード】
(iarray_equal, expect_iarray)
void iarray_add(int *a, int *b, int n) {
  for(int i = 0; i < n; i++) {
    a[i] = a[i] + b[i]; }
}
int main(void) {
  int a[] = {8,5,2,4,1}, b[] = {1,1,2,2,3}, c[] = {9,6,4,6,4};
  int d[] = {5,3,7,0}, e[] = {-3,4,0,-1}, f[] = {2,7,7,-1};
  iarray_add(a, b, 5); expect_iarray(a, c, 5, "85241+11223 -> 96464");
  iarray_add(d, e, 4); expect_iarray(d, f, 4, "5370+-340-1 -> 277-1");
  return 0;
}
【単体テストの実行例】
$ ./a.out
OK 85241+11223 -> 96464
  9  6  4  6  4
  9  6  4  6  4
OK 5370+-340-1 -> 277-1
  2  7  7 -1
  2  7  7 -1
【説明】
問題文に「2番目の各要素を、1番目の(対応する)各要素に足し込む」と
あるから、文字通りa[i]=a[i]+b[i]とした。
単体テストには、{5,3,7,0}と{-3,4,0,-1}の場合を加えた。これは減算、
プラス0および結果が負の値となるときでも問題ないことを確認するた
めである。
【考察など】
配列の足し算は既習だし、単体テストの工夫も上に述べた通りなので、
新しい事柄はない。
今回もテストのNGでバグに気付けたので、改めてその有用性を確認でき
たくらいである。

1e:配列の値の集約
【ソースコード】
#include <stdio.h>
#include <stdbool.h>

int iarray_inject(int *a, int n, int (*fp)(int, int)) {
  int s = a[0];
  for(int i = 1; i < n; i++) { s = fp(s, a[i]); }
  return s;
}
int iadd(int x, int y) { return x + y; } 
int imax(int x, int y) { return (x > y) ? x : y; }
void expect_int(int i1, int i2, char *msg) {
  printf("%s %d:%d %s\n", (i1==i2)?"OK":"NG", i1, i2, msg);
}
int main(void) {
  int a[] = {8,5,2,4,1}, b[] = {-6,-3,-9,-4};
  expect_int(iarray_inject(a, 5, iadd), 20, "8+5+2+4+1");
  expect_int(iarray_inject(a, 5, imax), 8, "max(8,5,2,4,1)");
  expect_int(iarray_inject(b, 4, iadd), -22, "-6-3-9-4");
  expect_int(iarray_inject(b, 4, imax), -3, "max(-6,-3,-9,-4)");
  return 0;
}
【単体テストの実行例】
$ ./a.out
OK 20:20 8+5+2+4+1
OK 8:8 max(8,5,2,4,1)
OK -22:-22 -6-3-9-4
OK -3:-3 max(-6,-3,-9,-4)
【説明】

問題の関数はiarray_injectである。配列とその長さ、さらに「関数の
ポインタ」を受け取り、集約した結果を返す。
今回はポインタにiadd(各項の総和を求める関数)とimax(最大値を求め
る関数)の2つが代入されることを想定して、次のようにした。
すなわち、あらかじめa[0]で初期化した変数sを用意して、カウンタiを
1以上n未満でインクリメントさせながら、sにfp(s,a[i])を代入させる
のである。
なぜこれでうまくいくのか。それは、総和も最大値もともに、それを表
す変数と初期値a[0]を必要とし、続く項a[i]によって変数の値が更新さ
れるからである。
単体テストには{-6,-3,-9,-4}の場合を加えた。配列の項が負の数のと
きも問題ないことを確かめるという目的である。

【考察など】
「関数のポインタ」という概念が新出である。基本的にはこれまでのポ
インタと同じだが、単独の関数名そのものもポインタであることに注意
が必要だった。
課題2aの感想で述べた「1つの関数内で複数の変数を扱う」ではないが、
この概念によって1つの関数内で複数の関数(今回ではiaddとimax)が扱
えるのは、場合によっては非常に有効である。
というのは、必要とする引数が共通しているとき威力が発揮されるからである。

2a:2つの列の内容を交互に並べる
【ソースコード】
#include <stdio.h>
#include <stdbool.h>

void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}
void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}
int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}
void ivec_read(int *a) { iarray_read(a+1, a[0]); }
void ivec_print(int *a) { iarray_print(a+1, a[0]); }
int *ivec_alt(int *a, int *b) {
  int *c = ivec_new(a[0]+b[0]);
  int m = (a[0] < b[0])?a[0]:b[0];
  for(int i = 1; i <= m; i++) {
    c[2*i-1] = a[i]; c[2*i] = b[i];
  }
  if(m == a[0]) {
    for(int j = 1; j <= b[0]-m; j++) {
      c[2*m+j] = b[m+j];
    }
  }
  else {
    for(int j = 1; j <= a[0]-m; j++) {
      c[2*m+j] = a[m+j];
    }
  }
  return c;
}
bool iarray_equal(int *a, int *b, int n) {
  for(int i = 0; i < n; ++i) {
    if(a[i] != b[i]) { return false; }
  }
  return true;
}
void expect_iarray(int *a, int *b, int n, char *msg) {
  printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
  iarray_print(a, n); iarray_print(b, n);
}
int main(void) {
  int a[] = {3,1,2,3}, b[] = {3,4,5,6}, c[] = {6,1,4,2,5,3,6};
  int d[] = {2,1,2}, e[] = {4,1,2,3,4}, f[] = {6,1,1,2,2,3,4};
  int *p = ivec_alt(a, b);
  expect_iarray(p, c, 7, "[1,2,3]+[4,5,6]=[1,4,2,5,3,6]");
  int *q = ivec_alt(d, e);
  expect_iarray(q, f, 7, "[1,2]+[1,2,3,4]=[1,1,2,2,3,4]");
  return 0;
}
【単体テストの実行例】
$ ./a.out
OK [1,2,3]+[4,5,6]=[1,4,2,5,3,6]
  6  1  4  2  5  3  6
  6  1  4  2  5  3  6
OK [1,2]+[1,2,3,4]=[1,1,2,2,3,4]
  6  1  1  2  2  3  4
  6  1  1  2  2  3  4
【説明】

問題の関数はivec_altである。はじめに、2つの「可変長配列」a,bを受
け取る。まず、長さa[0]+b[0]の「可変長配列」cを用意する。
次に、cにaとbの内容を交互に代入していくのだが、aとbの長さは等し
いとは限らないので、以下のようにする。
すなわち、小さいほうの長さをmとして、c[2*i-1]=a[i],c[2*i]=b[i]を
カウンタiが1以上m以下の範囲で行う。
そして、例えばm=a[0]なら、c[2*m+j]=b[m+j]をjが1以上b[0]-m以下の
範囲で行い、残りをcの末尾に付け足す。
単体テストには、長さの異なる配列d,eを加えた。長いほうの残りがそ
のまま、並べた列の後ろに続くのを確かめるためである。
【考察など】
「可変長配列」は、初項が長さを表すので、実質的な項がa[1]から始ま
るのが特徴である。これは高校数学の数列に似ている。
大学では(本学がそうなだけかもしれないが)数列の初項はa[0]であるか
ら、配列とともに注意しなければならなかったので、懐かしかった。
大学入試なんかで、偶数項や奇数項を問題にしたときの感覚で関数を書
けたのは新鮮だった。

【アンケート】(以下、Q1の回答をA1などと記す)
A1.ポインタとそうでないものとの区別をプログラム内ではっきりさせ
ること。ポインタの定義、ポインタにアドレスを代入、など注意しなけ
らばならない場面がいくつもある。
A2.簡潔に書くこと。クラスメイトのプログラムを参考に、勉強し続けたいです。
A3.次回はスタック。アルゴリズムの基本だそうなので真剣に取り組み
ます。

y1910715 2b:△ Sun May 24 19:00:31 2020


学籍番号:1910715
「個人作業」

一つ目の課題の再掲:課題1-d

長さが等しい二つの配列を受け取り、一つ目の配列の各要素に対して二
つ目の配列の各要素を足す関数を作成する。


作成したプログラム:
//iarrayadd.c --- add secand array value to first one.

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>

void iarray_add(int *a, int *b, int n){
    for(int i= 0; i < n; ++i){
        *(a+i) += *(b+i);
    }
}

void iarray_print(int *a, int n){
    for(int i = 0; i < n; ++i){ printf(" %2d", a[i]); }
    printf("\n");
}

bool iarray_equal(int *a, int *b, int n) {
    for(int i = 0; i < n; ++i) {
        if(a[i] != b[i]) { return false; }
    }
    return true;
}

void expect_iarray(int *a, int *b, int n, char *msg) {
    printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
    iarray_print(a, n); iarray_print(b, n);
}

int main(void){
    int a[] = {8,5,2,4,1}, b[] = {1,1,2,2,3}, c[] = {9,6,4,6,4};
    iarray_add(a, b, 5);
    expect_iarray(a, c, 5, "85241+11223 -> 96464");
    int d[] = {124,0,-10,0,2,-2,-0}, e[] = {124,0,0,-5,-2,2,-0}, f[] = {248,0,-10,-5,0,0,0};
    iarray_add(d, e, 7);
    expect_iarray(d, f, 7, "248,0,-10,-5,0,0,0");
    return 0;
}

説明:
iarray_printとiarray_equal、expect_iarrayは教科書に記載されているものを使った。
iarray_addではfor文を使い、配列の長さ分繰り返しを行い、+=で一つ
目の配列に二つ目の配列の値を足している。
得られるのが配列のためvoid型で作っている。
main関数では教科書に記載のテストケースに加え、0+0、0からの引き算、
マイナスに0を足す、結果が0になる計算、'-0'+'-0'を行うテストケー
スを行った。

考察:
上記のテストケース以外にも一つ試したことがあり、それは配列の片一
方一つの要素がからだった場合はどのような計算結果になるのかという
ものだ。
実際には自作のテストケースを下記のようなコードにかえて行った。
(配列eの最後に何の数字も入れていない状態)

int d[] = {124,0,-10,0,2,-2,-0,0}, e[] = {124,0,0,-5,-2,2,-0,}, f[] = {248,0,-10,-5,0,0,0,0};
    printf("\n");iarray_print(e ,8);printf("\n");
    iarray_add(d, e, 8);
    expect_iarray(d, f, 8, "248,0,-10,-5,0,0,0,0");
そうすると、配列eそのものを表示させると、124  0  0 -5 -2  2  0 124 となり、
結果は
NG 248,0,-10,-5,0,0,0,0
 248  0 -10 -5  0  0  0 248
 248  0 -10 -5  0  0  0  0

となった。最後の計算は0 + 248 = 248を行っていることが、iarrayadd
にprintfを入れることでわかった。
このことから配列に数を入れない場合でも計算自体は行われることが分かった。
計算もf[0]の値を使って計算しているのかとも思ったため、f[0]の数値
を変えて行ったが、248で計算されていたため影響しているわけではな
いことが分かったがそれ以上のことはわからなかった。

二つ目の課題の再掲:課題2-a
二つの列を受け取り、両方の列の内容を交互に並べた列を返す関数をつくる。

作成したプログラム:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

int *ivec_new(int size) {
    int *a = (int*)malloc((size+1) * sizeof(int));
    a[0] = size; return a;
}

void iarray_read(int *a, int n) {
    for(int i = 0; i < n; ++i) {
        printf("%d> ", i+1); scanf("%d", a+i);
    }
}

void ivec_read(int *a) { iarray_read(a+1, a[0]); }

void iarray_print(int *a, int n){
    for(int i = 0; i < n; ++i){ printf(" %2d", a[i]); }
    printf("\n");
}

bool iarray_equal(int *a, int *b, int n) {
    for(int i = 0; i < n; ++i) {
        if(a[i] != b[i]) { return false; }
    }
    return true;
}

void expect_iarray(int *a, int *b, int n, char *msg) {
    printf("%s %s\n", iarray_equal(a, b, n)?"OK":"NG", msg);
    iarray_print(a, n); iarray_print(b, n);
}

void ivec_print(int *a) { iarray_print(a+1, a[0]); }

int *ivec_alt(int *a, int *b){
    int na = a[0], nb = b[0], n = a[0]+b[0];
    //printf("n=%d\n",n);
    int *c = ivec_new(n);
    for(int i = 1; i <= n; ++i){
        if(na == 0){
            c[i] = b[b[0]-nb+1];
            //printf("%d",c[i]);
            nb -= 1;
        }else if(nb == 0){
            c[i] = a[a[0]-na+1];
            //printf("%d",c[i]);
            na -= 1;
        }else{
            c[i] = a[a[0]-na+1];
            //printf("%d",c[i]);
            i += 1;
            c[i] = b[b[0]-nb+1];
            //printf("%d",c[i]);
            nb -=1; na -=1;
        }
    }
    //printf("\n");
    return c;
}

int main(void){
    int *a, *b, *c;
    a = ivec_new(3); ivec_read(a);
    b = ivec_new(5); ivec_read(b);
    c = ivec_alt(a ,b);
    printf("c=");ivec_print(c);
    free(a);free(b);free(c);
    int d[] = {5,1,2,3,4,5}, e[] ={3,6,7,8} , f[] = {8,1,6,2,7,3,8,4,5};
    int *g = ivec_alt(d ,e);
    expect_iarray(g, f, 9, "[1,2,3,4,5]+[6,7,8]=[1,6,2,7,3,8,4,5]");
    return 0;
}

説明:
*ivec_new、iarray_read、ivec_read、iarray_print、iarray_equal、
expect_iarray、ivec_printは教科書に記載されているものを使用した。
*ivec_altにおいて、最初に受けとった配列の長さをそれぞれ新たな変
数na, nbに入れ、最終的に返す配列cを作った。
na, nbはforループの中でそれぞれ配列a、配列bの値が1つ配列cに格納
されたら値が1減少している。
なのでnaもしくはnbが0になったら、もう片方の配列の残りの値を順次
入れていくようにif文で条件を作った。テストケースとしてはまず入力
された値に対しても動くこと、一つ目の配列の方がが長い場合、二つ目
の配列の方が長い場合を試してみた。また途中エラーがでたため、
printfでどの値から入れられているかと合成後の配列の長さを表示して
いた。

考察:
途中配列の最初に配列の長さを入れていることを忘れて作成していたた
め、実行自体はされるが値がおかしいことになってしまった。オリジナ
ルの配列やルールを作成したらそれ専用の関数を作らなければエラーが
出やすいし、エラーに気付きにくいと感じた。

アンケート:
Q1. アドレス、ポインタを使うプログラムで注意すべきことは何だと思いますか。
*や&を使うときにそれはアドレスそのものを示しているのかアドレスに
格納されている値を示しているのかを理解すること。
実際今回の課題をやっていてそれが原因で予想外の出力がされたことがあった。

Q2. ここまでのところで、プログラムを作るときに重要だが自分で身に
付いていないと思うことは何ですか。
エラーが出たときに直したことによってエラーが出ることがあるので、
直した部分だけでなくその周りもチェックする注意力。

Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
一つ目の課題で二回に分けてテストケースを行ったが、最初の配列と二
つ目の配列が長さが違ったため、同じ名前を使うにはmallocなどを使わ
なければならないのだと分かった。
最初はRubyのような感覚でやってしまったため何回やってもエラーが取
れず、最終的には名前を変えて実行した。

y1910716 2b:○ Sat May 23 23:37:30 2020


学籍番号 1910716
個人作業
提出日時 2020年5月23日 

[課題1]
演習 1.d 2つの配列を受け取り、2番目の各要素の値を1番目の配列の各
要素に足し込む

ソース
#include <stdio.h>
#include <stdbool.h>


void iarray_read(int *a, int n){//配列の入力 
	for(int i =0; i < n; i++){
		printf("%d. ",i+1);scanf("%d",a+i);
	}
}

void iarray_print(int *a, int n){//配列の出力 
	for(int i = 0; i < n; i++){
		printf("%d",a[i]);
	}
	printf("\n");
}

bool iarray_equal(int *a, int *b, int n){//配列の同値比較 
	for(int i; i < n; i++){
		if(a[i] != b[i]){
			return false;
		}
	}
	
	return true;
}

void iarray_add(int *a, int *b, int n){
	for(int i = 0; i < n; i++){
		a[i] = a[i] + b[i];
	}
}

void expect_iarray(int *a, int *b, int n, const char *msg){
	printf("%s %s\n",iarray_equal(a,b,n) ? "OK" : "NG",msg);
	iarray_print(a, n);
	iarray_print(b, n);
}

int main(void){
	int i, n, ret = 1;
	int a[9], b[9];
	while(ret == 1){
	
	printf("1.実行 2.テスト>");scanf("%d",&i);
	if(i==2){
		ret = 0;
		int a[] = {8,5,2,4,1}, b[] = {1,1,2,2,3}, c[] = {9,6,4,6,4};
		int d[] = {3,5,3,1,8,2}, e[] = {6,2,3,6,1,5}, f[] = {9,7,6,7,9,7};
	
		iarray_add(a, b, 5); 
		iarray_add(d, e, 6);
	
		expect_iarray(a, c, 5, "85241+11223 -> 96464");
		expect_iarray(d, f, 6, "353182+623615 -> 976797");
		
	}
	else if(i == 1){
		ret = 0;
		printf("n(0-9)>");scanf("%d",&n);
		iarray_read(a, n);
		iarray_read(b, n);
		iarray_add(a, b, n);
		iarray_print(a, n);
	}
	else{
		printf("error\n");
	}	
	}
	
	
	return 0;
	
}

説明
実行について
初めに扱いたい配列の長さnを0から9のなかで入力した。次に二つの
配列a,bの数値の入力を行った。その二つの配列a,bと配列の長さnを引
数にして、for文で0からn-1まで繰り返しながらaにbを足した。最後にa
を表示した。

単純テストについて
二つの配列a,b又はd,eを作成し、それぞれを足し合わせたc,fを自作した。
作成した関数を通してa,b又はc,dの足し算を行い、その結果と自作した
配列を比べ、すべての数が同じであるかを確認した。


実行例
1.
n(0-9)>5
1. 3
2. 1
3. 3
4. 4
5. 2
1. 5
2. 2
3. 1
4. 5
5. 2
83494

2.
n(0-9)>7
1. 4
2. 1
3. 7
4. 2
5. 4
6. 1
7. 8
1. 1
2. 4
3. 2
4. 5
5. 1
6. 1
7. 3
55975211

単体テスト
OK 85241+11223 -> 96464
96464
96464
OK 353182+623615 -> 976797
976797
976797

考察
このプログラムでは、入力する配列の最大数は最初から決めておかなければいけないので、
配列の長さを9までにしかできない。そのため、演習2で行った方法で
配列を作成方法を行えばより自由度が増す。

[課題2]
演習 2.c 1つの列を受け取り、その内容を昇順に整列した列を返す

ソース
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

void iarray_read(int *a, int n) {
  for(int i = 0; i < n; ++i) {
    printf("%d> ", i+1); scanf("%d", a+i);
  }
}

void iarray_print(int *a, int n) {
  for(int i = 0; i < n; ++i) { printf(" %2d", a[i]); }
  printf("\n");
}

int *ivec_new(int size) {
  int *a = (int*)malloc((size+1) * sizeof(int));
  a[0] = size; return a;
}

void ivec_read(int *a) { iarray_read(a+1, a[0]); }

void ivec_print(int *a) { iarray_print(a+1, a[0]); }

int *ivec_ch(int *a){
	int *c = ivec_new(a[0]);
	int *demo = ivec_new(a[0]);
	int i, k;
	
	for( int i = 1; i <= a[0]; i++){
		demo[i] = 1;
	}
	
	for(i = 1; i <= a[0]; i++){
		for(k = 1; k <= a[0]; k++){
			if(a[i] < a[k]){
				demo[k]++;
			}
		}
	}
	
	for(i = 1; i <= a[0]; i++){
		for(k = i + 1; k <= a[0]; k++){
			if(demo[i] == demo[k]){
				demo[k]++;
			}
		}
	}
	
	for(i = 1; i <= a[0]; i++){
		c[demo[i]] = a[i];
	}
	
	return c;
	
}

bool ivec_equal(int *a, int *b){
	for(int i = 1; i <= a[0]; i++){
		if(a[i] != b[i]){
			return false;
		}
	}
	return true;
}

void expect_ch(int *p, int *q, const char *msg){
	printf("%s %s\n",ivec_equal(p,q)? "OK":"NG", msg);
	ivec_print(p);
	ivec_print(q);
	
}

int main(void){
	int i, n, ret = 1;
	int *a, *c;
	while(ret == 1){
	
	printf("1.実行 2.テスト>");scanf("%d",&i);
	if(i==2){
		ret = 0;
		int a[] = {5,8,5,2,4,1}, b[] = {5,1,2,4,5,8};
		int c[] = {6,7,5,-2,0,3,7}, d[] = {6,-2,0,3,5,7,7}; 
		
		int *p = ivec_ch(a);
		expect_ch(p, b, "[8,5,2,4,1] -> [1,2,4,5,8]");
		
		int *q = ivec_ch(c);
		expect_ch(q, d, "[7,5,-2,0,3,7] -> [-2,0,3,5,7,7]");
		
	}
	else if(i == 1){
		ret = 0;
		printf("n>");scanf("%d",&n);
		a = ivec_new(n); ivec_read(a);
 		c = ivec_ch(a); ivec_print(c);
  		free(a);  free(c);
  
	}
	else{
		printf("error\n");
	}	
	}
  	
	return 0;
}

説明
実行について
初めに配列の長さnを入力した。次に、n+1の長さの配列aを作成しa[0]
にn+1が入力されn個の数字をa[1]から入力した。配列aを引数にして、
a[0]の長さをした配列cとdemoを作成した。demoの中を1にした。すべて
の配列aの要素をそれ以外の要素と比べ、自身より大きい数字を持った
要素があった場合、同じ配列番号を持った配列demoの要素を+1した。
その後、demoの要素が一致してる場合、配列番号が大きい配列の要素を
+1した。配列demoの要素が小さい順に配列aの要素を配列cに代入した。
そして、配列cを表示させた。単体テストについて二つの配列a又はcを
作成し、その配列の長さを最初の要素にして、a又はcの要素を昇順にし
た配列b又はdを自作した。作成した関数を通してaまたはcを昇順に並べ
なおし、その結果と自作した配列を比べ、すべての数が同じであるかを
確認した。

実行例
1.
n(0-9)>4
1> 7
2> 2
3> 5
4> 1
  1  2  5  7

2.
n(0-9)>7
1> 1
2> 8
3> 3
4> -2
5> -4
6> 0
7> 2
 -4 -2  0  1  2  3  8

単体テスト
OK [8,5,2,4,1] -> [1,2,4,5,8]
  1  2  4  5  8
  1  2  4  5  8
OK [7,5,-2,0,3,7] -> [-2,0,3,5,7,7]
 -2  0  3  5  7  7
 -2  0  3  5  7  7

考察
b1とは違う方法でソートを行ったが、ほとんど実行時間が変わらなかった。

[アンケート]
Q1.
扱うものが変数なのかポインタなのかをしっかりと把握し、使う場所を
考えてプログラムを作成する。

Q2.
ヘッダファイルを扱ったプログラムの作成ができていない。
動作の無駄が多い。

Q3.
隠ぺいについての理解が難しかった。