.vimのruby用の設定
" rubyファイルの時のインデントをソフトTAB2にする(念のため自動インデントもON) autocmd FileType ruby set softtabstop=2 shiftwidth=2 tabstop=2 expandtab autoindent
電卓を作る
はじめに
数式を渡すとその計算結果を出力する電卓を作る。
仕様は次のとおり。
- 四則演算のみ行える
- 数値は、小数、整数どちらも指定可能。
- 10進数の整数表記は /\d+/
- 8進数の整数表記は /0[0-7]+/
- 16進数の整数表記は /0[xX][\da-fA-F]+/
- 「*/+-」を演算子とする
- *は乗算
- /は除算
- +は加算
- -は減算
トークン分割をする
数式を処理する前準備として、渡された文字列をトークンに分割します。
識別できないトークンが混じっていた場合は例外を投げます。
ソース
OPERATOR = Regexp.union("*", "/", "+", "-") OPERAND = Regexp.union(/^[1-9]\d*$/, /^0\d*$/, /^0x[\da-f]+$/i) def tokenizer(line) tokens = line.split(/\b/).map! {|token| token.strip} tokens.each {|token| case token when OPERATOR puts "#{token} は演算子です。" when OPERAND puts "#{token} は被演算子です。" else raise "#{token} は認識できない識別子です。" end } tokens end p tokenizer("3+ 4 * 5+0x52dF*07")
実行結果
3 は被演算子です。 + は演算子です。 4 は被演算子です。 * は演算子です。 5 は被演算子です。 + は演算子です。 0x52dF は被演算子です。 * は演算子です。 07 は被演算子です。 ["3", "+", "4", "*", "5", "+", "0x52dF", "*", "07"]
中置記法を後置記法へ変換する
中置記法とは、演算子を被演算子の間に書く記法。普段から使用している方法のこと。
後置記法とは、演算子を被演算子の後に書く記法。直感的じゃない。
中間記法は被演算子の間に必ず演算子が挟まるため、区切り文字が要らないというメリットがある。しかし、優先順位を持たせたい場合は括弧を付けなくてはいけないというデメリットがある。
それに対し後置記法は、区切り文字が必要というデメリットがあるが、括弧が不要というメリットがある。そして何より、後置記法だとスタックで簡単に計算ができるというメリットがあり、プログラムを書く上ではこれが何よりのメリット。
ソース
OPERATOR_PRIORITY = {"*" => 1, "/" => 1, "+" => 2, "-" => 2 } OPERAND = Regexp.union(/^[1-9]\d*$/, /^0\d*$/, /^0x[\da-f]+$/i) OPERATOR = Regexp.union(*OPERATOR_PRIORITY.keys) TOKEN = Regexp.union(OPERAND, OPERATOR) # 中置記法の文字列をトークン分割する def infix_tokenizer(line) tokens = line.split(/\b/).map! {|t| t.strip} tokens.each {|token| raise "#{token} は認識できない識別子です。" if TOKEN !~ token } tokens end # lhsがrhsより優先度が低ければtrue def less(lhs, rhs) if OPERAND =~ rhs true elsif OPERAND =~ lhs false else OPERATOR_PRIORITY[lhs] >= OPERATOR_PRIORITY[rhs] end end # 中置記法を後置記法へ変換 def infix_to_postfix(infix) postfix = [] stack = [] infix.each {|token| postfix << stack.pop while !stack.empty? and less(token, stack.last) stack << token } postfix += stack.reverse end infix_string = "3+ 4 * 5+0x52dF*07" infix = infix_tokenizer(infix_string) postfix = infix_to_postfix(infix) puts "元の文字列: #{infix_string}" puts "中置記法: #{infix.join(" ")}" puts "後置記法: #{postfix.join(" ")}"
実行結果
元の文字列: 3+ 4 * 5+0x52dF*07 中置記法: 3 + 4 * 5 + 0x52dF * 07 後置記法: 3 4 5 * + 0x52dF 07 * +
後置記法を評価する
後置記法を評価するには、後置記法の式の要素を一つずつ取り出し、その要素が被演算子であればスタックへ積み、演算子であれば、スタックの上から2つを取り出し、その2つの値をその演算子で評価する。評価した結果はスタックへ積み、以後、式の要素がなくなるまでこれを続ける。
これを行うだけで、最後にスタックに残った要素が評価結果となっているという仕組み。
以下のソースでは、スタックから取り出した値の評価に eval関数を利用している。演算子ごとに分岐したり、被演算子の進数変換がめんどうだったので…。
また、自力で計算した結果が合っているかどうかなの検算用にも eval関数を利用している。
ソース
OPERATOR_PRIORITY = {"*" => 1, "/" => 1, "+" => 2, "-" => 2 } OPERAND = Regexp.union(/^[1-9]\d*$/, /^0\d*$/, /^0x[\da-f]+$/i) OPERATOR = Regexp.union(*OPERATOR_PRIORITY.keys) TOKEN = Regexp.union(OPERAND, OPERATOR) # 中置記法の文字列をトークン分割する def infix_tokenizer(line) tokens = line.split(/\b/).map! {|t| t.strip} tokens.each {|token| raise "#{token} は認識できない識別子です。" if TOKEN !~ token } tokens end # lhsがrhsより優先度が低ければtrue def less(lhs, rhs) if OPERAND =~ rhs true elsif OPERAND =~ lhs false else OPERATOR_PRIORITY[lhs] >= OPERATOR_PRIORITY[rhs] end end # 中置記法を後置記法へ変換 def infix_to_postfix(infix) postfix = [] stack = [] infix.each {|token| postfix << stack.pop while !stack.empty? and less(token, stack.last) stack << token } postfix += stack.reverse end # 後置記法の式を評価 def eval_postfix(postfix) stack = [] postfix.each {|token| case token when OPERATOR rhs = stack.pop lhs = stack.pop stack << eval([lhs, token, rhs].join) when OPERAND stack << token end } stack.pop end infix_string = "3+ 4 * 5+0x52dF*07" infix = infix_tokenizer(infix_string) postfix = infix_to_postfix(infix) result = eval_postfix(postfix) puts "元の文字列: #{infix_string}" puts "中置記法: #{infix.join(" ")}" puts "後置記法: #{postfix.join(" ")}" puts "結果: #{result}" puts "検算: #{eval(infix_string)}"
実行結果
元の文字列: 3+ 4 * 5+0x52dF*07 中置記法: 3 + 4 * 5 + 0x52dF * 07 後置記法: 3 4 5 * + 0x52dF 07 * + 結果: 148528 検算: 148528
中置記法の括弧に対応する
中置記法では計算の優先順位を示すために括弧が必須。電卓としては括弧をサポートしないわけにはいかない。括弧に対応するために、トークン分割と、中置記法から後置記法への変換関数に若干手を加えた。
ソース
OPERATOR_PRIORITY = {"*" => 1, "/" => 1, "+" => 2, "-" => 2 } OPERAND = Regexp.union(/^[1-9]\d*$/, /^0\d*$/, /^0x[\da-f]+$/i) OPERATOR = Regexp.union(*OPERATOR_PRIORITY.keys) BRACKET_BEGIN = '(' BRACKET_END = ')' BRACKET = Regexp.union(BRACKET_BEGIN, BRACKET_END) TOKEN = Regexp.union(OPERAND, OPERATOR, BRACKET) # 中置記法の文字列をトークン分割する def infix_tokenizer(line) tokens = line.split(/\s+|\b/).map{|t| OPERAND =~ t ? t : t.split(//)}.flatten tokens.each {|token| raise "#{token} は認識できない識別子です。" if TOKEN !~ token } tokens end # lhsがrhsより優先度が低ければtrue def less(lhs, rhs) if OPERAND =~ rhs true elsif OPERAND =~ lhs false elsif BRACKET =~ lhs true elsif BRACKET =~ rhs false else OPERATOR_PRIORITY[lhs] >= OPERATOR_PRIORITY[rhs] end end # 中置記法を後置記法へ変換 def infix_to_postfix(infix) postfix = [] stack = [] infix.each {|token| case token when BRACKET_BEGIN stack << token when BRACKET_END postfix << stack.pop while stack.last != BRACKET_BEGIN stack.pop else postfix << stack.pop while !stack.empty? and less(token, stack.last) stack << token end } postfix += stack.reverse end # 後置記法の式を評価 def eval_postfix(postfix) stack = [] postfix.each {|token| case token when OPERATOR rhs = stack.pop lhs = stack.pop stack << eval([lhs, token, rhs].join) when OPERAND stack << token end } stack.pop end infix_string = "(3+ 4) * (5+0x52dF)*07" infix = infix_tokenizer(infix_string) p infix postfix = infix_to_postfix(infix) result = eval_postfix(postfix) puts "元の文字列: #{infix_string}" puts "中置記法: #{infix.join(" ")}" puts "後置記法: #{postfix.join(" ")}" puts "結果: #{result}" puts "検算: #{eval(infix_string)}"
実行結果
["(", "3", "+", "4", ")", "*", "(", "5", "+", "0x52dF", ")", "*", "07"] 元の文字列: (3+ 4) * (5+0x52dF)*07 中置記法: ( 3 + 4 ) * ( 5 + 0x52dF ) * 07 後置記法: 3 4 + 5 0x52dF + * 07 * 結果: 1039780 検算: 1039780
見え方のテスト用ページ
必ず1日1つの記事で登録すること。
日記の日付とタイトルは表示されないので、見出しがそのページのタイトルとなる。
なので見出しの順番が変わる。
以下は、その例。
小見出し
小見出し後の文章小見出し後の文章小見出し後の文章小見出し後の文章小見出し後の文章小見出し後の文章小見出し後の文章小見出し後の文章小見出し後の文章小見出し後の文章小見出し後の文章小見出し後の文章小見出し後の文章小見出し後の文章。
小見出し後の文章小見出し後の文章小見出し後の文章小見出し後の文章。
小見出し後の文章。
小々見出し
小々見出し後の文章小々見出し後の文章小々見出し後の文章小々見出し後の文章小々見出し後の文章小々見出し後の文章小々見出し後の文章小々見出し後の文章小々見出し後の文章小々見出し後の文章小々見出し後の文章小々見出し後の文章小々見出し後の文章。
小々見出し後の文章小々見出し後の文章小々見出し後の文章。
小々見出し後の文章。
リストのテスト
リストのテストリストのテストリストのテストリストのテストリストのテストリストのテストリストのテストリストのテストリストのテストリストのテストリストのテストリストのテストリストのテスト。
- リスト
- リスト
- リスト
- リスト
リストのテスト2
リストのテスト2リストのテスト2リストのテスト2リストのテスト2リストのテスト2リストのテスト2リストのテスト2リストのテスト2リストのテスト2リストのテスト2リストのテスト2リストのテスト2リストのテスト2。
- リスト
- リスト
- リスト
- リスト
定義済みリストのテスト
定義済みリストのテスト定義済みリストのテスト定義済みリストのテスト定義済みリストのテスト定義済みリストのテスト定義済みリストのテスト定義済みリストのテスト定義済みリストのテスト
引用文のテスト
引用文のテスト引用文のテスト引用文のテスト引用文のテスト引用文のテスト引用文のテスト引用文のテスト引用文のテスト引用文のテスト引用文のテスト引用文のテスト引用文のテスト引用文のテスト引用文のテスト引用文のテスト引用文のテスト。
ここは引用文です。
ただのブロックとして使うこともできます。
整形済みテキストのテスト
整形済みテキストのテスト整形済みテキストのテスト整形済みテキストのテスト整形済みテキストのテスト整形済みテキストのテスト整形済みテキストのテスト整形済みテキストのテスト整形済みテキストのテスト整形済みテキストのテスト整形済みテキストのテスト整形済みテキストのテスト整形済みテキストのテスト整形済みテキストのテスト。
整形済みテキストです。
整形済みテキストのテスト(スーパーpre)
整形済みテキストのテスト(スーパーpre)整形済みテキストのテスト(スーパーpre)整形済みテキストのテスト(スーパーpre)整形済みテキストのテスト(スーパーpre)整形済みテキストのテスト(スーパーpre)整形済みテキストのテスト(スーパーpre)。
#!/usr/bin/perl -w use strict; print <<END;</ppp> <html><body> </body></html> END
texのテスト
texのテストtexのテストtexのテストtexのテストtexのテストtexのテストtexのテストtexのテストtexのテストtexのテストtexのテストtexのテストtexのテストtexのテストtexのテストtexのテストtexのテスト。
リンクのテスト
リンクのテストリンクのテストリンクのテストリンクのテストリンクのテストリンクのテストリンクのテストリンクのテストリンクのテストリンクのテストリンクのテストリンクのテストリンクのテストリンクのテスト。
http://www.hatena.ne.jp/
https://www.hatena.ne.jp/login
info@hatena.ne.jp
はてな
はてなのトップページ
isbn,asinのテスト
isbn,asinのテストisbn,asinのテストisbn,asinのテストisbn,asinのテストisbn,asinのテストisbn,asinのテストisbn,asinのテストisbn,asinのテストisbn,asinのテストisbn,asinのテストisbn,asinのテストisbn,asinのテスト。
isbn:4886487319
asin:4886487319
「はてな」ではじめるブログ生活―はてな公式ハンドブック
はてなダイアリーガイドブック―ウェブログでつながる新しいコミュニティ
- 作者: 水野貴明,はてな
- 出版社/メーカー: 毎日コミュニケーションズ
- 発売日: 2004/05
- メディア: 単行本
- 購入: 29人 クリック: 1,226回
- この商品を含むブログ (410件) を見る
はてなダイアリーガイドブック―ウェブログでつながる新しいコミュニティ
- 作者: 水野貴明,はてな
- 出版社/メーカー: 毎日コミュニケーションズ
- 発売日: 2004/05
- メディア: 単行本
- 購入: 29人 クリック: 1,226回
- この商品を含むブログ (410件) を見る
はてなダイアリーガイドブック―ウェブログでつながる新しいコミュニティ
- 作者: 水野貴明,はてな
- 出版社/メーカー: 毎日コミュニケーションズ
- 発売日: 2004/05
- メディア: 単行本
- 購入: 29人 クリック: 1,226回
- この商品を含むブログ (410件) を見る
テーブルのテスト
テーブルのテストテーブルのテストテーブルのテストテーブルのテストテーブルのテストテーブルのテストテーブルのテストテーブルのテストテーブルのテストテーブルのテストテーブルのテストテーブルのテストテーブルのテストテーブルのテストテーブルのテストテーブルのテストテーブルのテスト。
項目1 | 項目2 | 項目3 | 項目4 |
---|---|---|---|
1111111111111111111 | 22222222222222 | 33333333333333 | 44444444444 |
1111111111111111111 | 22222222222222 | 33333333333333 | 44444444444 |
1111111111111111111 | 22222222222222 | 33333333333333 | 44444444444 |
1111111111111111111 | 22222222222222 | 33333333333333 | 44444444444 |
1111111111111111111 | 22222222222222 | 33333333333333 | 44444444444 |