ホーム » スタッフ (ページ 125)
「スタッフ」カテゴリーアーカイブ
授業アンケートの結果
授業アンケートが、年度末に演習室が使えなかったことから、 時期遅れではあるけれど、結果がでてきた。
プログラミング応用は、昨年度の81.7からは、若干落ちて76.3ポイント。 Q4の板書について、「やや悪い」の評価が多いのと、 Q15の理解把握について、「良い」の評価が少なめなのが、効いているか…。 板書については、文字を大きくとか、記載場所の流れを改めて意識してみよう。
情報構造論は、79.8ポイント。あとちょっとで、80点(学生さんなら優)だったのにぃ。 昨年度は、80.9ポイントなので、連続ポイントダウン。 Q4板書のポイントが、これまた低めなので、改めて意識した書き方の必要があるかな。
講義録記事にヒント表示を導入
講義録をBLOGの形で、ページに掲載するのを、この10年ほど続けているけど、 準備が不十分な場合には、授業で過去の記事をプロジェクタで表示しながら説明… という場合もある。
しかし、過去の記事で丁寧に書いてありすぎると、問題の答えまでモロに記事に 書いておくと、記事を読んでいる学生さんが、何も考えないまま…に なってしまう場合が多い。そこで、BLOGのJavaScriptに、 クリック表示/非表示を切り替えられるようなものを埋め込んおくようにした。
// BLOGパーツのJavaScriptに以下を追加 function hint_switch( hs_this ) { // 指定ブロック内の末尾ブロックをinline表示/none非表示をスイッチ if ( hs_this.lastElementChild.style.display == "none" ) { hs_this.lastElementChild.style.display = "inline" ; } else { hs_this.lastElementChild.style.display = "none" ; } }
そして、一時的に非表示にしたい部分には、以下のHTMLを埋め込む。
<div onclick="hint_switch(this)"> <p> // 常に表示しておく部分(ここをクリック) </p> <div style="display: none"> // 一時的に、非表示にしておきたい部分 </div> </div>
変数の寿命とスコープ
先週のC言語の制御構文のシメとして、switch-case文の説明をしてから、 変数の寿命とスコープの説明を行った。
switch-case文の説明では、以下の例は期待通りの動きをしない…といった例も交えて説明。 ただし、double誤差問題や文字列のポインタ関連なので、以後の授業で改めて説明が必要だろう。
// double誤差問題 double x ; for( x = 0.0 ; x <= 1.0 ; x += 0.1 ) switch( x ) { case 0.3 : printf( "A" ) ; // 0.299999...と0.3は違う値になるかも break ; } // 文字列の比較の問題 char str[ 10 ] ; scanf( "%s" , str ) ; switch( str ) { // strの先頭番地と"yes","no"の先頭番地の比較 case "yes" : printf( "はい" ) ; break ; case "no" : printf( "いいえ" ) ; break ; }
変数の寿命とスコープ
最初に、以下の様なプログラムが期待通りに動かない説明をして、 大域変数を共用することの問題を話す。
int i ; // 大域変数(global variable) void foo() { // Aを2回表示 for( i = 0 ; i < 2 ; i++ ) printf( "A" ) ; } void main() { // foo(Aを2回表示)を2回呼び出すつもり for( i = 0 ; i < 2 ; i++ ) foo() ; }
こういったトラブルを避けるためには、局所変数を使えば良い。
局所変数を使って、目的の処理…。改良版はココをクリックで表示。
最近の構造型プログラム言語であれば、 変数には寿命(変数が、作られる/消える、タイミング)と、 スコープ(変数が使える範囲)がある。
#include <stdio.h> // 静的大域変数(スコープ全体,寿命:起動から終了) int x = 123; void foo() { // 動的局所変数(スコープ:foo内部,寿命:foo呼出から戻るまで) int y = 234 ; // 静的局所変数(スコープ:foo内部,寿命:起動から終了) static int z = 345 ; x++ ; y++ ; z++ ; printf( "%d %d %d¥n" , x , y , z ) ; } void main() { foo() ; // 124,235,346が表示 foo() ; // 125,235,347が表示 }
関数の引数
関数の引数の受け渡しの説明。
返り値の型 関数名( 仮引数の宣言 ) { 何らかの処理 ; // 関数に入ると仮引数が局所変数で作られ、 return 式 ; // 実引数が代入される。 } void main() { 関数名( 実引数 ) ; // 実引数は仮引数にコピーされる。 }
値渡し、ポインタ渡しなどの説明をしたいけど、時間なので次週に説明。 残り時間では、BCPL→B言語→C言語(K&R-C→ANSI-C)→C++といった C言語の変遷を簡単に紹介。
処理速度の分析とオーダ記法
今回は、情報処理センターの機種更新に伴うパスワード再発行やら、 授業アンケートの作業に前半の時間をとられ、そのまま演習室にて授業。
2分探索法の処理時間分析
最初に、先週説明の単純サーチ と、2重ループの最大選択法
との比較をしながら、 以前のBLOG資料を使って、 2分探索法の処理時間が
であることを説明する。
オーダ記法
次に、定番の説明であるけれど、 「単純サーチで、100件で1msecかかった。 データ10000件なら何秒かかる?」 同様に、「最大選択法のプログラムで、100件で10msecかかったら、10000件なら何秒?」 という質問から、オーダ記法の導入を説明する。
最後に、 ,
,
といった、Nが大きな値になった時に、式で大きな値となる主要な部分を抜き出したもの がオーダといった説明を行う。
次回の授業での予習ネタとして、以下の式のオーダについて考えておくように…
2015年4月12日(第418回)
- まるよし Train Pops ~ 国語と遊ぼう! 第98便 「最後の授業」 前編
- 入学式について
- 板書に特徴のある先生
担当:稲葉(2EI・MC)、川﨑(2EI)、田中(2B)、中村(教員)
斉藤卒研室の課題
簡単なunix環境での卒研の課題として、サーバに /home/lab15/Sotsu/access.log を 置いておいた。このファイルは、Webサーバでの数日前のアクセス履歴である。 このファイルの中から、2014年卒研のページ "/sotsu/lab14/" を見た人の 人数を数えよ。
(( /home/lab15/Sotsu/access.log )) 192.156.146.101 - - [29/Mar/2015:07:35:46 +0900] "GET /~t-saitoh/exp/h8/sample/h8car/h8-3664.x ....
SQLite3でデータベース
卒研でデータベースを使いたい人もいるようだけど、 MySQLとかまで完璧なのが必要なければ、SQLite を使ったほうが楽。 ただし、データ型は実質すべてtext型になるけど、簡単なアプリベースなら 支障はないはず。
<?php // データ保存用の sqlite-data はあらかじめ作っておく。 // sqlite-data の書き込み許可 // (手抜) $ chmod 777 sqlite-data // (厳密) # chgrp sqlite-data // # chmod 774 sqlite-data // sqlite-data/.htaccess には"Require all denied"を書いて // ディレクトリ内をWeb的にアクセス禁止にする。 // サーバのPHPを使うと、エラーが見つからず苦労するかも // その時は、.htaccess ファイルに、以下の設定を記載しておく // "php_flag display_errors On" // 説明しやすいように実行だけ関数をつくる function exec_command( $db , $cmd ) { if ( ($db->exec( $cmd )) === FALSE ) { print $db->lastErrorMsg() ; } } // データベースを作って初期データを登録 function table_initialize( $db ) { exec_command( $db , "create table Person(name text,phone text) ;" ) ; exec_command( $db , "insert into Person (name,phone) values('t-saitoh','272925');" ) ; exec_command( $db , "insert into Person (name,phone) values('tomoko' ,'123456');" ) ; exec_command( $db , "insert into Person (name,phone) values('mitsuki' ,'234567');" ) ; } // データベースを作る if ( !file_exists( "./sqlite-data/sample.db" ) ) { // なにも無い状態 $db = new SQLite3( "./sqlite-data/sample.db" ) ; table_initialize( $db ) ; } else { // すでに作られている場合 $db = new SQLite3( "./sqlite-data/sample.db" ) ; } ?> <html> <head> </head> <body> <pre> <?php // 登録されているデータを全部表示 if ( ($query = $db->query( "select * from Person" )) !== FALSE ) { while( $res = $query->fetchArray( SQLITE3_NUM ) ) { printf( "| %-10s | %-10s |\n" , $res[0] , $res[1] ) ; } } ?> </pre> </body> </html>
情報構造論ガイダンス
前年度のプログラミング応用の次のステップとしての情報構造論として、 どういった内容を行うのかガイダンスを行う。
最初に、使用する教科書のタイトルにもある、アルゴリズムという言葉の説明として、 アルゴリズムとは、計算の方法の考え方、 そのアルゴリズムをどう表現するのかがパラダイム、 そのパラダイムに沿って実際に書いたものがプログラム。
いいプログラムとは?
次に、ある程度プログラム記述を学んできたところだし「あなたはどうプログラムを書くといいと思うか?」 を次々と聞いてみた。 学生さんから帰ってくる言葉は、 「わかりやすい、行数が短い、関数が使われている、変数名が解りやすい、バグが少ない…」 といった物がほとんど。 中には、「プログラムが再利用しやすい」といったオツな答えもあったけど、 どれも大きくまとめれば「わかりやすい」という観点。
途中で、「処理速度が速い」という答えもあったけど、「メモリの使用量が少ない」という 観点がなかなか出てこない。
処理速度という観点では、3秒ルールなどの雑談を交えながら計算時間について説明。 また、メモリの使用量という観点では、限られた主記憶を有効に使わないと、 プログラムが動かなかったり、仮想記憶などを使うことになり、頻繁なスワップ処理で 処理速度の低下につながることを説明する。
これらの「プログラムの分かりやすさ」、「処理速度」、「メモリの使用量」は、一般的に、 どれかを優先すれば、ほかの観点が悪化してしまうトレードオフの関係にあることを説明。
処理時間を式で表現
まず、プログラムの処理時間を分析するために、簡単なプログラムがどんな式で表現できるかを示す。
// 配列(順序はデタラメ)の中の特定データを探す処理。 int data[ N ] ; for( i = 0 ; i < N ; i++ ) { if ( data[ i ] == key ) break ; }
このようなループ処理であれば、ループ回数の平均を考えて式にすると、
で表現できることを説明。
// 最大選択法の並び替え int data[ N ] ; for( i = 0 ; i < N-1 ; i++ ) { int m = i ; for( j = i+1 ; j < N ; j++ ) { if ( data[m] > data[j] ) m = j ; } tmp = data[ m ] ; data[ m ] = data[ i ] ; data[ i ] = tmp ; }
このような処理であれば、ループ回数をΣなどを用いて表しながら、以下のような式で示されることを説明。
プログラミング応用ガイダンス
プログラミング応用では、どういった内容を扱うのかを最初に説明した後、 最初は2年の復習となることから、C言語の文法のおさらい。 特に制御構文のfor,ifを中心にフローチャートとの対応をとる説明とした。
最初に、簡単なプログラムでの式の実行順序の理解の確認。
// (1) int i ; for( i = 0 ; i < 4 ; i++ ) { if ( (i % 2) == 0 ) { printf( "%d\n" , i ) ; } } // (2) int i , j ; for( i = 0 ; i < 3 ; i++ ) { for( j = 0 ; j < 2 ; j++ ) { printf( "%d\n" , i * j ) ; } }
次に、break文とcontinue文の説明を行う。
// for文 int i ; for( i = 0 ; i < N ; i++ ) { : if ( .... ) break ; if ( .... ) continue ; : } // break , continueの動き i = 0 ; // for初期化 LOOP: if ( i >= N ) goto BREAK ; // for終了判定 : if ( .... ) goto BREAK ; // break(ループ脱出) if ( .... ) goto CONTINUE ; // continue(次の繰返し) : CONTINUE: i++ ; // for繰返し変数更新 goto LOOP ; BREAK:
オブジェクト指向ガイダンス
専攻科2年のオブジェクト指向の最初の講義で、前半ガイダンスで後半、C言語の構造体などの復習。
オブジェクト指向に関連する歴史
簡単にオブジェクト指向プログラミング(Object Oriented Programming – 略してOOP)が出てくるまでの歴史的流れを最初に説明。
最初のプログラム言語のFortran(科学技術計算向け)の頃は、処理を記述するだけだったけど、 COBOL(商用計算向け)ができた頃には、データをひとまとめで扱う「構造体」(C言語ならstruct …}の考えができた。 その後のALGOLの頃には、処理をブロック化して扱うスタイル(C言語なら{ 文 … }の複文で 記述する方法ができて、処理の構造化・データの構造化ができる。これが「構造化プログラミング(structured programming)」 の始まりとなる。
Wikipediaの記事だと、構造化プログラミングは手続きの構造化のことしか書いてないなぁ… 個人的にはOOPの話の一環として話すため、データ構造化も構造化プログラミングの一部としています。
この後、様々なプログラム言語が開発され、C言語などもできてきた。 一方で、シミュレーションのプログラム開発(例simula)では、 シミュレーション対象(object)に対して、命令するスタイルの書き方が生まれ、 データに対して命令するという点で、擬人法のようなイメージで直感的にも分かりやすかった。 これがオブジェクト指向の始まりとなる。
この考え方を導入した言語の1つが Smalltalk であり、この環境では、プログラムのエディタも Smalltalk で記述したりして、オブジェクト指向がGUIのプログラムと親和性が良いことから普及が拡大する。
C言語にこのオブジェクト指向を取り入れて、C++が開発される。さらに、この文法をベースとした、 Javaなどが開発される。最近の新しい言語では、どれもオブジェクト指向の考えが使われている。
構造体の導入
C++でのオブジェクト指向は、構造体の表記がベースになっているので、まずは構造体の説明。
// 構造体の宣言 struct Person { char name[ 20 ] ; // 要素1 int phone ; // 要素2 } ; // 構造体変数の宣言 struct Person saitoh ; struct Person data[ 10 ] ; // 実際にデータを参照 構造体変数.要素名 strcpy( saitoh.name , "t-saitoh" ) ; saitoh.phone = 272925 ; for( int i = 0 ; i < 10 ; i++ ) { scanf( "%d%s" , data[ i ].name , &(data[ i ].phone) ) ; }
値渡し、ポインタ渡し、参照渡し
構造体の使い方の話では、関数との構造体のデータ渡しでポインタなどが出てくるので、 値渡し・ポインタ渡し・参照渡しの復習。(参照渡しはC++で導入された考え方)
C言語の基本は、値渡し。呼び出し側の実引数は、関数側の仮引数に値がコピーされる。 このため、呼び出し側の変数(下の例ではa)の中身は変化しない。 よって、関数の呼び出しで呼び出し側の変数が勝手に中身が変わらないので、予想外の変数の中身の変化が無く分かりやすい。
// 値渡し(call by value)の例 void foo( int x ) { x++ ; printf( "%d¥n" , x ) ; } void main() { int a = 123 ; foo( a ) ; // 124が表示 foo( a ) ; // 124が表示 }
しかし、上の例では、foo()の呼び出しで、変数aの中身が変化してくれたほうが都合が良い場合もある。 この場合は、C言語ではポインタを使って記述する。 このように、関数を呼び出して、手元の変数が変化することは、副作用と呼ばれる。 副作用の多いプログラムは、変数の値の管理がわかりにくくなるので、副作用は最小限に記述すべき。
// ポインタ渡し(call by pointer)の例 void foo( int *px ) { (*px)++ ; printf( "%d¥n" , (*px) ) ; } void main() { int a = 123 ; foo( &a ) ; // 124が表示 foo( &a ) ; // 125が表示 }
しかし、ポインタを多用すると、ポインタを動かしてトラブルも増えることから、ポインタはあまり使わない方が良い。 そこで、C++では参照型というものがでてきた。
// 参照型(call by reference)の場合 void foo( int &x ) { x++ ; printf( "%d¥n" , x ) ; } void main() { int a = 123 ; foo( a ) ; // 124が表示 foo( a ) ; // 125が表示 }
参照型は、ポインタを使っていないように見えるけれども、機械語レベルでみればポインタを使ったものと同じ。