FD講演会で認証評価に関する講演
今年度は、福井高専では学位授与機構にによる認証評価とJABEE審査が行われる。 講演では、認証評価に備えた説明があった。
この中で、特徴的と思われる点は、第2サイクルの評価に当たり、 「学生による事前学習」が重要とのことであった。 このため、シラバスには事前学習の内容を明記などが、必要となりそう。 特に各週毎に明記が理想とのことであった。
うーむ、シラバスの管理システムに、記述欄そんなに増やせるのかな…
グラフィックスと純粋仮想クラス
先週の純粋仮想基底クラスの事例を、具体的なプログラムでの演習とすべく、 下記資料を配布し、演習を行う。
最初に、他学科の学生であればGrWinを知らない可能性もあり、 本科3年向けの資料を配布し、説明を行う。 ただし、電気電子OB学生が中心であるため、GrWinは授業で経験済みであった。
Figure というdraw()という仮想関数を持つ純粋仮想基底クラスを宣言し、 FigureBox , FigureCircle という、具体的な図形のクラスを派生させ、 各描画関数 draw を示す。演習では、別の図形を派生させたり、 色つきクラスに拡張してもらうなどして、次のクラス設計の元ネタとすべく演習を予定。
高専プロコン(第23回)の学内審査
高専プロコンへの参加者を決めるための学内審査を行った。
4EI前期授業の「創造工学演習」の中からの応募と、 他のグループからの応募希望者で選考を行なっているが、 今年は4EI以外からの希望者がなく、創造工学演習からの参加者でのみ選考を 行った。
- (競技部門)
-
競技部門への参加希望は、この1チームだけであり、 プログラム作成時の問題点をあげ、それに対応できる方法を検討中であった。 現在、各種プログラムをつくり、基本的な動作のテスト中ということで、 競技部門へ応募。
- (課題部門): お年寄りと子供の交流の場を提供するシステム
-
課題部門の「少子高齢化」という趣旨に沿っている点で、 アイデアは良く、熱心さも伝わる資料であった。 しかし、ゲーム要素等の具体性がなく説明があいまいで不十分。 より具体的な概念図が必要と思われる。 説明の中で、独自用語の説明が不十分で、作成予定の独自デバイスについても 機能などが不明瞭なので、説明などを修正して課題部門に応募。
- (自由部門): 子供がつえをもってお遊び
-
ディスプレイの前で杖を持ち、各種ゲームをする点で子供たちが喜びそう。 教育要素も取り入れているのが良いが、類似ソフトとの違いの説明が必要であろう。 説明や内容を練り直し、自由部門での応募を目指す。
- (自由部門): ネットワーク対応LEGOブロックシミュレータ
-
既存のシミュレータと違い、ネットワーク対応・複数人で遊べる点が良いと思われる。 ブロックの新しい遊び方を提案できる可能性を評価。 具体的な画面・説明・機能の説明が必要で、加筆の上自由部門での応募を目指す。
- ×電子ブロックシミュレータ
-
既存のソフトとの違いが不明瞭で、論理回路も利用可能にするなどのアイデアを 評価したいが、実装方法も不明瞭で完成が難しそうであった。
- ×どこでも掲示板 ネットワーク対応の電子掲示板
-
基本的な部分は、実現しやすいと思われるが、既存の電子掲示板の機能を超えていない。 フォントの取扱いやマトリックスLEDの複数行への対応の説明が不明瞭で、 完成が難しそう。
- ×稲作シミュレーション
-
稲作をとりあげるなど面白いと思えるが、実現性は高いが面白さのためのアイデア不足 している。 食文化をどのように、外国人に伝えることを目的としているけど、その方法が不明瞭であった。
2011年度授業アンケート
授業アンケート(2011年度)のデータが送付されてきた。 ということで、例年通りそのまま記載。
データベース

75.9→77.8でポイントは微増。 黒板やOHP資料のポイントが低い。 担任クラスでもあり、甘めの評価の懸念あり。
情報構造論

81.3→82.3でポイントは微増。 黒板やOHP資料のポイントが低い。 意見欄では、良かったや分かりやすかったの意見があり、 総じて良い評価であったと思われる。
創造工学演習

時間不足を指摘する意見が多い。 前年度の授業段階から事前準備などの提案もあり、 目的意識を持って取り組んだという意味では、良い傾向と思われる。
プログラミング応用

