ホーム » スタッフ » 斉藤徹 (ページ 108)

斉藤徹」カテゴリーアーカイブ

2025年7月
 12345
6789101112
13141516171819
20212223242526
2728293031  

検索・リンク

男子バドミントン部春季総体参加

団体戦は残念ながら初戦で破れましたが、以前に比べ粘れる試合が出来るようになったと思います。

1506051131_720x720.jpeg

Windows Update未適用が…

先週末の土日と、Kinectを使ったIT研向けの講習会を 3F演習室で行ったた。 この際に、Visual Studio をインストールする必要があって、 作業をしていたが、Windows Update の未適用が大量にあり、 実質2日間かけてアップデートを行った。

FEREC利用で最初の未適用…

原因は、WiFi設定で FEREC を使うようになっていると、 学生がWiFiで接続している時だけネットワークに繋がるため、 その作業時以外は、ネットワークにつながらず Update 未適用 になるのが一因とも思われる。

Windows Updateサーバが遮断された

最近は、有線LANでの接続状態にしてあるが、WiFiでUpdate 未適用の頃に、Microsoftのupdate用サーバ情報が更新されず、 Updateサーバを偽物と誤認し、ファイアウォールが接続の邪魔を して Windows Update が動かなかったと思われる。

一旦、ファイアウォールを止めて更新をかけたりしたら、 無事に更新がかかるようになった。 でも、100件以上の更新で、Updateの適用待ちやら再起動やらで、 実質2日仕事であった。

仮想関数と純粋仮想基底クラス

前々回の講義では派生と継承の説明にて、次のようなプログラムを説明した。

派生と継承の復習

// 基底クラス
class Person {
private:
   char name[ 20 ] ;
   int  age ;
public:
   Person( const char* s , int a ) {
      strcpy( name , s ) ;
      age = a ;
   }
   void print() {
      printf( "%s %d\n" , name , age ) ;
   }
} ;
// 派生クラス
class Student : public Person {
private:
   char dep[ 10 ] ;
   int  grade ;
public:
   Student( const char* s , int a , const char* d , int g )
     : Person( s , a )
   {
      strcpy( dep , d ) ;
      grade = g ;
   }
   void print() { // 継承でなくStudent版のprintを定義
      Person::print() ;
      printf( "= %s %d\n" , dep , grade ) ;
   }
} ;
int main() {
   // 派生と継承の確認
   Person tsaitoh( "tsaitoh" , 50 ) ;
   tsaitoh.print() ;
   Student naka( "naka" , 22 , "PS" , 2 ) ;
   naka.print() ; // Student版を実行
   // 異なる型のデータをごちゃ混ぜにできないか?
   Person* table[2] ;
   table[0] = &tsaitoh ;
   table[1] = &naka ;
   for( int i = 0 ; i < 2 ; i++ ) {
      table[i]->print() ;
   }
   return 0 ;
}

このプログラムのmain() では、tsaitohとnakaのデータを 表示させているが、tsaitohとnakaは、オマケの有る無しの違いはあっても、 元をたどれば、Person型。ごちゃまぜにできないだろうか?

int main() {
   :
   // 異なる型のデータをごちゃ混ぜにできないか?
   Person* table[2] ;
   table[0] = &tsaitoh ;
   table[1] = &naka ;
   for( int i = 0 ; i < 2 ; i++ ) {
      table[i]->print() ;
   }
   return 0 ;
}

C++では、派生クラスは格下げして基底クラスのように振る舞うこともできる。 上記の例では、Studentのポインタ型である、&nakaは、 Personのポインタ型として振る舞うこともできる。

ただし、table[]に格納する時点で、Person型へのポインタなので、 table[i]->print() を呼び出しても、Personとして振る舞うため、 tsaitoh 50 / naka 22 が表示される。

仮想関数

上記のプログラムでは、Student型には、名前,年齢,所属,学年をすべて表示する Student::print() が宣言してある。 であれば、「naka 22」 ではなく、「naka 22 PS 2」と表示したくはならないか?

上記の例では、table[] に格納する段階で、格下げされているため、「naka 22」しか表示できない。 しかし、以下のように書き換えると、「naka 22 PS 2」と表示できる。

