専攻科実験・コンパイラと関数電卓プログラム作成
- コンパイラの技術と関数電卓プログラム(1)
- 再帰下降パーサによる構文解析(LL法)による電卓プログラム作成
- 補助資料:コンパイラの技術と関数電卓プログラム(1-2)
- 課題
- 複数桁の数字が使えること。
- 式中に空白が使えること。
- 何らかの演算子を追加すること。
- (例) %,単項演算子のマイナスなど
- 演算子が左結合か右結合か確認すること。
- オプション課題
- 変数が使えること。
(変数名は1文字のA-Zといったもので良い)
- 変数が使えること。
- レポート内容
- コンパイラ技術の概要、課題(1)の説明・最終的なBNF記法・ソース・動作検証、考察
- 前半レポート提出先
- コンパイラの技術と関数電卓プログラム(2)
- コンパイラツールを使ったLR構文解析による電卓プログラムの作成
- 補助資料:専攻科実験: unix系 開発環境のインストール
- 課題
- 基本的に、lex+yaccで(1)と同様の課題で参考資料を元に改良を行う。
- レポート内容
- lex,yaccの概要、課題(2)の説明・ソース・動作検証、考察
- 後半レポート提出先
(2年後期実験) 簡単ゲーム作成
1.目的
簡単なゲーム作成を通して、遊びながらプログラミングすることで、スキルアップを目指す。
2.概要
簡単なゲームとして、以下の3つのゲームの中から1つを選び、リンク先の JavaScript による基本機能だけのゲームに、ルール判定・勝敗判定などの機能を組み込む。以下のリンク先の五目並べ、オセロゲームのプログラムは、2人で交互にコマをうつX,Y座標を入力しながら、コマの結果(や勝敗の結果)が表示されるものを作成する。
- 五目並べ(連珠)
- 5つ並べば勝ち…が五目並べ。これだけのルールだと、先手が有利なので先手の黒には、長連、四四、三三など禁じ手などが決められているのが連珠
- オセロゲーム
- 自分のコマで挟んだ相手の駒を裏返しで自分のコマにできる。ベースとなったリバーシを、日本のゲーム研究家の長谷川さんが改良したものがオセロゲーム。ルールが単純だけど、最後に大逆転となることも多い。
- ポーカー
- ポーカーはプレーヤーにトランプのカードを5枚づつ配り、その後にカードを入れ替えた後に、カードの役で勝敗が決まる。カジノゲームとして遊ぶ際には、役の完成度に応じて賭け(ベッド)たり、降りたりしながら遊ぶ。ゲームのやり方により、細かくルールが違ったりローカルルールを設ける場合もある。
プログラム作成の腕試しとしての難易度であれば、ポーカー(すべての役判定をするなら) > オセロ ≒ 連珠 > 五目並べ だと思われる。
もし、この3つのテーマの難易度が面白くないのであれば、三目並べでもよい。ただし人間対コンピュータであること。
3.実験方法
- 概要で示した五目並べ,オセロ,ポーカーのルールについて調べ、自分で2週の実験時間の中で、改良できそうなテーマを最初に1つ選んで下さい。
- 選んだテーマについて、概要で示したリンク先の html ファイルを自分のパソコンに保存し、エディタにてプログラム中の JavaScript の内容を理解してください。
- 以下のチェックシートのファイルをダウンロードし、シートのタブの中から自分が取り組むテーマを選んで、2週の実験の中で実装できそうな機能について、「作成の予定」の列に”〇”を入力してください。
- 実験1週目でプログラムを作成したら、実験時間の最後に、実験1週目で完成した機能について、チェックシートの「1週目」の列に”〇”を入力してください。
- 実験2周目では、前回の続きでプログラムを完成にむけて作成してください。2周目の実験時間の最後に、チェックシートの「2週目・自己判定」の欄に”〇”(一部未完成なら”△”,未完成なら”×”)を入力してください。
- 自己判定の記入が終わったら、同じ実験グループの1人と交代して、相手のプログラムの完成度をチェックし、相手のチェックシートの「2周目・他者判定」の欄に”〇”,”△”,”×”を入力してください。また、他の人のチェックシートに記入する際には、チェックシート表の下の「チェック担当」の欄に名前を記入しておいてください。
4.結果
- 出来上がったプログラムを動かして、作成した機能がちゃんと動作しているということが分かる実行例をいくつか画面のキャプチャ機能を使って「実験結果」として記録してください。
- 実験方法での2週目・他者判定において、(自分では動くと思っていたのに)”△”や”×”の判定のあったものについては、その実行例も「実験結果」として記録してください。
- また、他の人に他者判定をしてもらった自分のチェックシートも「実験結果」として保存してください。
5.レポート
実験レポートでは、以下の内容について記載してください。
- 目的
- 概要
- 自分が作成したテーマのルールなどを簡単にまとめる。
- プログラムの説明
- 自分のプログラムのフローチャートなどを作成し(全体の詳細なフローチャートでなくてもよい)、簡単に全体像を説明してください。
- 自分で工夫した所については、詳しく説明してください。
(例えばオセロなら裏返し処理の詳細なフローチャートなどを記載するなど。)
- 実験結果
- 実験結果として、プログラムリスト、チェックシート、動作の解る実例などをつけてください。
- 実験結果として、プログラムリストや動作例などの図をレポート内に入れる場合は、「プログラムリスト1」「図2動作例」といったように、リスト番号、図番号などをつけてレポート内で引用すること。
- 考察
- 自分で作ったプログラムでうまく動いていない所があれば、その原因やどのようにすれば直せるかを考え、その内容を説明してください。(必ずしもプログラムを直す必要はない)
- 動作で問題が見つからなかった場合は、さらにゲームを完全なものとするにはどういう改良が期待できるか、説明してください。
CTF問題とセキュリティの実験でWebサーバを使う場合
CTF問題とセキュリティ実験で、Webサーバを必要とする自作問題を作る時は、nitfcei.mydns.jp のサーバを使えます。
- nitfcei.mydns.jp に guestXX のIDでログイン(情報メディア工学(前期)資料を参考)
- public_html 配下のフォルダに xxxx.php などのファイルを作成
- http://nitfcei.mydns.jp/~guestXX/xxxx.php にブラウザでアクセス
- 最初に、Webの認証画面が出たら、別途アナウンスの ID, PW でログインしてください。
- https は使えないので要注意
- https://tsaitoh.net/~t-saitoh/ctf/ に設置してあるファイルと同じものが、
nitfcei.mydns.jp の /home0/t-saitoh/public_html/ctf/ のフォルダに置いてあります。
必要に応じてコピーや改造をしながら自作問題を作ってみてください。
創造工学演習・PHPとDB(予備実験)
インターネットを活用したプログラムを作成する場合、データを保存管理するためのデータベースと、データベースのデータを処理するためのプログラム言語が必要となってくる。今回の予備実験では、そのためにリレーショナルデータベースと、Webの動的なプログラム言語である PHP について説明する。
リレーショナル・データベース
データベースは、データを保存し、矛盾が発生しない様に管理してくれるシステムであり、インターネットで活用されている。
データを確実に保存し、矛盾なく扱うためには、本来複雑なプログラムが必要となる。この中で、データを表形式のテーブルを組み合わせて管理するシステムはリレーショナルデータベースと呼ばれる。リレーショナルデータベースでは、データの問い合わせなどの処理が簡単にできるように、SQL と呼ばれる言語を使って処理を行う。
大量のデータをインターネットの中で利用するためには、ネットワークを経由してデータの問い合わせが求められ、有名なデータベースシステムには、Oracle, MySQL などがある。今回の実験では、ネットワーク機能は持たないが簡単な手続きで使うことができる SQLite を使って説明する。
また、今回の予備実験では時間も限られることから、複数の表を組み合わせた SQL の処理については割愛する。
SQLの基本
リレーショナルデータベースでは、データの基本は表形式データであり、1つの表に相当するデータはテーブルと呼ぶ。
以下の様な名前・住所・年齢のデータがあったとすると、1人前のデータをレコードと呼び、name, addr, age といった属性はカラムと呼ぶ。
name | addr | age | |
t-saitoh | 越前市 | 55 | ←レコード |
sakamoto | 福井市 | 50 | |
murata | 福井市 | 35 | |
↑カラム |
データの型には、文字列型(char型,varchar型,text型)や、数値型(integer型,decimal型,real型)などがあり、create table 文にてカラムの型を定義する。
create table テーブルを作る
データベースの表を使う最初には、create table 文を実行する。C言語での struct 文をイメージすると解り易いかもしれないが、データはデータベースの中に永続的に保存されるので、システムを動かす最初に一度実行するだけで良い。
上記のような名前・住所・年齢のデータ構造であれば、次の様な create table 文を使う。
create table テーブル名 ( カラム名1 型1 , カラム名2 型2 ) ; -- 例 -- create table PERSON ( -- テーブル名:PERSON name varchar( 20 ) , -- 名前 addr varchar( 20 ) , -- 住所 age integer , -- 年齢 primary key( name ) -- name はデータ検索のキーであり重複は許されない ) ;
これと同じ様な処理をC言語で書くのであれば、以下の様な構造体宣言と同じであろう。
struct PERSON { char name[ 20 ] ; // 名前 char addr[ 20 ] ; // 住所 int age ; // 年齢 } ;
drop table テーブルを消す
データベースは永続的に保存されるので、テーブル全体のデータが不要であれば、drop table 命令で、テーブル全体を消す。
drop table テーブル名 ; -- 例 -- drop table PERSON ;
insert into レコードを追加
データベースに1レコードを保存するには、insert文を用いる。
insert into テーブル名 ( カラム名... ) values( 値... ) ; -- 例 -- insert into PERSON ( name , addr , age ) values ( 't-saitoh' , '越前市' , 55 ) ; insert into PERSON ( name , addr , age ) values ( 'sakamoto' , '福井市' , 50 ) ; insert into PERSON ( name , addr , age ) values ( 'murata' , '福井市' , 35 ) ;
delete レコードを消す
データベースのレコードを消すには、delete 文を用いる。条件を満たす複数のデータをまとめて消すことができる。
delete from テーブル名 where 条件 ; -- 例 -- -- 40歳未満のデータを全て消す。 murata,福井市,35 が消える。 delete from PERSON where age < 40 ;
update レコードを更新
データベースのレコードを修正するには、update 文を用いる。条件を満たす複数のデータをまとめて修正することもできる。
update テーブル名 set カラム = 値 where 条件 ; -- 例 -- -- 住所が越前市のレコードの年齢を 0 にする。 update PERSON set age = 0 where addr == '越前市' ;
select データを探す
データベースの内容を参照するための命令が select 文。where を記載することで、特定の条件のデータだけを選択したり、特定のカラムだけを抽出することができる。
select カラム名 from テーブル名 where 条件 ; -- 例 -- -- PERSON の全データを出力 select * from PERSON ; -- PERSON の住所が福井市だけを選別し、名前と住所を抽出 select name,addr from PERSON where addr = '福井市' ; -- PERSON の年齢の最高値を出力 (集約関数) select max(age) from PERSON where addr = '福井市' ; -- PERSON の年齢条件を満たす人数を数える (集約関数) select count(name) from PERSON where age >= 50 ;
動的なプログラム言語とPHP
本来、Webサーバが作られた頃は、論文や研究用のデータを公開する物であったが、扱うデータが増えるにつれ、特定の論文や研究データの一覧を表示したり探したりという処理が求められた。こういった処理のためにWebページのアクセスを受けた時に処理を実行する CGI という機能があったが、これを発展させてできたプログラム言語が PHP である。
PHPでは、ページを表示するための HTML の中に <?php … ?> のといった開始タグ・終了タグの中に、ブラウザから送られてきたデータに合わせて、処理を行うPHPの命令を記述し、データを(一般的にはHTML形式で)表示することができる。基本文法は C 言語に似ているが、様々なデータを扱うために変数にはどのような型でも保存できるようになっている。
ブラウザからデータを送るためのform文
ブラウザで入力欄を作ったり選択肢を表示し、その結果を送るための HTML は、入力フォーム(form)と呼ぶ。
<form method="get" action="処理ページ" > <input type="text" name="変数名" /> <input type="radio" name="変数名" value="値" /> <input type="checkbox" name="変数名" value="値" /> <textarea cols="横文字数" rows="行数"></textarea> <select name="変数名"> <option value="値1">表示1</option> <option value="値2">表示2</option> </select> <input type="submit" value="実行ボタンに表示する内容" /> </form>
formでは、入力する項目に変数名の名前を付け、action=”” で示したページにデータを送る。
PHPのプログラムの基本
PHPのプログラムは、外見は一般的に HTML ファイルであり、途中で <?php のタグからは、?> までの範囲が、PHP で処理が行われる。PHP のプログラムで print が実行されると、その場所に print 内容が書かれているような HTML ファイルが生成され、ブラウザで表示される。
PHP の中で変数は、$ で始まり、型宣言は基本的に不要である。
文字データを連結する場合は、“.” 演算子を使う。ダブルクオテーション”…”で囲まれた文字列の中の $名前 の部分は、変数名として扱われ、変数名の内容に置き換えられる。
HTMLのform文の action 属性で示された php であれば、PHPの中で送られてきた値を $_GET[‘変数名’] (method=”get”の場合)、 $_POST[‘変数名’] (method=”post”の場合)、または $_REQUEST[‘変数名’] (method=”get” or “post”) で参照できる。
((( sample.php ))) <html> <head> <title>sample.php</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> </head> <body> <form action="sample.php" method="POST"> <input name="A" type="text" /> <!-- 変数 $A --> + <input name="B" type="text" /> <!-- 変数 $B --> = <?php ini_set( 'error_reporting' , E_WARNING ) ; if ( $_REQUEST[ "A" ] != "" && $_REQUEST[ "B" ] != "" ) { print $_REQUEST[ "A" ] + $_REQUEST[ "B" ] ; } else { print "<INPUT TYPE=submit>" ; } ?> </form> </body> </html>
PHPでデータベースを扱う
SQLのデータベースを、プログラム言語の中で扱う場合は、その記述も色々である。PHPでは以下の様にSQLを扱う。
((( survey-init.php ))) <html> <head> <title>survey_init.php</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> </head> <body> <?php // デバッグ用にエラー警告を表示する ini_set( 'error_reporting' , E_WARNING ) ; // データベースに接続する $data_dir = "../public_data" ; $dbh = new PDO( "sqlite:$data_dir/sqlite.db" ) ; // データベースを初期化する $init_sql = "drop table if exists Survey ;" . "create table Survey (" . " uid varchar( 20 ) ," . " item varchar( 10 )" . ") ;" . "insert into Survey ( uid , item ) values ( 't-saitoh' , '猫' ) ;" . "insert into Survey ( uid , item ) values ( 'tomoko' , 'ケーキ' ) ;" . "insert into Survey ( uid , item ) values ( 'mitsuki' , 'ボードゲーム' ) ;" ; if ( $dbh->exec( $init_sql ) < 0 ) { print "Error: $init_sql" ; } // データベースの表形式を読み出し、表形式で出力する。 print "<table border='1'>\n" ; print "<tr><td align='center'>uid</td><td align='center'>item</td></tr>\n" ; $select_sql = "select uid,item from Survey ;" ; foreach( $dbh->query( $select_sql ) as list( $uid , $item ) ) { print "<tr><td>$uid</td><td>$item</td></tr>\n" ; } print "<table>\n" ; // データベースの単一データを取り出す $count_sql = "select count(item) from Survey where item = 'ケーキ' ;" ; print $dbh->query( $count_sql )->fetchColumn() ; ?> </body> </html>
PHPの主要なSQL関数(PDO)
$dbh = new PDO(…) ; データベースに接続するハンドラを取得。 $dbh->exec( “create…” ) ; データベースでSQLを実行。 $dbh->query( “select…” ) ; データベースに問い合わせ。「1レコードに対応した配列」が全データだけ繰り返す、2次元配列を返す。 $dbh->query( “…” )->fetchColumn() 結果が1つだけの問い合わせ。集約関数の結果を参照する場合に用いる。
練習問題(1)
- 上記の survey-init.php の select 文の部分を編集し、色々なデータ検索を試してみよ。
入力フォームのデータをデータベースに書き込む
((( survey-vote.php ))) <?php // エラー警告を表示 ini_set( 'error_reporting' , E_WARNING ) ; // form から送られてきた変数を保存 $uid = $_REQUEST[ "uid" ] ; $item = $_REQUEST[ "item" ] ; // データベースに接続する $data_dir = "../public_data" ; $dbh = new PDO( "sqlite:$data_dir/sqlite.db" ) ; // データベースに項目を追加する if ( $uid != "" && $item != "" ) { $insert_sql = sprintf( "insert into Survey( uid , item ) values ( %s , %s ) ;" , $dbh->quote( $uid ) , $dbh->quote( $item ) ) ; $dbh->exec( $insert_sql ) ; // reload処理で追記しないためページを強制的に再表示させる header( "Location: survey-vote.php" ) ; } ?> <html> <head> <title>survey_vote.php</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> </head> <body> <form method="get" action="survey-vote.php"> 名前: <input type="text" name="uid" /> 好きな物:<input type="text" name="item" /> <input type="submit" value="投票" /> </form> <?php // データベースの表形式を読み出し、表形式で出力する。 print "<table border='1'>\n" ; print " <tr><td align='center'>uid</td><td align='center'>item</td></tr>\n" ; $select_sql = "select uid,item from Survey ;" ; foreach( $dbh->query( $select_sql ) as list( $t_uid , $t_item ) ) { print " <tr><td>$t_uid</td><td>$t_item</td></tr>\n" ; } print "</table>\n" ; ?> </body> </html>
練習問題(2)
- 上記の survey-vote.php のプログラムを編集し色々な入力方法・出力方法を試してみよ。
- 例えば、入力の item 選択に select や ラジオボタン フォームを使う。
- 例えば、出力結果で、item の投票結果を、棒グラフで出力する。
CTF問題とセキュリティ(4年実験)
この実験では、セキュリティコンテストのCTF問題(Capture The Flag競技)について、インターネットの仕組みを理解し、その問題の解き方を考え、新しく自分自身でCTF問題を作ってもらいます。
日程
実験は、4週にわたり、以下の日程で行います。
週 | 内容 |
1 | (前半)暗号・ファイル・Web |
2 | |
3 | (後半) プログラム作成・インターネット・OS |
4 |
前半・後半でそれぞれ、問題例(もしくは自分で見つけたCTF問題)の1つの説明と、自作の問題を新しく作り説明をしてください。
提出物
- 実験の目的
- 問題例(or 自分で見つけたCTF問題)を1つ選び
- 前半・後半それぞれについて
- その問題が情報セキュリティにどう関係しているのか
- 問題の解き方のしくみと解説
- 自作問題について
- 前半・後半それぞれ
- その問題が情報セキュリティにどう関係しているのか
- 問題の解き方、問題の作り方
- しくみと解説
- 提出先はこちらのTeams共有フォルダに。
- 自作問題の例
- サブリミナル効果で隠されたGIF
- 電子透かし
- 学校からアクセスする時だけ、答えが見える。(REMOTE_HOST)
- スマホでアクセスした時だけ、答えが見える。(HTTP_USER_AGENT)
- Google 検索した後だけ、答えが見える。(HTTP_REFERER)
コンパイラと関数電卓実験の総括
複雑な字句解析
コンパイラでは、字句解析→構文解析を行うのが一般的である…と説明をしたが、最近のC++では少し話がややこしい。
C++ではテンプレート機能などがあるので、整数型のリストみたいな型は、forward_list<int>といった書き方をする。そして、リスト型のリストを作る場合は、forward_list<forward_list<int>>という型が出てくるかもしれない。しかし、この場合、C言語の単純な字句解析処理が行われると、forward_list, “<” , forward_list , ”<” , int , “>>” というトークンに分解されることになる。しかし、これでは、ただしいC++でのテンプレート表記に構文解析できないので、少し古い C++03 では、”forward_list<forward_list<int> >”と、最後の2つの”>”の間に空白を入れる必要があった。
しかし、これはプログラム記述上問題も多いため、最新の C++11 では、”>>”と書いてもテンプレート記述の”<“との組を判断して、”>”,”>”と2つに分解してくれる。このため、字句解析の処理が lex のようなものでは不十分となっている。
形態素解析
今回の実験では、コンパイラを作るという目的で、字句解析、構文解析 を行う流れを説明し演習を行った。しかし、こういった処理は、自然言語処理でも使われている。
自然言語処理(Natural Language Processing)とは、人間の言語(自然言語)を機械で処理し、内容を抽出することです。
具体的には、言葉や文章といったコミュニケーションで使う「話し言葉」から、論文のような「書き言葉」までの自然言語を対象として、それらの言葉が持つ意味をさまざまな方法で解析する処理技術を指します。(入門編)自然言語処理(NLP)とは[引用]
今回のコンパイラの技術では、最初の処理は字句解析で説明をしていたが、日本語の場合は形態素解析が必要となる。
形態素解析 — 形態素解析とは、文法的な情報の注記の無い自然言語のテキストデータから、対象言語の文法や、辞書と呼ばれる単語の品詞等の情報にもとづき、形態素の列に分割し、それぞれの形態素の品詞等を判別する作業である。(wikipedia引用)
意味解析
自然言語処理では、これに加え構文解析の後に、意味解析の処理が行われる。例えば、「高い」という単語は、金額の意味なのか、高度の意味なのか、判断が必要だが、かかり受けする単語にお金に関するものであれば金額と判断するし、身長という単語があれば高低の意味と判断し、全体の意味を解析する。
コンパイラ処理でも、目的プログラム生成行程プログラミング言語において、コンパイラーがソースコードを解析し目的プログラムを生成する際の処理工程のひとつ。意味解析は、ソースコード内に記述された変数の型や文(ステートメント)が言語の記述仕様に沿っているかどうかをチェックする。
静的型付け・動的型付け・型推論
プログラム言語のコンパイラでも、意味解析が必要な事例として、型推論について紹介する。プログラム言語では、プログラムを記述する際に、値を記憶するために型情報が重要である。C言語では、明確に型を記述する必要がある(静的型付け言語)。これに対し、Perl , Python , PHP , JavaScript といった言語では、変数にどういった型の情報でも代入が可能となる。このため、変数宣言では型を明記する必要がないが、プログラムが動作している時点でインタプリタは型を確認しながら処理が行われる(動的型付け言語)ため、無駄な型判定処理が常に行われ処理効率が悪い。また、動的型付け言語では、型が明記されていないのでプログラムの間違いが見逃されることもある。
Microsot では、JavaScript の動的型付けの問題を解決するために、TypeScript を開発している。TypeScript では JavaScript に、静的型付けとオブジェクト指向のクラスの機能が追加されている。
プログラムを安全に作る視点であれば、データの型のチェックが行われる静的型付けはバグを減らす意味で重要であるが、プログラム記述が複雑になる問題も出てきている。例えば、C++ でのリスト処理は、forward_list のテンプレート機能を使うと、以下のように書ける。
#include <iostream>
#include <forward_list>
int main() {
// std::forward_list<>線形リスト
std::forward_list<int> lst{ 1 , 2 , 3 } ; // 1,2,3のリストで初期化
// for( List* p = lst ; p != NULL ; p = p->next ) {...} に相当
for( std::forward_list<int>::iterator itr = lst.begin() ;
itr != lst.end() ;
itr++ ) {
std::cout << *itr << std::endl ;
}
}
しかし、繰り返し処理のためのデータ型(反復子) itr の宣言は、ただ「リストの要素で繰り返し」とい目的で書くには、型宣言が面倒すぎる。
そこで、最新の C++ では、型推論 とよばれる機能が導入され、型宣言の初期化の右辺式から変数の型を推論してくれる。下記プログラム例では繰り返しのイテレータ itr が auto という曖昧な型で宣言されているけど、初期化の右辺式 lst.begin() の型 std::forward_list<int>::iterator で宣言してくれる。あくまでも型推論は、コンパイル時に型が確定しているので、静的型付け言語の便利な機能の1つである。
#include <iostream> #include <forward_list> int main() { // std::forward_list<>線形リスト std::forward_list<int> lst{ 1 , 2 , 3 } ; // 1,2,3のリストで初期化 for( auto itr = lst.begin() ; // lst.begin() の型からitrの型を推論 itr != lst.end() ; itr++ ) { std::cout << *itr << std::endl ; } }
しかし、変数の型推論をしなくちゃいけないのは、変数を使った副作用を伴う記述方法が間違いのモトという考え方では、関数型プログラミングという話が出てくる。C++のalgorithm = 関数型という意味じゃないけど…
#include <iostream> #include <forward_list> #include <algorithm> int main() { std::forward_list<int> lst{ 1 , 2 , 3 } ; std::for_each( lst.begin() , lst.end() , []( int x ) { // 配列参照のコールバック関数 std::cout << x << std::endl ; } ); return 0 ; }
lexのそれ以外のアクション
flex+bisonを使った関数電卓を作る専攻科の実験で、空白処理の質問が出た。
こちらのような mycalc.l だと、空白の場合の処理とか、それ以外の文字が入力された時の処理は記述していない。
lex の出力する C 言語のソースをざらっと読むと、正規表現で指定しないパターンに対しては、そのまま文字を出力する処理( ECHO という入力文字をそのまま出力する #define マクロ)が生成される。だから、例で示したプログラムだと、式として”1+ab2″ を入力すると”ab>>3″と出力される。
%% 文字1 アクション1 # このような処理を書くと、雑な説明でいうなら、以下のような処理が作られる 文字2 アクション2 %% switch( 入力文字 ) { case '文字1' : アクション1 ; break ; case '文字2' : アクション2 ; break ; default : ECHO ; // 入力文字をそのまま出力するマクロ break ; }
このため空白を読み捨てる、それ以外の文字が入力されたら字句解析段階でのエラーを出力するのであれば、lex の正規表現の処理の部分に以下のようなものを追加する必要がある。
[ ¥t] ; /* 空白やタブ文字の時は何もしない */ . { /* それ以外の文字は、エラーメッセージとエラーとなった文字を出力して中断 */ /* 正規表現の . は、任意の1文字という意味 */ printf("lex:error ") ; ECHO ; exit( 1 ) ; }
batchとnohup
学校の CUDA サーバを自宅より使う学生より、「接続が Broken Pipeで切れるのどうすれば?」との質問があった。
原因は ssh で操作が何もない状態が続くと、セキュリティのために接続が切られるため。んで、.ssh/ssh_config でセッションの最大時間を延ばす設定あたりを説明したけど、「計算どのぐらいかかるの?」と聞くと「20時間かな…」。# さすが CUDA を使う卒研。
ssh のセッション延長の設定で20時間とかしちゃうと、ネットワークトラブルで通信が途中で切れたときに、サーバ側で「まだこの ssh 通信中だよね…」というプロセスが生き残る可能性があるので、あまり勧められる方法ではない。
こういう時には、処理を継続したままで、ログアウトするテクニックが必要。
通常は処理を起動して、ログアウトすると親プロセスにあたる sh が死ぬので、子プロセスが死んでしまう。
# このプロセスの原則があるから、Linux サーバで「訳の変わらんプロセスがあったら、『親を殺せ!!』が基本。」
プロセス確認の基本
((( 通常のプロセス全表示 ))) $ ps ax ((( 指定したコマンドのプロセス情報表示 ))) $ ps ax | grep コマンド名など ((( プロセスの起動引数を全部表示 ))) $ ps axl ((( 負荷の高い順にプロセス一覧を表示 ))) $ top ((( 指定したプロセスを停止 ))) $ kill -KILL プロセス番号
プロセスのバックグラウンド起動
((( プロセスをバックグラウンド起動 ))) $ コマンド... & # &がバックグラウンド起動の意味 ((( プロセスをバックグラウンドに変更 ))) $ コマンド... # フォアグランドでプロセスが動き出す ^Z (Ctrl-Z) # プロセスを一時停止 $ fg # 停止中のプロセスを再びフォアグラウンドで再起動 $ bg # 停止中のプロセスをバックグラウンドで再起動 $ jobs # 起動中の job を表示
上記で説明したコマンドは、login した shell の子プロセスになるため、たとえバックグラウンドで起動して「裏で動いている…プロセス」といえども、logout すると親プロセスが死ぬので、一緒に子プロセスも死んでしまう。
このため、起動するプロセスの親をシステムに代替わりしてもらって起動する nohup コマンドが使われる。
((( プロセスを No Hugup コマンドで起動 ))) $ nohup コマンド & # 出力はファイル保存される。 $ nohup コマンド > file.out & # 明示的にリダイレクトで表示保存先を指定 $ tail file.out # 保存しているファイルの末尾を表示
ただし nohup コマンドだと、プロセスが終了したかどうかわからない。こういう時は、batch コマンドが便利。ただし、batch コマンドはインストールされていない処理系が多い。(メールの設定が必要だから)
((( at パッケージのインストール ))) $ sudo apt-get install at # at(指定時間コマンド起動) # batch(バッチ処理起動) ((( batchの使い方 ))) $ batch at> コマンド at> ^D $ echo "コマンド" | batch
ただし batch は PATH が /bin/sh だけで起動するので、”python ほげ.py” とかで起動しても動かない場合あり。”/usr/bin/python ほげ.py” とかPATHを明記して起動するか、PATH=/usr/local/bin:/usr/bin:/bin なりの設定する処理を明記しないと動かない場合があるので要注意。
batch は、処理の出力結果はメールで送られてくる。結果を残すのなら、出力リダイレクトをしておく方がいい。
さくらクラウドの実験サーバのアップグレード
学生実験で、Webプログラムのセキュリティ問題の対応をテーマに実験しているけど、Ubuntu 18系を20系にアップグレードを行った。基本的な実験だけなので、Apache+PHP程度なので、移行も手間取らないだろうと踏んでいたけど、sulinux でちょっと手間取った。
# systemctl enable apache2 # aptitude install update-manager # aptitude update # aptitude dist-upgrade # do-release-upgrade -d
無事に、更新が終わって再起動をかけたら、ブートに失敗。ssh などが繋がらなくなる。
どうしようもなくなったので、さくらのクラウドのコンソールをみると、sssd 関連でエラーが出てブート途中で止まっている。
冷や汗をかきながら、Ubuntu ブート時のリカバリーモードの起動を試みて、ようやく成功。
起動時にESCを押すと、ブートメニューが表示される。インストール済みの kernel とそのリカバリーモードの一覧がでるので、リカバリーモードでログイン。”aptitude remove sssd-common” により一旦、sssd を削除(sulinux関連)すると、無事に起動したので、基本機能の確認をして、再び”aptitude install sssd-common”。
アップグレード作業中に、apache2 が disable されていたり、php の関連パッケージが入っていなかったので、インストール。
# systemctl enable apache2 # systemctl start apache2 # aptitude install php-mbstring php-sqlite3 # a2enmod php7.4