RT @pckouza_fisc: 平成…(07/20)
- 07/20 RT @pckouza_fisc: 平成23年8月8日から22日間でコンピュータとネットワークの?基礎研修を実施します http://ow.ly/5IB50 費用はテキスト料の実費?のみです。就職活動中の方や大学生の方は、就職する前に習得しておくと良いと思います… #fnct
この記事は、 の @TohruSaitohに掲載した #fnct タグ付き記事を、まとめたものです。
リスト処理応用part2
リストを用いたStack,Queueの説明の次に、 リスト途中へのデータ挿入と削除を説明。
リスト途中への挿入削除
リストの途中にデータを挿入するのであれば、以下の処理となる。 配列であれは、途中へのデータ挿入は挿入場所を作るための、 後ろシフトで、O(N)の処理がかかるけど、リストであれば、O(1)となる。
// mallocの使い方の意味も込めて、 // cons()を使わずに書いてみる。 void insert( struct List* prev , int value ) { // prevは、挿入すべき場所の1つ前とする。 struct List* n = (struct List*)malloc(sizeof(struct List)) ; if ( n != NULL ) { n->data = value ; n->next = prev->next ; prev->next = n ; } }
今日は、テスト直前ということもあり、説明後は早々に自習の時間とした。 んで、説明後の質問で「先頭にデータを入れるときは?」 このプログラムでは、1つ手前のデータのポインタがあることを前提にしているけど、 先頭の場合はこうはいかない。対応の方法としては、List**型を使うか、 「リストの先頭には必ず1個のデータを入れないダミーのList型を置く」 方法もある。こういった、リストが0件とか先頭とか末尾に伴うデータで特別処理を 書くのはプログラムを複雑にするので、先頭・末尾に置くダミー(番兵とと呼ぶ)を使えば特別処理も不要でプログラムをシンプルにできる。んで、番兵は、リスト処理ではよく見られるテクニック….という追加説明を行う。
同様に、途中データの削除も書いてみる。
void remove( struct List*prev ) { // prevは、削除したい場所の1つ手前とする。 struct List* del = prev->next ; prev->next = prev->next->next ; free( del ) ; }
リストでの集合演算
ここまでに示したように、リストを使えば要素の追加削除が簡単にできるため、 集合のように扱うことができる。
// p = { 1 , 2 , 3 } のつもり... struct List* p = cons( 1 , cons( 2 , cons( 3 , NULL ) ) ) ;
実際に、集合積のプログラムを以下に示す。 以下のプログラムでは、p1の各要素が、p2 に含まれていれば、 ans に要素を追加していく。
// 分かりやすく書くための補助関数 int find( struct List* p , int value ) { for( ; p != NULL ; p = p->next ) { if ( p->data == value ) return 1 ; } return 0 ; } // 集合積を求める struct List* prod( struct List* p1 , struct List* p2 ) { struct List* ans = NULL ; for( ; p1 != NULL ; p1 = p1->next ) { if ( find( p2 , p1->data ) ) ans = cons( p1->data , ans ) ; } return ans ; }
これまた、説明後にて、『 "if ( find( … ) != 0 ) {…" と書くべきでは?』 との質問。ということで、C言語の条件式は、0=false,0以外=true なので、 "if ( find(…) ) { … " で十分と解説する。
標準入力・標準出力・リダイレクト・パイプ
通常、プログラムを動かすと、scanf()の入力はキーボード、printf()の出力は画面となっている。 しかしunixなどでは、入出力の対象がキーボードか画面かファイルかプログラム入出力といった区別なくファイル(FILE)を使って読み書きができるようになっている。
標準入力は、通常の起動ではキーボードに割り当てられているが、 起動時に入力リダイレクトなどを使うと、ファイルから読み込むこともできる。 標準出力は、通常は画面に出力されるようになっているが、 起動時に出力リダイレクトなどを使うと、ファイルに書きこむこともできる。 標準入力や標準出力を使ってプログラムを書いておけば、 fopenなどを使わずに簡単でかつ、後で処理対象の入力元(あるいは出力先)を 切り替えることが簡単にできるようになる。
例題として、以下のように標準入力のデータを大文字に変換して 標準出力に出力するプログラムを作る。
// このプログラムをupper.cで // 保存し、コンパイル結果を upper.exe とする #include <stdio.h> void main() { int c ; while( (c = getchar()) != EOF ) { if ( c >= 'a' && c <= 'z' ) c = c-'a' + 'A' ; putchar( c ) ; } }
入力リダイレクトや出力リダイレクトを用いた作業例を以下に示す。
Z:> upper.exe This is a pen. THIS IS A PEN. That is a pencil. THAT IS A PENCIL. ^Z ←入力データがこれで終わり(unixなら^D) Z:> upper.exe < upper.c ←入力リダイレクト #INCLUDE <STDIN.H> : Z:> upper.exe > out.txt ←出力リダイレクト This is a pen. That is a pencil. ^Z ←入力データがこれで終わり(unixなら^D) Z:> type out.txt THIS IS A PEN. THAT IS A PENCIL.
入出力のリダイレクトでは、標準入出力をファイルからに変更できたが、 パイプを使えば、別のプログラムの出力を標準入力として利用したり、 プログラムの標準出力を別のプログラムの入力とすることもできる。
簡単な例として、echo コマンドは、引数をそのまま標準出力に出力する。 これを先のupper.exe と使うと、以下のようにできる。
Z:> echo "This is a pen." This is a pen. Z:> echo "This is a pen." | upper.exe THIS IS A PEN.
上記の例は、以下の入力リダイレクト出力リダイレクトとほぼ同じである。 中間ファイルを使わない点でパイプの方が単純…
Z:> echo "This is a pen." > temp.out Z:> upper.exe < temp.out
2011年7月17日(第225回)
テスト期間中につき、機械工学科 五味先生、電子情報工学科 奥田先生、西の3名でお送りしました!
- 高専地区体育大会の結果について
テニス部の結果 - エコ対策について
Microsoft Small Basic …(07/15)
- 07/14 Microsoft Small Basic だってさ。http://www.forest.impress.co.jp/docs/new… Turtleオブジェクト等も準備されているので、初心者にタートルグラフィックとか… #fnct
- 07/14 昨日、軽くトラブって思い出したので、管理しているサーバ群のアップデート中。 #fnct
この記事は、 の @TohruSaitohに掲載した #fnct タグ付き記事を、まとめたものです。
福井はやっぱり狭いっす
今日は、自分自身が高専学生の時のクラスメイトから、 とあるサイトが見えないという技術相談をうける。 でも、その質問は以前、別ネタでパソコン通信時代の知り合いから 質問された内容だった。
クラスメイトの下調べが丁寧だったので、 それなりに原因は分かった。 んで、その見えないサイトの運営は、 これまた電子情報OBの知り合いだった。
ということで、パソ通時代の知り合いと久々にFacebookで、 chatしながら説明してたんだけど、 「ミカカ怖い」とか言いながらchatしてたのが、Webベースに変わったとはいえ、 なんだかすごく懐かしい気分になった1日でした。
来年度は認証評価とJABEE…
来年度は認証評価の中間審査(予定)と、JABEEの審査が重なる。 んで、認証評価の準備の委員なので、ちょいと準備…
色々と書類を書くためのデータや、その分析などをしなければならない。
Arduinoでサーボ制御
夏休みに工業系先生向けの制御講習会を行うけど、 例年参加している人もでてくるので、少しは違う実験とすべく、 サーボモータの制御を入れてみようと計画中。 ということで、手持ちである共立電子の通信販売のサーボモータ(プチロボ)を動かしてみた。
#include <Servo.h> Servo motor ; // White D9 int d = 1 ; // Red 5V int v = 0 ; // Black GND void setup() { motor.attach( 9 ) ; } void loop() { // 0°〜180°〜0°を繰り返す。 motor.write( v ) ; v = v + d ; if ( v >= 180 ) { v = 179 ; d = -1 ; } else if ( v < 0 ) { v = 0 ; d = 1 ; } delay( 2 ) ; }
これで動かすと、このサーボモータでは、プログラム的には0-180で変化させているが、実際の変化幅は120°程であった。 delay(1)にすると、サーボの制御信号の変化にモータが追いつけないため、 90°ほどの変化幅となった。
ちなみに、最近購入したArduino Uno であったが、開発環境は Arduino 0022 に 更新しないと、Uno への書き込みができなかった。(0017はダメ)
加速度センサーと連動
加速度センサーで傾きを検出し、傾きに応じてサーボモータを動かしてみた。
#include <Servo.h> Servo motor ; int acc_x ; // Vcc--3.3V, GS1-GND g1.5 int acc_y ; // GND--GND , GS2-GND int acc_z ; // X----AN0 , SLP-3.3V int ang ; // Y----AN1 // Z----AN2 #define OFFSET 335 void setup() { motor.attach( 9 ) ; acc_x = acc_y = acc_z = 0 ; } void loop() { acc_x = (3*acc_x + analogRead(0) - OFFSET )/4 ; // 加重移動平均 acc_y = (3*acc_y + analogRead(1) - OFFSET )/4 ; acc_z = (3*acc_z + analogRead(2) - OFFSET )/4 ; ang = (int)( atan2( acc_y , acc_x ) / 3.141592 * 180 ) ; ang = constrain( ang , 0 , 180 ) ; delay( 2 ) ; }
リストを用いたスタックとキュー
リスト操作の演習中だけど、残り授業もあと(今日を入れて)あと2回ということで、 リスト関連のネタが未消化にならないように、スタックとキューについて説明。
スタック
配列を用いた、LIFO(Last In First Out)=スタックであれば、一般的に 以下のようなコードになる。
int stack[ 100 ] ; int sp = 0 ; void push( int x ) { stack[ sp++ ] = x ; } int pop() { return stack[ --sp ] ; }
しかし、この方法では、配列サイズ以上のデータは保存できない。 これをリストを使うことでサイズを気にしないスタックを実現できる。
struct List* stack = NULL ; void push( int x ) { stack = cons( x , stack ) ; } int pop() { int ans = stack->data ; struct List* del = stack ; stack = stack->next ; free( del ) ; return ans ; }
キュー
待ち行列(Queue)は、FIFO(First In First Out)を配列で実装する場合、 一般的には、以下のようになる。ただしエラー対策は記載していないので、要注意。
int que[ 100 ] ; int wp = 0 ; // 書き込み用ポインタ int rp = 0 ; // 読み出し用ポインタ void put( int x ) { que[ wp ] = x ; wp = (wp + 1) % 100 ; // 循環させる } int get() { int ans = que[ rp ] ; rp = (rp + 1) % 100 ; // 循環させる return ans ; }
このような配列の領域を使い切ったら、先頭から再利用するような方法は、 リングバッファなどと呼ばれる。 このような待ち行列は、キー入力バッファや、プロセス待ち行列などに よく利用される。しかし、このプログラムでも、配列サイズ以上の データは保存できないので、リストを用いる。
struct List* top = NULL ; struct List** tail = &top ; void put( int x ) { *tail = cons( x , NULL ) ; tail = &( (*tail)->next ) ; } int get() { int ans = top->data ; struct List* del = top ; top = top->next ; free( del ) ; return ans ; }
ただし、このプログラムは、常に1件以上データがリストに入っている場合は 問題がないが、get() を実行して、データ件数が0件になると、tail の指す先が おかしくなるので注意が必要。
また、待ち行列では、先頭ポインタと末尾ポインタの2つが必要であるが、 リスト構造の末尾のNULLを、先頭データを指すようにする循環リストと する場合も多い。特に、プロセス待ち行列を実装するときのラウンドロビン方式 などでは、末尾まで処理が及んだ次は先頭に戻って処理を行うため、 循環リストは都合がいい。
サーバ室の温度じゃないな
朝起きたら、監視システムから大量のメール。 高専のWebサーバがAM3:00頃から止まっているみたい。 後で聞いた話だと、ファイアウォールのトラブルで、それなりに復帰。
その復帰の話を聞いた後、しばらくして お守りをしている緊急連絡システムが繋がらないとの警告。 5年もののサーバなので、いつお亡くなりになってもおかしくはない。 (ホットスタンバイ機があるのでそんなに心配するほどじゃないんだけどね。) んで、実際にサーバ室に向かうと、マシンは正常に動いている。 どうも間に入れているファイアウォール的なルータが気絶していたみたい。 電源リセットで普通に復帰してくれた。
んで、下の写真がサーバ室の温度計。クーラー効いているんだけど、 何台ものマシンがあるから、29℃….普通のサーバ室の温度じゃない。 普通は24℃設定で、最近の省エネのニュースで26℃でも問題ないとか 話題があがっていたからな…