// 基底クラス
class Person {
private:
   char name[ 20 ] ;
   int  age ;
public:
   Person( const char* s , int a ) {
      strcpy( name , s ) ;
      age = a ;
   }
   virtual void print() {
      printf( "%s %d\n" , name , age ) ;
   }
} ;
// 派生クラス
class Student : public Person {
private:
   char dep[ 10 ] ;
   int  grade ;
public:
   Student( const char* s , int a , const char* d , int g )
     : Person( s , a )
   {
      strcpy( dep , d ) ;
      grade = g ;
   }
   virtual void print() { // 継承でなくStudent版のprintを定義
      Person::print() ;
      printf( "= %s %d\n" , dep , grade ) ;
   }
} ;
int main() {
   Person tsaitoh( "tsaitoh" , 50 ) ;
   Student naka( "naka" , 22 , "PS" , 2 ) ;
   // 異なる型のデータをごちゃ混ぜにできないか?
   Person* table[2] ;
   table[0] = &tsaitoh ;
   table[1] = &naka ;
   for( int i = 0 ; i < 2 ; i++ ) {
      table[i]->print() ;
   }
   return 0 ;
}

上記の例の用に、printメソッドに virtual 宣言を追加すると、 C++では、tsaitoh,naka のデータ宣言時に、データ種別情報が埋め込まれる。 この状態で、table[i]->print() を呼び出されると、 データ種別毎のprint()を呼び出してくれる。 (この実装では、前回授業の関数ポインタが使われている)

ここで、学生さんから、「table[] の宣言をStudentで書いたらどうなるの?」という 質問がでた。おもわず『良い質問ですねぇ〜』と池上彰ばりの返答をしてしまった。

Student* table[] ;
table[0] = &tsaitoh ; // 文法エラー
// 派生クラスは基底クラスにはなれるけど、
// 基底クラスが派生クラスになることはできない。
table[1] = &naka ;

純粋仮想基底クラス

// 純粋仮想基底クラス
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\n" , data ) ;
   }
} ;
// 文字列の派生クラス
class StringObject : public Object {
private:
   char data[ 100 ] ;
public:
   StringObject( const char* s ) {
      strcpy( data , s ) ;
   }
   virtual void print() {
      printf( "%s\n" , data ) ;
   }
} ;
// 実数の派生クラス
class DoubleObject : public Object {
private:
   double data ;
public:
   DoubleObject( double x ) {
      data = x ;
   }
   virtual void print() {
      printf( "%lf\n" , data ) ;
   }
} ;
// 動作確認
int main() {
   Object* data[3] = {
      new IntObject( 123 ) ,
      new StringObject( "abc" ) ,
      new DoubleObject( 1.23 ) ,
   } ;
   for( int i = 0 ; i < 3 ; i++ ) {
      data[i]->print() ;
   }
   return 0 ;
} ;

この書き方では、data[]には、整数、文字列、実数という異なるデータが入っているが、 Objectという純粋仮想基底クラスを通して、共通な型のように扱えるようになる。 そして、data[i]->print() では、各型の仮想関数が呼び出されるため、 「123 abc 1.23」 が表示される。

ここで、Object の様な一見すると中身が何もないクラスを宣言し、 このクラスから様々な派生クラスを用いるプログラムテクニックは、 広く利用され、Objectのような基底クラスは、純粋仮想基底クラスなどと呼ばれる。

関数ポインタと仮想関数への導入

派生と継承の基本の説明をしてきたので、次に説明する予定の仮想関数の導入として、 実装のために使われている関数ポインタを紹介。

関数ポインタ

関数ポインタとは、関数へのポインタであり、ポインタを変更することで、 処理を切り替えるために使われる。 まずは、動作説明の簡単なプログラムを紹介。

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を表示
}

関数ポインタを利用すれば、異なるデータに対する処理を、 汎用性高く作ることも可能となる。 例えば以下の vmax() 関数は、自分で用意した大小を比較するだけの関数を渡し、 それ以外の最大値を求めるための処理を行う。 このため、他のデータの最大値を求めたい場合でも、最大値を求める処理を すべて記載するのではなく、対象データの比較関数だけを記述すれば良い。

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 ] ) ;
}

6/1よりOffice365ProPlusが使えます

高専機構とMicrosoft社との包括協定ですが、この6/1より本校学生の方は、 Office365 ProPlus が使えます。

Office 365 を使うと、 (1)メールやグループウェアとしての機能と、 (2)パソコンなどにアプリを入れることでWord,Excel,PowerPointなどが 使えるようになります。 今までは、Microsoftとの包括協定で売店でOSやOfficeのメディアを 安く購入し、学生さんの自宅パソコンにインストールが可能でした。 しかし、今後はこのOffice365によって、利用することになります。