77.3→81.0でポイントも上がっている。 この科目では、黒板OHPのポイントが特に悪いという程ではない。 意見欄でも、プリントが分かりやすいとか理解が深まったとの 好印象な意見が得られた。 一方、授業中の演習が座学と区別しているため、実際にどんな動きをするのか見てみたかったとの意見もあった。
計算機システム

N進数変換とbit演算の応用
前回の授業で、#defineマクロやbit演算の説明をしたけど、 まだ詰めが甘かったので、開いたサブルーチン・閉じたサブルーチンなどの 説明を行い、標準関数などの説明も行う。
開いたサブルーチンと閉じたサブルーチン
#define マクロと、関数の違いを理解してもらうために、 プリプロセッサ・コンパイル・リンクといった機械語生成までの流れを説明し、 #define マクロを多用すると、同じ機械語が何度も生成され、機械語が肥大する 可能性を説明する。逆に関数を何度も利用しても、一箇所の機械語が使われるため 機械語の肥大は無いことを説明する。一方、関数では実引数・仮引数の 値渡しなどが発生するので、わずかとはいえ余計な処理時間を要することを説明。 この結果、#define マクロは、小さな関数向きであることを述べる。 また、関数は入口・出口が明確に作られることから、閉じたサブルーチンと呼ばれる。 逆に #define マクロは入口・出口が明確でなく、開いたサブルーチンと呼ばれることを 説明する。
N進数変換や文字列処理で使う標準関数
N進数変換や文字列処理で、ctype.h の isdigit() 関数や、stdlib.h の strtol() などの 標準関数を説明する。
型の問題と実行時のトラブル
1桁の数値と+,-記号で式の値を求めるeval() という関数を示し、 文字列処理のサンプルを説明する。このあと、以下の様な呼び出しの問題点を考えてもらう。
int eval( char s[] ) { // 一桁の数値と"+","-"の計算式 // 例 printf( "%d" , eval( "1+2+3" ) ) ; // 6 } void main() { printf( "%d" , eval( 1+2+3 ) ) ; // 引数型の不一致 printf( "%d" , eval( '1+2+3' ) ) ; // 文字定数エラー printf( "%d" , eval( "1+2+3" ) ) ; // OK // よくありそうな書き間違い char s[ 100 ] ; scanf( "%s" , s ) ; printf( "%d" , eval( s[100] ) ) ; // 引数型不一致 // 文字列ポインタの間違い char* s ; scanf( "%s" , s ) ; // 変な場所に文字列格納でメモリエラー printf( "%d" , eval( s ) ) ; // char s[ 5 ] ; scanf( "%s" , s ) ; // 1+2+3+4+5+6 を入力したら!? printf( "%d" , eval( s ) ) ; }
リスト構造
配列の利点・欠点を踏まえ、リスト構造の説明を行う。
配列は、N番目のデータ抽出が簡単であり、2分探索法では中央の値を 簡単に取り出せることから、O(log N)の処理が可能となる。 この一方で、C言語では配列サイズは固定であり、 最初の段階で適切なサイズが解らない場合は、メモリの使用効率を 低下させる。
これに加え、配列の欠点としてあげられるのが、途中データの削除・挿入となる。 配列の指定位置にデータを追加しようと思うと、 入れるべき場所に"空き"を作るために、以降のデータを1つ後ろにずらす必要がある。 削除でも同様に、1つ前にずらす処理が必要となる。 これにかかる処理時間は、最悪N件、最低で0件となることから、平均N/2回の ループを要し、O(N)の処理が必要となる。
これらの欠点の対応方法として、配列であれば、次のデータの入っている 場所を使えば良い。
((リストを配列で)) top: 3 data,next 0: 41 1 int data[..] ; 1: 60 -1 int next[..] ; 2: 10 4 for( p = top ; p >= 0 ; p = next[ p ] ) 3: 6 2 printf( "%d" , data[ p ] ) ; 4: 22 5 5: 34 0
この方法は、アパートの回覧板で、部屋の住人は「次の部屋の人の番号を知っていれば十分」という状況と同じである。 この方式であれば、たとえデータを追加したい場合でも、 例えば30を追加するのであれば、data[4]の後ろに追加したいのであり、 data[6] = 30 を保存し、next[4] = 6、next[6] = 5 とすれば、途中にデータを混ぜることができる。
この方法によって、データを途中に追加する場合でも、O(1)で挿入が可能となる。 (ただし挿入すべき場所を探すにはO(N)の時間を要する)
リスト構造
一方でこのままでは、data[],next[]の最初の配列サイズ以上のデータを保存することは できない。そこで必要に応じてメモリを確保する方法をとる。
// 基本となるリスト構造の宣言 struct List { int data ; struct List* next ; } ; // 手作業で地道にリストを生成 struct List* top ; top = (struct List*)malloc( sizeof(struct List) ) ; top->data = 1 ; top->next = (struct List*)malloc( sizeof(struct List) ) ; top->next->data = 2 ; top->next->next = (struct List*)malloc( sizeof(struct List) ) ; top->next->next->data = 3 ; top->next->next->next = NULL ; // リストデータを表示 struct List* p ; for( p = top ; p != NULL ; p = p->next ) printf( "%d" , p->data ) ;
このような方法であれば、必要に応じてメモリを確保するため、 配列サイズが途中で不足するといった事態を考える必要がない。 (mallocがNULLを返すことは考える必要があるけど…) 一方、欠点としては、データを参照する場合には、 先頭から1つづつアクセスする必要があり、 中央データを参照して2分探索といったテクニックは使えない。
いいねぇ〜 “素人でも使…(05/21)
- 05/21 いいねぇ〜 "素人でも使いやすい回路図エディタBSch3V" http://tinyurl.com/6wdovm #fnct
- 05/21 いいねぇ〜… RT @jun1s: 先日も、市内の某IT会社の役員達と飲んでいたわけだが、曰く、「いい人材の雇用はとかく難しい。面接やテストだけではなかなか測れない。奥が深い。しかし、高専卒は別。彼らは安心して採用できる。5年間彼らを見てきた先生達から意見を… #fnct
この記事は、 の @TohruSaitohに掲載した #fnct タグ付き記事を、まとめたものです。
浮動小数点データ・bit演算・#defineマクロ
演習も終わった所で、同じプログラムを効率良く記述できることを知ることを 目標に、bit演算や#defineマクロを説明する。
浮動小数点データ
浮動小数点形式のデータは、float,double の2通りがあるが、最近はほとんど double 型で 計算をすれば十分。単精度実数(float)と、その倍の倍精度実数(double)
((float)) S:符号(1bit) E:指数部(8bit) F:仮数部(23bit) SEEE,EEEE,EFFF,FFFF,FFFF,FFFF,FFFF,FFFF 指数部 - 127 float = 1.仮数部 * 2 ((double)) S:符号(1bit) E:指数部(11bit) F:仮数部(52bit) SEEE,EEEE...EEEE,FFFF,FFFF,.....,FFFF,FFFF 指数部 - 1023 double = 1.仮数部 * 2
bit演算子
2進数,8進数,16進数の変換プログラムでは、 2,8,16といった数値で割った余りや、商を求めることが多い。 コンピュータ内は2進数で扱っているため、16での余りを求める計算は、 下4bitを取り出したり、16で割るという計算は4ビットシフトに置き換えられる。 そこで、AND(&),OR(|),XOR(^),NOT(~)などのbit演算を説明。
printf( "%d" , 5 & 3 ) ; // 0101 & 0011 = 0001 = 1)10 printf( "%d" , 5 | 3 ) ; // 0101 | 0011 = 0111 = 7)10 printf( "%d" , 5 ^ 3 ) ; // 0101 ^ 0011 = 0110 = 6)10 printf( "%d" , 5 << 2 ) ; // 0101 左2bitシフト = 010100 = 20)10 printf( "%d" , ~10 + 1 ) ; // 反転+1は2の補数 = -10
#defineマクロ
N進数変換のプログラムの中には、下記のような部分があるけど、これを シンプルに書くためには、#defineマクロがよく利用される。
char s[...] ; if ( '0' <= s[i] && s[i] <= '9' ) .... #define isdigit(C) ('0' <= (C) && (C) <= '9') if ( isdigit( s[i] ) ) ...
C言語のプログラムは、最初にプリプロセッサと呼ばれる文字列置換プログラムによって、 #include や、#define の処理が行われる。 #define は、指定された文字を、指定された文字に置換する。 #define マクロは、それを関数的に記載できるようにしたもの。 閉じたサブルーチンと開いたサブルーチンの説明は次回に行う予定。
qmailとsudoの相性の問題?
先日、学科のメインサーバのアップデートで、sudoなども更新された。 これでMovableTypeの記事をメールで書き込む mail2entry.pl の動きがおかしくなった。
具体的には、今までは、sudo を使って www-data 権限を得て 記事を書き込んでいた。
(( .qmail-XXXX )) | /usr/bin/sudo -u www-data /usr/lib/cgi-bin/movabletype/mail2entry.pl
しかし、sudo-1.8.3p2 では、記事書き込み終了後に、mail2entry.pl が ZOMBIE になって処理が終わらず、上位プロセス sudo , qmail-local が残って しまう。このため、一定数以上のmovabletype記事書き込みが溜まると、 メールシステム全体がメール配達が止まったり遅配となっていた。
どうも、sudo の問題みたいなので、sudo を使わずに処理を行うように、 設定を変更した。具体的には、/var/qmail/users/assign によって、 www-data 権限を与えて動かすようにした。
(( /var/qmail/users/assign )) +mt-XXXX:www-data:33:33:/var/qmail/alias:-:mt-XXXX: (( /var/qmail/alias/.qmail-mt-XXXX )) | /usr/lib/cgi-bin/movabletype/mail2entry.pl (( 設定 )) # chmod 644 /var/qmail/alias/.qmail-mt-XXXX # qmail-newu # /etc/init.d/qmail restart
純粋仮想基底クラスと関数ポインタ
先週の仮想関数の説明の応用ということで、 コンテナクラスなどの話につなげるための純粋仮想基底クラスの説明を行う。 その前に、仮想関数の実装の元となっている関数ポインタを簡単に紹介する。
関数ポインタ
関数ポインタとは、名前の通りで、動作説明の簡単なプログラムを紹介。
int add( int x , int y ) { // 加算関数 return x + y ; } int mul( int x , int y ) { // 乗算関数 return x * y ; } void main() { int (*f)(int,int) ; // int×2引数、返り値intの関数へのポインタ f = add ; printf( "%d" , (*f)( 2 , 3 ) ) ; // 5を表示 f = mul ; printf( "%d" , (*f)( 2 , 3 ) ) ; // 6を表示 }
関数ポインタを利用すれば、異なるデータに対する処理を、 汎用性高く作ることも可能となる。
int intcmp( int* x , int* y ) { // 整数比較関数 if ( *x > *y ) return 1 ; else if ( *x < *y ) return -1 ; else return 0 ; } int vmax( void* array , // 配列先頭アドレス int size , // 配列データ件数 int sizeofdata , // 1件あたりのbyte数 int(*f)( void*,void* ) ) { // 比較関数 int i , max = 0 ; for( i = 0 ; i < size ; i++ ) if ( (*f)( array + max * sizeofdata , array + i * sizeofdata ) ) max = i ; return max ; } int idata[ 4 ] = { 11 , 33 , 22 , 44 } ; char sdata[ 4 ][ 4 ] = { "ab" , "bc" , "aa" , "c" } ; void main() { int m ; // intcmp関数を使って、idata から最大値を探す m = vmax( idata , 4 , sizeof(int) , intcmp ) ; printf( "%d" , idata[ m ] ) ; // strcmp関数を使って、sdata から最大値を探す m = vmax( sdata , 4 , sizeof(sdata[0]) , strcmp ) ; printf( "%s" , sdata[ m ] ) ; }
純粋仮想基底クラス
これらの仮想基底クラスの考え方をもっと利用すると、 1つの配列に、異なる型の派生クラスを保存することも可能となる。
// 純粋仮想基底クラス class Object { public: virtual void print() = 0 ; } ; // 整数クラス class IntObject : public Object { private: int data ; public: IntObject( int x ) { data = x ; } virtual void print() { printf( "%d" , data ) ; } } ; // 実数クラス class DoubleObject : public Object { private: double data ; public: DoubleObject( double x ) { data = x ; } virtual void print() { printf( "%f" , data ) ; } } ; // 文字列クラス class StringObject : public Object { private: char* data ; public: StringObject( char* x ) { data = x ; } virtual void print() { printf( "%s" , data ) ; } } ; void main() { Object* a[ 3 ] ; // 配列に整数・実数・文字列が混在できる。 a[0] = new IntObject( 123 ) ; a[1] = new DoubleObject( 1.23 ) ; a[2] = new StringObject( "abc" ) ; // 混在したデータでも、正しく全要素をprint()できる for( int i = 0 ; i < 3 ; i++ ) a[ i ]->print() ; }