mimetex.cgi は便利
学科のWebサーバには、mimetex.cgi というパッケージを入れてある。これを使えば、img タグの src 部分に、mimetex.cgi を記述し、パラメータのURL部分に、の数式を書くだけ。
(( mimetex.cgi のインストール )) $ sudo aptitude install mimetex
なので、普通にテキストで書いたら、O(N log N) みたいな式でも、 なんて記述も、 で数式書くのに慣れてれば、楽々。
(( HTML の中で数式画像の img タグを書く )) <p align="center"> <img src="/cgi-bin/mimetex.cgi ?\int{\frac{1}{\sqrt{a^2-x^2}}dx} = \sin^{-1}\frac{x}{a}+C" /><p>
iPadでlinux仕事-Shelly Pro
仕事用の Surface Pro のキーボードがクソで、「T」の反応がダメダメなので、Bluetooth キーボードを購入。以前に、iOS,Windowsといった複数接続に切り替え可能で、接続相手に合わせてキーアサインも変更してくれる小型キーボードを購入していたけど、小さすぎてメールを打ったり、Linux サーバを触るときに、思うように打てずあんまり使わなくなっていた。
今回、Logicool の K380 を購入したけど、Mac のキーボードと同じ配列で複数のBluetooth接続を切り替えられるもの。OSに合わせてキーアサインを切り替える機能はない。
やはり、いつも使っているキーボードと同じなので、手に馴染む。それならばということで、iOS に ssh ターミナルソフト入れたら、Linux 仕事で違和感なく使えるか試したくなった。
日本語が使えるiOS用sshクライアント
以前に、ある程度日本語が使えるということで Termius という無料アプリを使っていたけど、emacs で漢字をつかっていると、文字位置がずれるので今ひとつ。
今回、”iOS ssh 日本語”で検索をかけたら、この記事が比較や有料ソフトの値段なども記載してあってわかりやすかった。
最終的に入れたのは、Shelly Pro となりました。
実際に、ベッドで寝ながら複数のサーバ更新作業をやったけど、Ctrl-Caps交換ができないし、iPad は記号が英字キーボードの配置なので、キートップを確認しながらになったけど、まあまあ使える。
K380 には、iOS のキー配置も刻印されているので、便利。
ただし、iPad に入れて、”Pro” にアプリ内課金でバージョンアップ(¥600)したけど、iPhone に 同じく Shelly を入れたら、バージョンアップしようとしたら、¥480 の課金の画面が出てきた。値段も違うし、タブレット版とスマホ版は別物扱いしてるんだろうなぁ。
次の出張では、iPad mini とキーボードだけ…を試そうかな。
高専ライブ:2018年5月27日(第576回)
収録の模様をお送りいたしました。
- 1年生への質問コーナー
- サイエンス共和国 第31回「iPhoneで儲ける技術者の話 その4 iPhoneカバーガラスの話」
- 1年生のゴールデンウィークの話
担当:水島(4C,MC)、西島(4EI,MIX)、吉田(F2)、武村(F3)、馬淵(F5)、中村(教員)
派生と継承
隠ぺい化の次のステップとして、派生・継承を説明する。
派生を使わずに書くと…
元となるデータ構造(例えば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 yama1 ; set_PersonStudent1( &yama1 , "yama" , 22 , "PS" , 2 ) ; print_PersonStudent1( &yama1 ) ; }
パターン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 yama2 ; set_PersonStudent2( &yama2 , "yama" , 22 , "PS" , 2 ) ; print_PersonStudent2( &yama2 ) ; }
このパターン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 yama( "yama" , 22 , "PS" , 2 ) ; yama.print() ; }
ここで注目すべき点は、main()の中で、Studentクラス”yama”に対し、yama.print() を呼び出しているが、パターン2であれば、print_PersonStudent2()に相当するプログラムを 記述していない。 しかし、この派生を使うと Person の print() が自動的に流用することができる。 これは、基底クラスのメソッドを「継承」しているから、 このように書け、名前と年齢「yama 22」が表示される。
さらに、Student の中に、以下のような Student 専用の新しい print()を記述してもよい。
class Student ...略... {
...略...
void print() {
Person::print() ;
printf( "%s %d\n" , dep , grade ) ;
}
} ;
void main() {
...略...
Student yama( "yama" , 22 , "PS" , 2 ) ;
yama.print() ;
}
この場合は、継承ではなく機能が上書き(オーバーライト)されるので、 「yama 22 / PS 2」が表示される。
派生クラスを作る際の後ろに記述した、public は、他にも protected , private を 記述できる。
public だれもがアクセス可能。 protected であれば、派生クラスからアクセスが可能。 派生クラスであれば、通常は protected で使うのが一般的。 private 派生クラスでもアクセス不可。
仮想関数への伏線
上記のような派生したプログラムを記述した場合、以下のようなプログラムでは何が起こるであろうか?
class Student ... { : void print() { Person::print() ; // 名前と年齢を表示 printf( " %s %d¥n" , dep , grade ) ; // 所属と学年を表示 } } ; void main() { Person saitoh( "t-saitoh" , 53 ) ; saitoh.print() ; // t-saitoh 53 Student mitsu( "mitsuki" , 18 , "E" , 4 ) ; Student ayuka( "ayuka" , 16 , "EI" , 2 ) ; mitsu.print() ; // mitsuki 18 / E 4 名前,年齢,所属,学年を表示 ayuka.print() ; // ayuka 16 / EI 2 名前,年齢,所属,学年を表示 Person* family[] = { &saitoh , &mitsu , &ayuka , // 配列の中に、Personへのポインタと } ; // Studentへのポインタが混在している // 派生クラスのポインタは、 // 基底クラスのポインタとしても扱える for( int i = 0 ; i < 3 ; i++ ) family[ i ]->print() ; // t-saitoh 53/mitsuki 18/ayuka 16 } // が表示される。
様々なメモリ確保
前回の授業で説明していたような、必要に応じて確保するメモリは、動的メモリと呼ばれる。
動的メモリも、局所変数やalloca()を用いたスタック領域と、malloc()とfree()を使うヒープメモリ領域に分類される。
strdup
前回の文字列の確保の説明では、malloc()とstrcpy()をあわせて実行していたが、C言語ではこういった処理が多いので、専用の関数 strdup() がある。
char str[] = "abcdefg" ; char*pc ; if ( (pc = (char*)malloc( strlen( str ) + 1 )) != NULL ) { strcpy( pc , str ) ; } // おなじことを strdup では... pc = strdup( str ) ;
様々なメモリ確保
自分で定義した構造体を、malloc で領域確保しながら使う場合、1次元配列や2次元配列を作る場合、色々な確保の方法がある。
// 複素数を例に struct Complex { double re ; double im ; } ; // 基本 struct Complex a ; a.re = 1.0 ; a.im = 2.0 ; // ポインタで確保 struct Complex* b ; b = (struct Complex*)malloc( sizeof( struct Complex ) ) ; if ( b != NULL ) { b->re = 1.0 ; b->im = 2.0 ; } // 一次元配列 struct Complex c[ 2 ] ; // 通常の使い方 c[0].re = 2.0 ; c[0].im = 3.0 ; c[1].re = 4.0 ; c[1].im = 5.0 ; // 一次元配列を動的に確保 struct Complex* d ; // Complexの配列 d = (struct Complex*)malloc( sizeof( struct Complex ) * 2 ) ; if ( d != NULL ) { d[0].re = 2.0 ; d[0].im = 3.0 ; d[1].re = 4.0 ; d[1].im = 5.0 ; } // 一次元のポインタ配列 struct Complex* e[ 2 ] ; // Complexのポインタの配列 e[0] = (struct Complex*)malloc( sizeof( struct Complex ) ) ; if ( e[0] != NULL ) { e[0]->re = 2.0 ; e[0]->im = 3.0 ; } e[1] = (struct Complex*)malloc( sizeof( struct Complex ) ) ; if ( e[1] != NULL ) { e[1]->re = 4.0 ; e[1]->im = 5.0 ; }
C++での new, delete 演算子
複雑なデータ構造のプログラムを作成する場合には、このような malloc() , free() をよく使うが煩雑であるため、C++ではこれらをすっきりと記述するために、new 演算子、delete 演算子があり、それぞれ malloc(), free() に相当する。
// 単独 Complex* b = new Complex ; b->re = 1.0 ; b->im = 2.0 ; delete b ; // 配列 Complex* d = new Complex[2] ; d[0].re = 2.0 ; d[0].im = 3.0 ; d[1].re = 4.0 ; d[1].im = 5.0 ; delete[] d ; // 配列のdeleteには[]が必要 // ポインタの配列 Complex* e[2] ; e[0] = new Complex ; e[0]->re = 2.0 ; e[0]->im = 3.0 ; e[1] = new Complex ; e[1]->re = 4.0 ; e[1]->im = 5.0 ; delete e[0] ; delete e[1] ;
2次元配列
2次元配列の扱いでも、注意が必要。
int cs = 何らかの値 ; // データ列数 int rs = 何らかの値 ; // データ行数 int a[ rs ][ cs ] ; // C言語ではエラー a[ y ][ x ] = 123 ; // 1次元配列を2次元配列のように使う int* b ; b = (int*)malloc( sizeof( int ) * rs * cs ) ; b[ y * cs + x ] = 123 ; // b[ y ][ x ] への代入 // 配列へのポインタの配列 int** c ; c = (int**)malloc( sizeof( int* ) * rs ) ; // NULLチェック省略 c[0] = (int*)malloc( sizeof( int ) * cs ) ; c[1] = (int*)malloc( sizeof( int ) * cs ) ; : c[ y ][ x ] = 123 ;
レポート課題
メモリの動的確保の理解のために、自分の理解度に応じて以下のプログラムのいずれかを作成せよ。
ただし、プログラム作成時には、配列サイズは未定で、プログラム起動時に配列サイズを入力するものとする。
- 固定長の名前で、人数が不明。
- 長い名前かもしれない名前で、人数も不明
- 複素数のデータで、データ件数が不明。
- 名前と電話番号のデータで、データ件数が不明。
このような状況で、データを入力し、検索などの処理を通して自分のプログラムが正しく動くことを検証せよ。
レポートには、プログラムリスト、プログラムの説明、動作確認した結果、考察を記載すること。
C++のvectorクラスを使ったら
// C++であればvectorクラスを使えば配列なんて簡単 #include <vector> int main() { // 1次元配列 std::vector<int> a( 10 ) ; for( int i = 0 ; i < 10 ; i++ ) a[ i ] = i ; // 2次元配列 std::vector< std::vector<int> > b( 9 , std::vector<int>(9) ) ; // ↑ ここの空白は重要 for( int i = 0 ; i < 9 ; i++ ) { // ">>" と書くとシフト演算子 for( int j = 0 ; j < 9 ; j++ ) { // "> >" と書くと2つの">" b[i][j] = (i+1) * (j+1) ; } } return 0 ; }
高専プロコン2018/校内審査
今年度の高専プロコンの応募時期を控え、4EI前期の創造工学演習で作品づくりや、卒業研究のグループからの参加希望の資料をみて、今年度の学内審査を行いました。
課題部門
サバ×サバ(,指導:村田)
- 村田研5年からエントリー
- スマホのカメラ・音楽・SNS情報から好みに応じて旅行コースを作る。
Cloudraw(石井,寺本,兵田,鷲田,指導:斉藤)
- 見上げた空に仮想的な雲を作り、空にお絵かきする。
- 提出資料の完成度をあげる必要あり。
自由部門
chair CAM(京藤,玉村,辻野,中村,幅岸,木村,指導:高久)
- お店の空き情報をLINE botで返答するシステム。
- 店内に設置するセンサー系の具体性の追記が必要
ポーズでプログラミング(大瀬,奥村,道関,向井,村上,指導:小松)
- Kinectでポーズを認識させ、そのポーズを並べることでプログラミング
- プログラミングで動くオブジェクトがもうひと工夫ほしい。
競技部門
(永田,谷川,河野,指導:村田)
- 具体的な計算方法の説明が不足
H30年度秋季・情報処理技術者試験
情報処理技術者試験
情報処理技術者試験開催の案内が届きました。
試験実施日 | 平成30年10月21日(日) |
受験手数料 | 5,700円 |
試験区分 | 基本情報処理技術者試験(FE) 応用情報処理技術者試験(AP),ほか |
願書受付期間 | 個人申込:7月5日(木) インターネット申込みができます。 |
ITパスポート試験
ITパスポート試験は、コンピュータを用いたCBT方式で、随時申込ができます。
情報処理技術者試験の中で、初心者向けの試験ですので、ぜひともトライしてみましょう。
高専ライブ:2018年5月20日(第575回)
- 三国まつりの話
- 暑い日が続いた話
- サイエンス共和国 第30回「iPhoneで儲ける技術者の話 その3 iPhoneに使われる部材の話」
- 電気自動車の話
担当:西野(2E,MC)中島(4C,MIX),椎林(2B)、西(教員)
D/A変換回路とA/D変換回路
小型コンピュータを使った制御では、外部回路に指定した電圧を出力(D/A変換)したり、外部の電圧を入力(A/D変換)したりすることが多い。以下にその為の回路と動作について説明する。
D/A変換回路
ラダー抵抗回路によるD/A変換の仕組みを引用
このような回路で、D0,D1,D2 は、デジタル値の0=0[V] , 1=5[V] であった場合、Output 部分の電圧は、(D0,D1,D2)の値が、(0,0,0),(0,0,1),…(1,1,1)と変化するにつれ、5/8[V]づつ増え、(1,1,1)で 5*(7/8)=4.4[V]に近づいていく。Output が出力によって電圧が変化しないように、アンプで増幅する。
DCモータをアナログ量で制御しないこと
このように、電圧をコンピュータから制御するようになると、ロボットで模型用の直流モータの回転速度をこれで制御したい…と考えるかもしれない。
しかし、直流モータは、コイル(電磁石)は単なる導線である。例えば、小さい電流で遅い速度でモータを回転させようとすると、小さい電圧でも導線(抵抗はほぼ0[Ω])には大量の電流が流れる。
PWM変調
こういう場合には、PWM変調(Pulse Width Modulation) を行う。
このような波形であれば、低速度でも高トルクが得られる。
A/D変換回路
D/A変換とは逆に、アナログ量をデジタル値に変換するには、どのようにするか?
このような場合には、A/D変換回路を用いる。一般的な回路では、以下のような逐次比較型A/D変換を用いる。
この回路では、変換開始と共に入力値をサンプル保持回路でアナログ量を保存する。
その後、Registerの中のデジタル値を、D/A 変換回路でアナログ量に変換した結果を、比較器(Comparator)でどちらが大きいか判断し、その結果に応じて2分探索法とかハイアンドローの方式のように、比較を繰り返しながらデジタル値を入力値に近づけていく。
ハイアンドロー(数あてゲーム)
数あてゲームで、デタラメな0〜127までの整数を決めて、ヒントを元にその数字を当てる。回答者は、数字を伝えると、決めた数よりHighかLowのヒントをもらえる。
最も速い回答方法は…例えば決めた数が55だとすると ・64 - Low 0------ 0..63 ・32 - High 01----- 32..63 ・48 - High 011---- 48..63 ・56 - Low 0110--- 48..55 ・52 - High 01101-- 52..55 ・54 - High 011011- 54..55 ・55 - Bingo 0110111 55確定 どんな値でも、7回(27=127)までで当てることができる。
入出力と変数・レポートNo.2
以下のような、位相が 0°,15°,30°,45° ずれた sin(x) のグラフを描くためのプログラムを作りたい。
Excel で式入力すりゃいいじゃん…というのはナシ。
0 =A1+15 =B1+15 … =A1+5 =sin((A2+B1)/180*3.141592) =sin((A2+C1)/180*3.141592) …
// 以下のプログラムは、初心者なら書きそうなボケが沢山入ってます。 // 正しく直してください。 #include <stdio.h> #define PI = 3.1415926535 ; int th ; void phase( int x ) { printf( "%d" , x ) ; // 位相を0度から45度まで15度ずつ変化させる for( th = 0 ; th <= 45 ; th += 15 ) { printf( " %d" , sin( (x + th) / 180 * PI ) ) ; } printf( "¥n" ) ; } int main() { // 角度を 0..360度 の範囲で表示 for( th = 0 ; th != 360 ; th += 5 ) { phase( th ) ; } return 0 ; }
このプログラムを正しく修正し、Excel に値を取り込んで、上図に示すようなグラフにしてください。
レポートでは、プログラムリスト、プログラムの説明、実行結果、感想・考察を記載し提出してください。