Office365のアプリは、パソコン以外にも、iOSやAndroidのタブレット,スマート フォンでも提供されています。 Office365 ProPlus では、パソコン5台,タブレット5台,スマホ5台合計15台にイ ンストールできます。 データは、ユーザ認証のもとでOneDriveなどの機能を経由しながら、同期されます。

使い方

Office 365 をブラウザ上で使うには、https://portal.office.com/Home にアクセスし、 高専機構全体でのメールアドレスでloginします。 福井高専の方は、総合情報処理センターで利用するユーザIDの後ろに、"@fukui.kosen-ac.jp"をつけたものが、 Office365のメールアドレスになります。パスワードは、センターの端末と同じものを使ってください。

  login: xxxxxxxx@fukui.kosen-ac.jp
  password: センターと同じ
1505271953_806x538.png

ブラウザ上では、以下のような機能が利用できます。

1505271953_873x359.png

先に示した、"xxxxxxxx@fukui.kousen-ac.jp" は、そのままメールアドレスとしても使えます。 左上のメニューボタンを押すと表示されるものの、「メール」を使うと以下のような画面になります。

1505271953_997x371.png

パソコンにOffice365のインストール

高専機構でOffice365では、ブラウザ上だけでなくパソコンにOfficeをインストールして使うこともできます。
自宅の自分のパソコンで、前述の手順によりブラウザでログインすると、直後に以下の様な画面が表示されます。 この状態で「今すぐインストール」をクリックして下さい。

2015-06-29-Office365-install.png

ブラウザ上のOffice365の機能を使っている状態では、ブラウザの画面上部の「アカウント」→「Office365の設定」→「ソフトウェア」 でインストール画面を出してください。

ここで、インストールを選ぶと、画面下に「Setup…..exe(xxxxMB)を実行または保存しますか?」と表示が出てくるので、「実行(R)」を選んでください。 Windowsであれば、「次のプログラムにこのコンピュータへの変更を許可しますか?」などの表示が出てきます。 あとは「はい(Y)」を選べばインストールが行われます。

2015-06-29-Office365-install-final.png

派生と継承

隠ぺい化の次のステップとして、派生・継承を説明する。

派生を使わずに書くと…

元となるデータ構造(例えばPersonが名前と年齢)でプログラムを作っていて、 途中でその特殊パターンとして、所属と学年を加えた学生(Student)という データ構造を作るとする。

// 元となる構造体(Person)                                                                                                                                                  struct Person {
   char name[ 20 ] ; // 名前
   int  age ;        // 年齢
} ;
// 初期化関数
void set_Person( struct Person* p ,
                 char s[] , int x ) {
   strcpy( p->name , s ) ;
   p->age = x ;
}
// 表示関数
void print_Person( struct Person* p ) {
   printf( "%s %d\n" , p->name , p->age ) ;
}
void main() {
   struct Person saitoh ;
   set_Person( &saitoh , "t-saitoh" , 50 ) ;
   print_Person( &saitoh ) ;
}

パターン1(そのまんま…)

上記のPersonに、所属と学年を加えるのであれば、以下の方法がある。 しかし以下パターン1は、要素名がname,ageという共通な部分があるようにみえるが、 プログラム上は、PersonとPersonStudent1は、まるっきり関係のない別の型にすぎない。

このため、元データと共通部分があっても、同じ処理を改めて書き直しになる。

// 元のデータに追加要素(パターン1)
struct PersonStudent1 {
   char name[ 20 ] ; // 名前
   int  age ;        // 年齢
   char dep[ 20 ] ;  // 所属
   int  grade ;      // 学年
} ;
void set_PersonStudent1( struct PersonStudent1* p ,
                         char s[] , int x ,
                         char d[] , int g ) {
   strcpy( p->name , s ) ; // 同じことを書いてる
   p->age = x ;
   strcpy( p->dep , d ) ;  // 追加分はしかたない
   p->grade = g ;
}
// 名前と年齢だけ表示
void print_PersonStudent1( struct PersonStudent1* p ) {
   // また同じ処理を書いてる
   printf( "%s %d\n" , p->name , p->age ) ;
}
void main() {
   struct PersonStudent1 naka1 ;
   set_PersonStudent1( &naka1 ,
   "naka" , 22 , "PS" , 2 ) ;
   print_PersonStudent1( &naka1 ) ;
}

パターン2(元データの処理を少し使って…)

パターン1では、同じような処理を何度も書くことになり、面倒なので、 元データ用の関数をうまく使うように書いてみる。

