3年前くらいから「合格率10%弱~20%未満」程度の資格試験を受験しています。
そこで自分なりの方法論を述べてみたいと思います。
この丸暗記で何とかなった資格試験は以下の通りです。
・宅建(1度で合格)
・マンション管理士(2度で合格)
・管理業務主任者(1度で合格)
・消防設備士(4類、6類とも1度に合格)
・2種電気工事士(1度で合格)
・保育士(1度で学科試験全て合格、実技試験は2回で合格)
まず、この勉強法を行う前提条件は以下の通りです
●前提条件
1)文系の資格であること(または、公式を覚えれば解けるくらいの計算以外出題されないこと)
2)極端に難しい試験ではないこと
3)マークシートや選択式の試験であること(4の一問一答問題集が出版されやすいため)
4)目標とする資格試験の一問一答問題集が販売されていること
1)は自分が文系であることと、そもそも「丸暗記」でやるので「応用問題」とかできない為です。
2)については、丸暗記では厳しすぎる内容ということならば腰をすえて勉強するものだ、ということですね。
4)は、後で詳しく述べますが「繰り返し問題を解く」のに非常に手軽だからです。
●教材について
自分が使う教材は「参考書(テキスト)」、「一問一答問題集」および「過去問題集」です。
テキストは下の「手順1」でも述べますが「重要なところが赤字や太字になっている」ものを選ぶのがいいです。
問題集は2種類で「一問一答」と「過去問」です。
一問一答問題集は学習のはじめから試験直前までずっと使います。なるべく過去問題を一問一答問題集にしたものを使ってください。
過去問題集は試験直前2週間になったら過去5年分を1~2回くらい解いて一問一答問題集ではわからない「出題のされ方」を確認します。
なぜ、「一問一答問題集」なのか、という点について説明しますね。箇条書きにすると以下のとおりになります。
1)大抵の一問一答問題集は「過去問の選択肢をバラバラにして○×」という形式なので、網羅性が高く、読み飛ばしができないため
2)「過去問の選択肢をバラバラにして○×」という形式と、分野別に並べてあるものが多いので、取り組むときに比較的敷居が低いため
3)配点という概念が無く、学習進捗度を集計しやすい
3)の学習進捗度というのは「一問一答問題集の正答率」のことです。
自分がやっている集計方法としては、Excelで成績表を作り、問題番号を列に並べ、問題番号の隣の行にはその問題番号の問題が正答なら「1」誤答なら「0」を入力して集計します。
その集計を問題数で割れば「正答率」がでます。
分野別に分かれている問題集なら「分野別の正答率」も出せますから苦手分野もわかりやすくなります。
では、具体的な丸暗記勉強法について。
◆手順1:テキストの本文の「太字、色分け」部分のみ「ノートに抜き出す」作業を行う
参考書やテキストは、だいたい重要な部分は赤字だったり、太字になっていたりします。または枠で囲ってあったりもします。
その「重要な部分周辺の文章」を読み取って、「自分でまとめ」て、そのまとめをノートに書きます。
そのほかの文章は全て無視でOKです。これをテキストの最後まで行います。
この作業は結構時間がかかるものなので、試験日から2ヶ月前くらい前までには終えるようにスケジュールを組んでください。
最初に受ける試験の時は、試験日の6ヶ月前くらいから始めるとかなり余裕を持ってできると思います。
慣れてきたら、試験日の3ヶ月~4ヶ月前からでも余裕で間に合います。
はっきりいいますがテキストはここで御役御免です。この後は問題集の解説を読んでもわからないところだけ確認のために読むくらいしか出番がありませんw
◆手順2:一問一答問題集を平日5日間かけて全て回答
手順1が終了したら、次は一問一答問題集で問題演習です。
この勉強の問題演習は基本的に1週間単位です。1週間で行う量としてはそれほど多くなく、しかも効果がテキメンです。
大体の一問一答問題集は1冊で1000問前後です。これを平日5日間でやるとすると1日200問です。1日200問もか…、と思われるでしょうが、大丈夫です。ここで一問一答問題集なのが効いてくるのです。
一問一答問題集の問題1つは大体1行から多くても5行くらいのものです。解くのに1分もかかりません。200問でも1時間はかからないものです。しかもやっていくうちに丸暗記が出来てきますので、解くのに10秒かからないこともあります。
だいたい、問題演習を始めて3週目以降は1日の勉強時間は大体1時間以内で終わります。
これくらいの勉強時間なら忙しい社会人の方でも、昼休みや帰宅後でも大丈夫じゃないでしょうか。また、複数の試験勉強の同時進行も比較的楽に実行可能ですね。
◆手順3:週末に手順1で作ったノートを読む
一問一答問題集を平日5日間で終わらせたら、週末です。週末になったら復習のために手順1で作ったノートを読みます。
当然テキストの全てを書き写していないので、問題演習中に「習ってない」部分が出てきたはずです。
そこは一問一答問題集の解説、自分の記憶、テキスト確認でノートに補足しておきます。
◆手順4:手順2と手順3を試験前2週間まで繰り返す
繰り返すのは丸暗記のためです。最低でも3回は繰り返してください。3回繰り返すと解答正答率が全体で85%~95%程度になります。ならなくてももう1回か2回繰り返せばなります。
うそじゃありません。面倒なのを3回我慢すれば試験には本当に合格します。
注意としては、手順2をはじめるのは最低でも試験前5週間前くらいにしてください。多分暗記が間に合いません。
最低でも3回は手順2と手順3を繰り返してもらえれば、自分が言ってることがわかってもらえると思いますw
◆手順5:過去問題を1~2回解いて、実際の出題のされ方に慣れる
手順4が終了すれば後は仕上げのみです。
一問一答問題集ではわからない「実際の出題形式」に「慣れる」ために過去問題集を1回か2回解きます。
そうすれば出題の形式もわかるし、実際に時間を計って本番さながらの状況にもなれることができます。
以上が、「丸暗記で乗り切る資格試験」という駄文でしたw
自分の中では実績のある方法なのですが、これを読んで下さっている皆さんには会わない方もいると思います。
なぜ、こんな方法で勉強しているかといえば、「資格試験では100%の得点は必要ないから」であります。
つまり、「資格取得だけ」が目標になっているためですね。資格を使って就職、転職しようという方ならば、もっとちゃんと理解を進めるような勉強法をお勧めしますw
以上です。それでは。
2012-05-27
2012-05-23
【サービス】家計簿システム作ってみた。
久しぶりに専門である「お金計算」のプログラム作ってみました。
今回は慣らしということで簡単な「家計簿」のシステムにしてみました。
開発期間は設計から賞味2日位です。
以前にも同じようなシステムを作っていたのですが、そちらはすでに古いバージョンのデータベースやPHPを使っていたので保守性も悪く、長いプログラムになっていました。
ですので、全面的にプログラムを書き直したうえでデータベースのバージョンもあげています。
あと、プライバシーとセキュリティを考えると、「名前」や「住所」は入力してもらわないほうが良いと判断して、ユーザ登録の際の入力には「メールアドレス」「パスワード」「ニックネーム」のみお願いしています。
「TagajoTown Household Account Book System (TgagajoTown家計簿システム)」
紹介サイトURL : http://kakei.tagajo.tv/
システムの使い方: http://kakei.tagajo.tv/how_to_use.php
というわけで、以下システムの目的と特徴紹介です。
■システムの目的
この家計簿システムは「現金出納」に利用するための物です(世間一般の家計簿はみな現金出納だと思いますが)。
基本的に、
1)預金からお金を引き出し
2)買い物をし
3)あまったら預金へ戻す
という流れを記録することで、現金の流れ(キャッシュフロー)を把握します。
つまり、無駄なお金を使ってないか確認するのが目的のひとつです。
あと重要なのが3)の「あまったら預金へ戻す」ことです。
これは「貯蓄を増やす」こととイコールなので「ヘソクリを増やしたい」という方にもお勧めの方法です。
このシステムでは、摘要が自由に設定できるので「預金預入」の摘要を追加して、その金額が増えるように家計を管理していけば、このシステムの目的達成、ということになります。
■特徴1.加算式家計簿
自分が家計簿(お小遣い帳でもいいですが)をつけるときに気になることがあります。
それは、家計簿をつけるとき「摘要」に合致する出納を「事前に集計して」からでないと家計簿に記載できないのがイヤだったということです。
たとえば、「食費」の摘要にはその日買った「昼食代」や「おやつ代」などを一旦「集計」するというひと手間です。
今回、それをなくすために以下のような方針で入力画面を設計しました。
1)1日の出納入力は「摘要」ごとに行う。つまりある日の出納データ数は設定した摘要の数までとなる。
2)摘要に入力した出納の数値は、すでにデータがあるときは「データに加算」、無いときは「その数値のデータを作成」とする。
3)摘要のデータは削除できないようにする(このシステムでの「出納データ削除」とは「摘要の削除」を意味するため)
4)削除できないため、出納の数値から「減算」もできるように「マイナスの数値」を受け入れるようにする。
という感じにしました。
このような設計にすると、「買った金額」ごとに入力できます。
買い物の後レシートをもらったとして「その商品ごと」に金額を入力(しかも同じ摘要へ何度も)を行えるようになります。
こうしたおかげで「日毎に増えるデータ数」が限定され、ディスクの容量も節約できるようになり、集計演算もまぁまぁのスピードで行えるようになりました。
■特徴2.摘要のカテゴリ分類
摘要を「カテゴリ分け」できるようになっています。
カテゴリ分けすると、システムトップの集計一覧表に「小計」が表示されるようになります。
たとえば「収入」カテゴリに「預金引出」と「給与(手渡)」の摘要があったとして、2つの摘要の合計が「小計」欄に表示されるようになります。
また、これら「カテゴリ」「摘要」ともに「登録、変更、削除」ができるようになっています。
ただし、出納データが登録された摘要やカテゴリを削除すると同時に出納データも削除されてしまうので注意が必要です。
■特徴3.グラフ表示
システムのトップページには、「カテゴリ・摘要の集計一覧表」が表示されますが、そのほかに「摘要ごと」の折れ線グラフが表示されます。
このグラフ自体はGoogle Chart APIを使用しています。
以上です。
今回は慣らしということで簡単な「家計簿」のシステムにしてみました。
開発期間は設計から賞味2日位です。
以前にも同じようなシステムを作っていたのですが、そちらはすでに古いバージョンのデータベースやPHPを使っていたので保守性も悪く、長いプログラムになっていました。
ですので、全面的にプログラムを書き直したうえでデータベースのバージョンもあげています。
あと、プライバシーとセキュリティを考えると、「名前」や「住所」は入力してもらわないほうが良いと判断して、ユーザ登録の際の入力には「メールアドレス」「パスワード」「ニックネーム」のみお願いしています。
「TagajoTown Household Account Book System (TgagajoTown家計簿システム)」
紹介サイトURL : http://kakei.tagajo.tv/
システムの使い方: http://kakei.tagajo.tv/how_to_use.php
というわけで、以下システムの目的と特徴紹介です。
■システムの目的
この家計簿システムは「現金出納」に利用するための物です(世間一般の家計簿はみな現金出納だと思いますが)。
基本的に、
1)預金からお金を引き出し
2)買い物をし
3)あまったら預金へ戻す
という流れを記録することで、現金の流れ(キャッシュフロー)を把握します。
つまり、無駄なお金を使ってないか確認するのが目的のひとつです。
あと重要なのが3)の「あまったら預金へ戻す」ことです。
これは「貯蓄を増やす」こととイコールなので「ヘソクリを増やしたい」という方にもお勧めの方法です。
このシステムでは、摘要が自由に設定できるので「預金預入」の摘要を追加して、その金額が増えるように家計を管理していけば、このシステムの目的達成、ということになります。
■特徴1.加算式家計簿
自分が家計簿(お小遣い帳でもいいですが)をつけるときに気になることがあります。
それは、家計簿をつけるとき「摘要」に合致する出納を「事前に集計して」からでないと家計簿に記載できないのがイヤだったということです。
たとえば、「食費」の摘要にはその日買った「昼食代」や「おやつ代」などを一旦「集計」するというひと手間です。
今回、それをなくすために以下のような方針で入力画面を設計しました。
1)1日の出納入力は「摘要」ごとに行う。つまりある日の出納データ数は設定した摘要の数までとなる。
2)摘要に入力した出納の数値は、すでにデータがあるときは「データに加算」、無いときは「その数値のデータを作成」とする。
3)摘要のデータは削除できないようにする(このシステムでの「出納データ削除」とは「摘要の削除」を意味するため)
4)削除できないため、出納の数値から「減算」もできるように「マイナスの数値」を受け入れるようにする。
という感じにしました。
このような設計にすると、「買った金額」ごとに入力できます。
買い物の後レシートをもらったとして「その商品ごと」に金額を入力(しかも同じ摘要へ何度も)を行えるようになります。
こうしたおかげで「日毎に増えるデータ数」が限定され、ディスクの容量も節約できるようになり、集計演算もまぁまぁのスピードで行えるようになりました。
■特徴2.摘要のカテゴリ分類
摘要を「カテゴリ分け」できるようになっています。
カテゴリ分けすると、システムトップの集計一覧表に「小計」が表示されるようになります。
たとえば「収入」カテゴリに「預金引出」と「給与(手渡)」の摘要があったとして、2つの摘要の合計が「小計」欄に表示されるようになります。
また、これら「カテゴリ」「摘要」ともに「登録、変更、削除」ができるようになっています。
ただし、出納データが登録された摘要やカテゴリを削除すると同時に出納データも削除されてしまうので注意が必要です。
■特徴3.グラフ表示
システムのトップページには、「カテゴリ・摘要の集計一覧表」が表示されますが、そのほかに「摘要ごと」の折れ線グラフが表示されます。
このグラフ自体はGoogle Chart APIを使用しています。
以上です。
2012-04-29
【電脳卸】全てのカテゴリをパースする。
電脳卸のWebAPIで「全てのカテゴリを返す」XMLファイルがあるので、それをパースしてみたよ。
PHPの組み込み関数にもXMLをパースする関数があるけど、自分で実装したくなったので自分で書いてみたよ。
APIから返されるXMLの構造が微妙に難しかったので、再帰関数とスタックでやってみた。
APIのURLは→「http://webservice.d-064.com/3.x/category.xml」です。
こんな感じ。
PHPの組み込み関数にもXMLをパースする関数があるけど、自分で実装したくなったので自分で書いてみたよ。
APIから返されるXMLの構造が微妙に難しかったので、再帰関数とスタックでやってみた。
APIのURLは→「http://webservice.d-064.com/3.x/category.xml」です。
/** 電脳卸用全てのカテゴリをパースして配列して、シリアライズ後保存する。 パース結果の配列は以下のような仕様で出力される 結果配列仕様: array( 'カテゴリID 1' => array( 'name' => 'カテゴリ名', 'parent' => '親カテゴリID' ) ); array( 'カテゴリID 2' => array( 'name' => 'カテゴリ名', 'parent' => '親カテゴリID' ) ); array( 'カテゴリID 3' => array( 'name' => 'カテゴリ名', 'parent' => '親カテゴリID' ) ); ... array( 'カテゴリID n' => array( 'name' => 'カテゴリ名', 'parent' => '親カテゴリID' ) ); ※一番上のカテゴリは親カテゴリIDが「-1」となっている。 商品のカテゴリIDから親カテゴリ全てを取得するには、 function get_category_tree( $category_id, &$category_tree ) { global $global_category_array; $id = $category_id; $name = $global_category_array[ $id ][ 'name' ]; $parent = $global_category_array[ $id ][ 'parent' ]; $category_tree[ $id ] = array( 'name' => $name, 'parent' => $parent ); if( $parent == '-1' ) { return ; } else { get_category_tree( $parent, $category_tree ); } } $global_category_array = unserialize( file_get_contents( './category_array.dat' ) ); $tree = array(); get_category_tree( '1269', $tree ); var_dump( $tree ); といったプログラムで返せる。 */ define( 'CATEGORY_XML_URL', 'http://webservice.d-064.com/3.x/category.xml' ); define( 'CATEGORY_XML_FILENAME', dirname( __FILE__ ) . DIRECTORY_SEPARATOR . 'category.xml' ); define( 'CATEGORY_ARRAY_FILENAME', dirname( __FILE__ ) . DIRECTORY_SEPARATOR . 'category_array.dat' ); define( 'CATEGORY_SRC_ENCODING', 'UTF-8' ); define( 'CATEGORY_DIST_ENCODING', 'SJIS' ); date_default_timezone_set( 'Asia/Tokyo' ); /* 個々のカテゴリデータを取得する @return array [ 'category_id', 'category_depth', 'category_name' ] */ function get_id_depth_name() { global $xmlp; return array( preg_replace( '/<[^>]+>(.*?)<\/[^>]+>/', '$1', trim( fgets( $xmlp ) ) ), // カテゴリID preg_replace( '/<[^>]+>(.*?)<\/[^>]+>/', '$1', trim( fgets( $xmlp ) ) ), // カテゴリ深さ preg_replace( '/<[^>]+>(.*?)<\/[^>]+>/', '$1', trim( fgets( $xmlp ) ) ) ); // カテゴリ名 } /** カテゴリXMLをパースする。再帰関数。 @param $parent 親カテゴリID */ function parse( $parent = -1 ) { global $xmlp; global $global_category_array; global $global_parent_ids; // 親IDのスタック $line = trim( fgets( $xmlp ) ); switch( true ) { // 空行 case ($line == ''): parse( $parent ); break; // XML定義 case preg_match( '/<\?xml(?:[^>]+)>/', $line ): parse( $parent ); break; // 一番外側のタグ case ($line == ''): parse( $parent ); break; case ($line == ' '): fclose( $xmlp ); return; // 1つのカテゴリ case ($line == ''): list( $id, $depth, $name ) = get_id_depth_name(); $name = mb_convert_encoding( $name, CATEGORY_DIST_ENCODING, CATEGORY_SRC_ENCODING ); // 親IDを退避 $parent = $global_parent_ids[ 0 ]; // スタックに自分を積む array_unshift( $global_parent_ids, $id ); $global_category_array[ $id ][ 'name' ] = $name; $global_category_array[ $id ][ 'parent' ] = $parent; parse( $parent ); break; case ($line == ' '): // 自分のIDを捨てる array_shift( $global_parent_ids ); parse( $global_parent_ids[ 0 ] ); break; // サブカテゴリ case ($line == ''): parse( $global_parent_ids[ 0 ] ); break; case ($line == ' '): parse( $global_parent_ids[ 0 ] ); break; // ファイルが終わる前に「/Categorys」が来るはずなので使わないかも。 case feof( $xmlp ): fclose( $xmlp ); return; // 何かあったときのための保険。そのまま終了。 default: die( '[' . $line . '] Exception.' ); } } // XMLファイルダウンロードと保存 file_put_contents( CATEGORY_XML_FILENAME, file_get_contents( CATEGORY_XML_URL ) ); // XMLファイルのポインタ取得 $xmlp = fopen( CATEGORY_XML_FILENAME, 'r' ); if( !$xmlp ) { die( 'XML File Dont Open.' ); } // 親IDスタックと結果配列準備 $global_parent_ids = array( -1 ); $global_category_array = array(); // パース本体 parse(); // 結果配列をシリアライズして保存 file_put_contents( CATEGORY_ARRAY_FILENAME, serialize( $global_category_array ) ); exit( 0 );
こんな感じ。
2012-04-25
【Scheme】ForthをSchemeで実装してみた。
はい。今度はScheme(処理系はGaucheを使用)でForthを実装してみました。
プログラムは5つの部分に分かれていて、
1)スタック管理
2)標準入力からプログラムを読むリーダ
3)ForthプログラムをSchemeプログラムに変換するトランスレータ
4)ワードが登録される辞書
5)実行制御
という構成になっています。
とりあえず、各部分の説明をしていきます。
1)スタック管理
;;; スタック管理 (define *data-stack* '()) (define (push val) (set! *data-stack* (cons val *data-stack*))) (define (pop) (cond ((null? *data-stack*) (raise "Stack UnderFlow.")) (else (let ((val (car *data-stack*))) (set! *data-stack* (cdr *data-stack*)) val))))
スタック管理は面倒くさくないグローバル変数を使ったものにしました。
関数もPUSHとPOPのみです。POPにはスタックアンダーフローがでたら例外が投げられるようになっています。
2)標準入力からプログラムを読むリーダ
;;; 標準入力から読み込み、トークンに分割する。 (define (trim str) (begin (set! str (regexp-replace #/^\s+/ str "")) (set! str (regexp-replace #/\s+$/ str "")) str)) ;; シンボルに使えない文字を変換する (define (def-word-translate str) (begin (set! str (regexp-replace-all #/\;/ str "edef")) (set! str (regexp-replace-all #/\:/ str "sdef")) (set! str (regexp-replace-all #/\./ str "dot")) (set! str (regexp-replace-all #/\.([a-zA-Z])/ str "dot\\1")) (set! str (regexp-replace-all #/\s+/ str " ")) str)) ;; 半角スペースで分割 (define (tokenizer str) (token->symbol (string-split (def-word-translate str) " "))) ;; トークンをシンボルと数値のリストにする (define (token->symbol tokens) (cond ((null? tokens) '()) ((rxmatch #/[0-9\.]+/ (car tokens)) (cons (string->number (car tokens)) (token->symbol (cdr tokens)))) (else (cons (string->symbol (car tokens)) (token->symbol (cdr tokens)))))) ;; 標準入力からプログラムを読み取り、トークンにして返す (define (reader) (let ((line (read-line))) (cond ((eof-object? line) '()) ((string=? "" (trim line)) (reader)) (else (tokenizer (trim line))))))
基本的には標準入力から入ってきた文字列をトークンに分割するだけなのですが、「;」や「.」がSchemeでは特別な意味を持っているので、変換してからトークンに分割しています。
3)ForthプログラムをSchemeプログラムに変換するトランスレータ
;;; ForthプログラムをSchemeに変換する (define *program* '()) (define (translate-token token) (cond ((number? token) (list 'push token)) ((search-dict token) (list (get-dict token))) (else (raise (format #f "Undefined [~A]." token))))) (define (define-word tokens) (cond ((null? tokens) (raise "Syntax Error: [;] is missing.")) ((eq? 'edef (car tokens)) '()) (else (cons (translate-token (car tokens)) (define-word (cdr tokens)))))) (define (translate tokens define-flag) (cond ((null? tokens) '()) ((and define-flag) (if (eq? (car tokens) 'edef) (translate (cdr tokens) #f) (translate (cdr tokens) #t))) ((eq? (car tokens) 'sdef) (add-dict (cadr tokens) (append '(lambda ()) (define-word (cddr tokens)))) (translate (cdr tokens) #t)) (else (cons (translate-token (car tokens)) (translate (cdr tokens) #f)))))
トランスレータは今はこれだけです。これに「ループ処理」や「ローカル変数」などを実装するともっと長くなりそうです。
ワード定義もここでやっていて、
(append '(lambda ()) '(define-word ...))
とやれば、手軽にワードを生成することができますね。
4)ワードが登録される辞書
;;; 辞書用プログラム (define *dictionaly* (make-hash-table 'equal?)) ;; 辞書初期化 (define (init-dict) (begin (hash-table-put! *dictionaly* '+ (lambda () (push (+ (pop) (pop))))) (hash-table-put! *dictionaly* '- (lambda () (let ((a (pop))) (push (- (pop) a))))) (hash-table-put! *dictionaly* '* (lambda () (let ((a (pop))) (push (* (pop) a))))) (hash-table-put! *dictionaly* '/ (lambda () (let ((a (pop))) (push (/ (pop) a))))) (hash-table-put! *dictionaly* 'mod (lambda () (let ((a (pop))) (push (mod (pop) a))))) (hash-table-put! *dictionaly* 'dot (lambda () (print (pop)))) (hash-table-put! *dictionaly* 'dots (lambda () (print (reverse *data-stack*)))) (hash-table-put! *dictionaly* 'exit (lambda () (exit))) )) ;; 辞書に追加 (define (add-dict key val) (hash-table-put! *dictionaly* key val)) ;; 辞書検索 (define (search-dict key) (hash-table-exists? *dictionaly* key)) ;; 辞書から取得 (define (get-dict key) (if (search-dict key) (hash-table-get *dictionaly* key) (raise (format #f "Undefined [~A]" key))))
ワード辞書もグローバル変数としました。辞書からワードを取得するとき、ワードが存在しなければ例外が投げられます。
ワードは「init-dict」関数に追加していけば使える組み込みワードが増えます。今は四則演算と表示くらいしかないですがw
5)実行制御
;;; Forthエントリポイント ;; 処理系初期化 (define (init-forth) (begin (load "./dictionaly") (load "./reader") (load "./stack") (load "./translate") (init-dict))) ;; ForthプログラムをSchemeに変換した結果を実行する (define (exec-forth prog ret) (cond ((null? prog) ret) (else (exec-forth (cdr prog) (eval (car prog) (interaction-environment)))))) (init-forth) (let loop () (display "> ") (flush) (display (exec-forth (translate (reader) #f) #f)) (newline) (loop))
これは特に説明することもないです。
処理系の初期化関数とトランスレートしたForthプログラムの実行くらいです。
トランスレートしたプログラムはただevalしてるだけですね。
こんな感じです。
2012-04-20
【Forth】JavaScriptでForthっぽいの作ってみた。
ちょっと前から、簡単な言語構造をしているプログラミング言語を他の言語で実装することにはまっています。
今回挑戦したのはForth言語です。
今回はJavascriptで作ったので、整数も浮動小数点数も同じ1つのスタックで処理できます。つまり、「1.2 2 + sqrt」とか書くことが可能です。
この言語の特徴は「スタック志向」と「逆ポーランド記法(後置記法)」につきます。他の言語で言う「関数」は「ワード」といいます。ワードは辞書に登録されています。
と、突然言われても良くわからないのと思うので、早速サンプルプログラムをいくつか列挙して解説していきたいと思います。
Forthは(というより後置記法がですが)偶然にも日本語の並びにとても似ているので日本語で説明するのが実は簡単です。
04-21追記:個別サイトを作ってみました。JS Forth
●インタプリタデモ
1.下の入力欄にプログラムを入力して、エンターキーを押します。
2.自動的に計算結果が入力欄の下にたまります。
※このページをリロードすると定義したワードが消えてしまいますので注意してください。
●20000円を3人で割り勘にする
→ 20000 3 /mod floor . .
解説:
上のプログラムは以下の通り処理が進みます。
1)20000をスタックに積む。3をスタックに積む。(20000 3)
2)スタックから2つ値を取り出して、除算をし、剰余をスタックに積む。商をスタックに積む。(/mod)
3)スタックからひとつ取り出して小数点以下を切り捨てる。(floor)
4)スタックから1つ取り出して表示する。(.)
5)スタックから1つ取り出して表示する。(.)
ね、簡単でしょ?
日本語で説明するときは、「20000円と3万円で割り算して商と余りを出す。商の小数点以下を切り捨てて結果を出す」といったところでしょうか。
●割り勘ワード定義
上のプログラムをワード(関数)にしてみます。
→ : warikan ( n1 n2 -- n n ) /mod floor ;
解説:
1)「:(以下コロン)」はワード定義が始まるときに書きます。コロンがないとForthインタプリタは解釈モードとして動作し、結果を出そうとします。それをやめさせるにはこのコロンが必要です。コロンが見つかったときインタプリタは「コンパイルモード」になってプログラムがワード定義だとわかり、結果を出すのではなく定義されたワードを辞書に登録します。
2)「warikan」はワード名です。
3)「(」~「)」はコメントです。この場合のコメント「( n1 n2 -- n n )」はスタックから2つ取り出し、処理結果を2つ積む。という内容のコメントです。
4)「/mod floor」はwarikanワードのプログラム本体でこれらに引数の記述は必要なく、ワードの中でスタックからポップする処理やプッシュ処理が記述されているので「引数を本当に」とりません。
5)「;」はコロン定義の終了マークです。
Forth言語のプログラムは以上のような感じです。
●組み込みワード
今のところ組み込みワードは以下の通りです。
注意としては、制御構造ワードは「コンパイルモード」でしか使用できないところでしょうか。
詳しいワードの説明は「GForthマニュアル」がいいと思います。
・算術演算(整数・小数の区別なし)
+ 1+ - 1- * / mod /mod negate abs min max
・ビット演算
and or xor invert lshift rshift 2* 2/
・比較演算
< <= <> = > >= 0< 0<= 0<> 0> 0>=
・算術関数
floor round ** sqrt exp log cos sin tan acos asin atan atan2 pi
・制御構造
if else then endif begin agein repeat while until ?do loop +loop -loop unloop leave exit recurse
・スタック操作
drop nip dup over tuck swap rot -rot pick clearstacks
・表示
. .s
今回挑戦したのはForth言語です。
今回はJavascriptで作ったので、整数も浮動小数点数も同じ1つのスタックで処理できます。つまり、「1.2 2 + sqrt」とか書くことが可能です。
この言語の特徴は「スタック志向」と「逆ポーランド記法(後置記法)」につきます。他の言語で言う「関数」は「ワード」といいます。ワードは辞書に登録されています。
と、突然言われても良くわからないのと思うので、早速サンプルプログラムをいくつか列挙して解説していきたいと思います。
Forthは(というより後置記法がですが)偶然にも日本語の並びにとても似ているので日本語で説明するのが実は簡単です。
04-21追記:個別サイトを作ってみました。JS Forth
●インタプリタデモ
1.下の入力欄にプログラムを入力して、エンターキーを押します。
2.自動的に計算結果が入力欄の下にたまります。
※このページをリロードすると定義したワードが消えてしまいますので注意してください。
●20000円を3人で割り勘にする
→ 20000 3 /mod floor . .
解説:
上のプログラムは以下の通り処理が進みます。
1)20000をスタックに積む。3をスタックに積む。(20000 3)
2)スタックから2つ値を取り出して、除算をし、剰余をスタックに積む。商をスタックに積む。(/mod)
3)スタックからひとつ取り出して小数点以下を切り捨てる。(floor)
4)スタックから1つ取り出して表示する。(.)
5)スタックから1つ取り出して表示する。(.)
ね、簡単でしょ?
日本語で説明するときは、「20000円と3万円で割り算して商と余りを出す。商の小数点以下を切り捨てて結果を出す」といったところでしょうか。
●割り勘ワード定義
上のプログラムをワード(関数)にしてみます。
→ : warikan ( n1 n2 -- n n ) /mod floor ;
解説:
1)「:(以下コロン)」はワード定義が始まるときに書きます。コロンがないとForthインタプリタは解釈モードとして動作し、結果を出そうとします。それをやめさせるにはこのコロンが必要です。コロンが見つかったときインタプリタは「コンパイルモード」になってプログラムがワード定義だとわかり、結果を出すのではなく定義されたワードを辞書に登録します。
2)「warikan」はワード名です。
3)「(」~「)」はコメントです。この場合のコメント「( n1 n2 -- n n )」はスタックから2つ取り出し、処理結果を2つ積む。という内容のコメントです。
4)「/mod floor」はwarikanワードのプログラム本体でこれらに引数の記述は必要なく、ワードの中でスタックからポップする処理やプッシュ処理が記述されているので「引数を本当に」とりません。
5)「;」はコロン定義の終了マークです。
Forth言語のプログラムは以上のような感じです。
●組み込みワード
今のところ組み込みワードは以下の通りです。
注意としては、制御構造ワードは「コンパイルモード」でしか使用できないところでしょうか。
詳しいワードの説明は「GForthマニュアル」がいいと思います。
・算術演算(整数・小数の区別なし)
+ 1+ - 1- * / mod /mod negate abs min max
・ビット演算
and or xor invert lshift rshift 2* 2/
・比較演算
< <= <> = > >= 0< 0<= 0<> 0> 0>=
・算術関数
floor round ** sqrt exp log cos sin tan acos asin atan atan2 pi
・制御構造
if else then endif begin agein repeat while until ?do loop +loop -loop unloop leave exit recurse
・スタック操作
drop nip dup over tuck swap rot -rot pick clearstacks
・表示
. .s
登録:
投稿 (Atom)