// 元のデータに追加要素(パターン2)
struct PersonStudent2 {
   struct Person person ;
   char          dep[ 20 ] ;
   int           grade ;
} ;
void set_PersonStudent2( struct PersonStudent2* p ,
                         char s[] , int x ,
                         char d[] , int g ) {
   // Personの関数を部分的に使う
   set_Person( &(p->person) , s , x ) ;
   // 追加分はしかたない
   strcpy( p->dep , d ) ;
   p->grade = g ;
}
void print_PersonStudent2( struct PersonStudent2* p ) {
   // Personの関数を使う。
   print_Person( &p->person ) ;
}
void main() {
   struct PersonStudent2 naka2 ;
   set_PersonStudent2( &naka2 ,
   "naka" , 22 , "PS" , 2 ) ;
   print_PersonStudent2( &naka2 ) ;
}

このパターン2であれば、元データ Person の処理をうまく使っているので、 プログラムの記述量を減らすことはできるようになった。

しかし、print_PersonStudent2() のような処理は、元データ構造が同じなのに、 いちいちプログラムを記述するのは面倒ではないか?

そこで、元データの処理を拡張し、処理の流用ができないであろうか?

基底クラスから派生クラスを作る

オブジェクト指向では、元データ(基底クラス)に新たな要素を加えたクラス(派生クラス)を 作ることを「派生」と呼ぶ。派生クラスを定義するときは、クラス名の後ろに、 「:」「public/protected/private」基底クラス名を書く。

// 基底クラス
class Person {
private:
   char name[ 20 ] ;
   int  age ;
public:
   Person( const char s[] , int x ) {
      strcpy( name , s ) ;
      age = x ;
   }
   void print() {
      printf( "%s %d\n" , name , age ) ;
   }
} ;
// 派生クラス
class Student : public Person {
private:
   char dep[ 20 ] ;
   int  grade ;
public:
   Student( const char s[] , int x ,
            const char d[] , int g )
            : Person( s , x ) // 基底クラスのコンストラクタ
   {
      strcpy( dep , d ) ;
      grade = g ;
   }
} ;
void main() {
   Person saitoh( "t-saitoh" , 50 ) ;
   saitoh.print() ;
   Student naka( "naka" , 22 , "PS" , 2 ) ;
   naka.print() ;
}

ここで注目すべき点は、main()の中で、Studentクラス"naka"に対し、naka.print() を呼び出しているが、パターン2であれば、print_PersonStudent2()に相当するプログラムを 記述していない。 しかし、この派生を使うと Person の print() が自動的に流用することができる。 これは、基底クラスのメソッドを「継承」しているから、 このように書け、名前と年齢「naka 22」が表示される。

さらに、Student の中に、以下のような Student 専用の新しい print()を記述してもよい。

class Student ...略... {
   ...略...
   void print() {
      Person::print() ;
      printf( "%s %d\n" , dep , grade ) ;
   }
} ;
void main() {
   ...略...
   Student naka( "naka" , 22 , "PS" , 2 ) ;
   naka.print() ;
}

この場合は、継承ではなく機能が上書き(オーバーライト)されるので、 「naka 22 / PS 2」が表示される。

派生クラスを作る際の後ろに記述した、public は、他にも protected , private を 記述できる。

public    だれもがアクセス可能。
protected であれば、派生クラスからアクセスが可能。
派生クラスであれば、通常は protected で使うのが一般的。
private   派生クラスでもアクセス不可。

数値の範囲とトラブル事例

先日の数値の範囲の説明で、浮動小数点型(float,double)などについても説明を行う。

16bitコンピュータの時代…

簡単な桁あふれの事例として、古いコンピュータ16bitの時代の事例。

// int は16bitとする。
// パソコン画面上の2点の距離を計算したい。
int x1 , y1 ;
int x2 , y2 ;
int r = (int) sqrt( (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) ) ;

このプログラムは、負の値の平方根を求めた…というエラーメッセージで止まってしまう。

これは、座標が200ドット離れていれば、2乗の和を求めた段階で、40000となるが、 16bit 符号付き整数であれば、最大数が32767 なので桁あふれして、負の数として扱われている。

2038年問題

次に、20XX年問題の説明。 2000年問題とは、古いプログラムで西暦末尾2桁で年を扱っていたプログラムが誤動作を 起こす可能性の問題。

同じように数値の範囲が原因となるものとして、2038年問題を紹介する。 OS unix では、時間を1970年からの経過秒数で表す。 この時、32bit(符号付きint型)であれば、2^31-1が扱える最大数であり、 (2^31-1) / (3600*24*365.22)を求めると、約68年となる。 つまり、32bit int であった場合は、2038年以降は桁あふれで時間を正しく扱えなくなる可能性がある。

しかし、計算の途中の段階で桁あふれして動かない事例はある。 以下の事例は、2004年に実際に発生したトラブルと同様の簡単なコードである。

// intは32bitとする
// 2つの時間の中央時間を計算するコード
int t1 , t2 ;
int tm = (t1 + t2) / 2 ;

32bitの時間情報では、2004年以降は、 t1,t2の上位ビットが01XX……となっているため、 t1+t2 を計算すると最上位ビット(符号ビット)が1となり この後の計算は、負の値として扱われた。

上記のコードは、次のように記述するか、64bit化されたtime_t型で 記述すべきである。

// 32bitでも動く
int tm = t1 + (t2 - t1) / 2 ;
// 64bitのtime_t型
time_t t1 , t2 ;
time_t tm = (t1 + t2) / 2 ; // △
time_t tm = t1 + difftime( t2 , t1 ) / 2 ; // ◎

248日問題

丁度ニュースで、「ボーイング787が248日以上連続稼働すると、電源停止で制御不能となる」というネタが 紹介されている。点検などがあるから、実際はあり得ない状況だとは思えるが、 これもint型の桁あふれが原因らしい。 あり得ないとはいえ、フライバイワイヤ(操縦桿を電気信号で制御)の最新機は、電源が落ちれば即墜落。 これは、10msec単位で、OSの経過時間をint型(32bit)で制御しているのが 原因らしい。10msec*(2^31)=248日で桁あふれが発生して、電源稼働経過時間を誤認するらしい。 これと同様の事象は、Windows Xpなどが出た頃にも発生している。 787ほど深刻な墜落はないにしても、サーバ機なら248日稼働ぐらいはよくある話。

浮動小数点型と演算順位の問題

float型やdouble型は、浮動小数点で実数の小さな値から大きな値まで扱える。


しかしながら、プログラム中の式の順序によっては、注意が必要となる。

int a[ 5 ] = { 10 , 10 , 10 , 10 , 11 } ;
int s = 0 , size = 5 ;
for( int i = 0 ; i < size ; i++ )
s += a[ i ] ;
printf( "%lf¥n" , s / size ) ;
// × %lf はdouble型、s/size はint型で異常な値が表示される。
printf( "%lf¥n" , (double)( s / size ) ) ;
// × s/sizeはint型で、小数点以下が求まらない。
printf( "%lf¥n" , (double)s / (double)size ) ;
// ◎
#define PI 3.141592
double x ;
for( x = 0.0 ; x < 2*PI ; x += (1/6) * PI ) {
printf( "%lf %lf¥n" , x , sin( x ) ) ;
}

このプログラムは、1/6が整数型で行われるため(つまり0とPIを掛ける)、 xの値が変化しない。 これは、"x += (1.0/6.0) * PI" と書かなければならない。

隠蔽化の課題(複素数クラスを例に)

前回の隠蔽化の話を受け、実際のプログラムの例を課題に説明。 複素数クラスを(実部,虚部)で実装した後に、(絶対値,偏角)に直したら…

基本プログラム(実部と虚部)

複素数を扱うクラスを作るのであれば、基本的には以下の様なコードとなるだろう。 複素数どうしの簡単な加算・乗算を記載する。

class Complex {
private:
   double re , im ;
public:
   Complex( double x , double y ) {
      re = x ;
      im = y ;
   }
   // 上記コンストラクタは、以下のようにも書ける。
   // Complex( double x , double y )
   // :   re( x ) , im( y )
   // { メンバ以外の初期化... }
   void print() {
      printf( "%lf+j%lf¥n" , re , im ) ;
   }
   void add( Complex &z ) {
      re = re + z.re ;
      im = im + z.im ;
   }
   void mul( Complex &z ) {
      double x = re * z.re - im * z.im ;
      double y = re * z.im + im * z.re ;
      re = x ;
      im = y ;
   }
} ;
int main() {
   Complex a( 1 , 2 ) ;
   Complex b( 2 , 3 ) ;
   a.add( b ) ;
   a.print() ;
   a.mul( b ) ;
   a.print() ;
   return 0 ;
}

Complexクラス内部をリファクタリング

しかし、前述プログラムでは、mul()メソッドは、add()メソッドよりは、 複雑なものとなっている。 しかし、複素数の乗算は、(絶対値と偏角)を用いれば、絶対値の乗算・偏角の加算で 処理は簡単に記述できる。そこで、クラス内部を乗算と偏角で処理をするように変更してみる。

class Complex {
private:
   double r , th ;
public:
   Complex( double x , double y ) {
      r = sqrt( x*x + y*y ) ;
      th = atan2( y , x ) ; // atan2は象限を考慮してくれる
   }
   void print() {
      printf( "%lf ∠ %lf¥n" , r , th / 3.141592 * 180.0 ) ;
   }
   void add( Complex &z ) {
      // ここは面倒な式になっちゃう
   }
   void mul( Complex &z ) {
      r  = r  * z.r ;
      th = th + z.th ;
   }
} ;
int main() {
   Complex a( 1 , 2 ) ;
   Complex b( 2 , 3 ) ;
   a.add( b ) ;
   a.print() ;
   a.mul( b ) ;
   a.print() ;
   return 0 ;
}

ここで重要なポイントは、2つめの絶対値∠偏角のプログラムの呼び出し側 main() は、 1つめのプログラムとまるっきり同じである。

このように、オブジェクト指向の隠蔽化を行っていれば、当初のクラス設計が悪くて後で変更 したくなった場合、利用者側からの見た目の動作を変更せずに、内部のデータ構造や処理メソッドを 変更が可能となる。 このように、利用者側からの見た目を変更せずに処理の内部を変更すること、 リファクタリング と呼ぶ。これにより、プログラムの不備や問題点があっても、積極的にプログラムを 改良できることから、不備の少ない安全なプログラムを作れるようになる。

隠蔽化の課題

以上の2つのプログラムで複素数の計算メソッド、加算(add),除算(sub),乗算(mul),除算(div)…その他を (実部,虚部)、(絶対値,偏角)で記載し、適切に記述をすれば、呼び出し側main()を まるっきり同じものにできることを通して、隠蔽化についてレポートにまとめよ。

レポートでは、以下の点を記載すること。(レポートは、本科中間試験の頃までに提出が望ましい)

  • 2つの方式でのプログラム例
  • 上記プログラムに対する説明
  • 上記プログラムが正しく動作していたことが判る結果
  • この課題から判る考察

パスワードの有効期限…

福井高専の様々なWebシステムを利用している場合、 パスワードなどの認証は、UnifIDone というシステムに 統合されています。

1504301038_957x368.png

このシステムでは、安易なパスワード管理による不正アクセス を防止するために、パスワードの有効期限が400日に設定されています。

このため、年度が改まる頃には、システムにアクセスする際には、 パスワード有効期限の警告が表示されます。 この際には、パスワードの変更をお願いします。

教職員の場合には、UnifIDone のパスワード変更へのリンクは、 グループウェア Garoon の右下にあるリンク集の所に、 「パスワード変更」のリンクがあります。

1504301038_369x386.png

学生の場合には、ブラウザのブックマーク先頭に、 「パスワード変更」が登録されています。

Debian 8.0/Jessie のインストール

Debian 8.0 / Jessie が stable となって配布が始まったので、 まずは影響の少ないサーバに入れてみる。 自宅や自室のサーバは、すでに Jessie に切り替え済みだけど、 久々だと、なかなか手間取る。

# aptitude remove `dpkg -l | awk '{print $2}' | egrep -e ^kde-`
# aptitude remove `dpkg -l | awk '{print $2}' | egrep -e ^gnome-`

あたりを実行して、依存の原因となりそうなパッケージはひとまず 減らしてから、

# aptitude update ; aptitude full-upgrade

ただし、処理中に libxml-libxml-perl が更新前の削除でエラーが発生し 手間取ってしまった。パッケージの依存情報の微妙なエラーがあるようだ。 この次に学科のメインサーバの更新となるが、 その際には、一番最初に削除してからにしよう。

再起動させたら失敗

更新は上手くいったが、翌朝再起動をかけたら起動しない。 Debian/Jessie/netinst のCDを作り、リカバリーモードで起動させる。

起動時には、ブートローダーで linux-image-2.16 なんかを探しているので、 古いブートローダーを使おうとしている。どうも、grub2のインストールで失敗していた様子。 以下のコマンドで、インストールを確認し再起動。

# aptitude install grub2
# grub-install /dev/sda
# update-grub

更新の最終段階は、夜中に家からの作業だったけど、reboot は翌朝にして正解だった…(x_x;

システム

最新の投稿(電子情報)

最近の投稿(斉藤 徹)

アーカイブ

カテゴリー