単項マイナスと構文解析
単項マイナスとは 単項マイナスと括弧 括弧なし単項マイナスを許容する場合のBNF calcの場合
2023/10/29 追記:この記事は新しく書き直しました。
古い記事で使っていたGitHubのCalc
が大幅にアップデートされたためです。
そこで、この記事に合うようなプログラムsimple_calc
を新たに作りました。
このプログラムは本レポジトリの_example/simple_calc
にあります。
Rubyプログラムは
$ ruby ファイル名
で起動できるのですが、「ruby」と入力するのは煩わしいものです。 アプリ作成中は仕方がないとしても、完成したアプリはファイル名だけで起動したいですね。 それを実現するには、次の3行をファイル名の先頭に付けます。 この方法はRubyのドキュメントで紹介されています。
#!/bin/sh
exec ruby -x "$0" "$@"
#!ruby
お呪いのようなものだと考えて、コピペしても構いません。
一応説明すると、まず1行目の#!
はシバン(shebang)と呼ばれ、Unix系のOSではそのスクリプトを実行するプログラムを指定します。
この場合は「/bin/sh
によってこのファイルを実行する」のですから、シェルスクリプトとしての実行になります。
(/bin/bash
ではなく/bin/sh
となっているのは、システムによってはbash以外のシェルが使われているかもしれないからです。
/bin/sh
はいろいろなシェル共通の呼び出しを提供します)。
2行目はシェルのコマンドで、exec
はそのシェルのプロセスで(新規プロセスを生成せずに)コマンドを実行する、というものです。
$0
は起動されたファイル名(スクリプトファイルのファイル名)、$@
は引数すべてを表します。
これから、もしこのスクリプトファイルのファイル名がabc
でコマンドラインから次のように呼ばれたとすると、
abc file1 file2
まず、/bin/sh/
が起動され、2行目が実行されます。
2行目は
ruby -x abc file1 file2
とコマンドラインから入力するのとほぼ同じことになります。
-x
はrubyのオプションで、「スクリプトを読み込む時に、`#!’で始まり, “ruby”という文字列を含む行までを読み飛ばす」というものです。
このことにより、最初の3行(#!/bin/sh〜#!ruby)が読み飛ばされ、4行目からrubyプログラムが実行されます。
Unix系OSのシバングは指定されたファイルを実行するので、
#!/usr/bin/ruby
でも良いように思われますが、これだとまずい場合もあります。
最もありそうなケースはrbenvでインストールしたrubyです。
rubyは$HOME/.rbenv以下に保存されるので、/usr/bin/rubyでは呼び出せません。
($HOMEはユーザのホームディレクトリで、シェルからは~
でも参照できます)。
このrubyはシェルから呼ぶことにより起動できるので、いったんシェルを起動してからrubyを起動する、という面倒なやり方が必要なのです。
以上の説明でたいていは良いのですが、Gem(Rubyのライブラリ)の実行プログラムでは上手く動かない場合があります。 その場合は次のようにします。
#!/usr/bin/env ruby
この方法が最も良いかもしれません。
Unix系OSではコマンドラインの構成が
コマンド 引数1 引数2 ・・・・
となっています。
Rubyでは、引数はARGV
という配列に代入されます。
例えば
$ ruby_echo Hello world
とコマンドruby_echo
(これはRubyスクリプトだとします)が呼ばれたとき、
ARGV[0]
には文字列”Hello”が代入されているARGV[1]
には文字列”world”が代入されているとなります。 コマンドラインでは半角空白が引数の区切り文字になります。 区切り文字はARGVの中には入りません。 もし、空白も入れたいというときには、シングルクォートを用います。
$ruby_echo 'Hello world'
この場合はARGV[0]
に文字列”Hello world”が代入されます。
引数は1個ということになります。
引数が何個あるかは配列の要素の数を返すメソッドsize
を使います。
ARGV.size
が引数の数です。
ruby_echo
のプログラムは簡単です。
#!/usr/bin/env ruby
print ARGV.join(' '), "\n"
配列ARGVの各要素をjoinメソッドで繋げて文字列にします。 そのとき要素の区切りには、引数’ ‘(半角空白)が用いられます。
コマンドの例として、電卓プログラムsimple_calc.rb
を考えてみましょう。
このプログラムは引数に与えられた式を計算して答を表示します。
例えば1+2*3
を引数に与えると7を表示します。
$ ruby simple_calc.rb 1+2*3
7.0
$
このとき、注意すべき点が3つあります。
*
を使う7
ではなく7.0
となるこのプログラムではs_calc
というRubygemsのGemを使います。
事前にGemをインストールする必要があります。
それにはgem
コマンドを使います。
$ gem install s_calc
プログラム中でこのGemをrequireするときはcalc
です。
s_calc
ではないことに注意して下さい。
このGemの使い方はかんたんです。
run
を引数付きで呼び出す。引数は評価したい数式の文字列。メソッドの返り値がその計算結果になるプログラムは次のようになります。
#!/usr/bin/env ruby
require 'calc'
if ARGV.size != 1
print "Usage: simple_calc expression\n"
print "Example: simple_calc 1+2*3\n"
else
c = Calc.new
print "#{c.run(ARGV[0])}\n"
end
Calc
名のGemを取り込むこのプログラムをコマンド名simple_calc
でインストールしたいとします。
その場合はディレクトリ$HOME/.local/bin
以下にファイルを置き、実行属性をつければよいのです。
もし$HOME/.local/bin
がない場合は、あらかじめ作成しておきます。
FileUtils
モジュールを使うのが便利です。
require 'fileutils'
include FileUtils
cp __dir__ + "/simple_calc.rb", "#{Dir.home}/.local/bin/simple_calc"
chmod 0755, "#{Dir.home}/.local/bin/simple_calc"
__dir__
はそのファイル自身の置かれているディレクトリの絶対パスを返すDir.home
はユーザのホームディレクトリ($HOME
と同じ)を返すメソッドcp
はFileUtils
モジュールのメソッドで、ファイルをコピーする。chmod
はFileUtils
モジュールのメソッドでファイルの属性を指定する。
0755
は8進整数を表す。このファイル属性は
実行属性をつけないと、ファイル名での起動はできません。 以上のようにインストールするとコマンドラインから
$ simple_calc
で起動できるようになります。
なお、ユーザ用のコマンドのディレクトリ名は$HOME/bin
でも大丈夫です。
アプリケーションを作るときには、インストーラも作っておきましょう。
また、アンインストーラ(インストーラに含め、オプションで切り替えても良い)も入れておくと良いです。
アンインストールは、単に$HOME/.local/bin/simple_calc
を削除するだけなので、プログラムは簡単です。
File.delete("#{Dir.home}/.local/bin/simple_calc")
これまでのプログラムは、レポジトリの_example
フォルダに次の名前で保存されています。
simple_calc.rb
simple_calc_installer.rb
simple_calc_uninstaller.rb
上記ではユーザ領域へのインストールでしたが、他のLinuxユーザにも使えるようにするには/usr/local/binにインストールします。 このときは管理者権限が必要なので、Ubuntuなどではsuコマンドを使います。 例えば
$ su ruby simple_calc_installer.rb
のように起動します。
しかし、Linuxを共用で使っておりかつそのプログラムが実用性の高いものでない限り、システム領域にインストールする必要はありません。
Rubyの標準のテスト・スートはminitestです。 名前はミニですが、結構大きいプログラムで、ドキュメントの量もあります。 minitestは別の記事で詳しく述べようと思いますが、ここではポイントを絞って書きたいと思います。
テストプログラムはRubyで書きます。
ここでは、テストプログラムはsimple_calc
と同じフォルダにあるものとします。
simple_calc
のテストは、例えば次のようになります。
require 'minitest/autorun'
class TestSimpleCalc < Minitest::Test
def test_simple_calc
assert_equal("100.0\n", `ruby simple_calc.rb 10*10`)
end
end
minitesti/autorun
をrequireで取り込むMinitest::Test
のサブクラスとして定義するtest_
というプレフィックスをつける`ruby simple_calc.rb 10*10`
はKernelモジュールが定義するメソッドで、バックティック内の文字列を外部コマンドとして実行し、その結果を返す今回は、カレントディレクトリをテストプログラムのディレクトリに移してから実行します。 テストを実行すると次のように出力されます。
$ ruby test_simple_calc.rb
Run options: --seed 3936
# Running:
.
Finished in 0.106165s, 9.4193 runs/s, 9.4193 assertions/s.
1 runs, 1 assertions, 0 failures, 0 errors, 0 skips
$
ドットはテスト項目、つまりTestSimpleCalcの各テスト・メソッドを表しています。 上記はテストが成功したときの表示です。
テストが失敗するときは、例えば次のようなメッセージが出力されます。
$ ruby test_simple_calc.rb
Run options: --seed 37133
# Running:
F
Finished in 0.106931s, 9.3518 runs/s, 9.3518 assertions/s.
1) Failure:
TestSimpleCalc#test_simple_calc [test_simple_calc.rb:5]:
--- expected
+++ actual
@@ -1,2 +1,2 @@
-"100.0
+"1000.0
"
1 runs, 1 assertions, 1 failures, 0 errors, 0 skips
Failureはテストで失敗したことを示しています。 ここでは100.0を期待していたのに実際は1000.0が返されたという失敗です。
この他にErrorが出ることがありますが、それはプログラムを実行した時にエラーがあったことを意味しており、テストの結果ではありません。
すべての場合をテストするのは無理なので、典型的な例をテストすることになります。 プログラムのエラーは境界で起こりやすいです。 例えば正負が問題になるプログラムでは0が境界です。 「(変数)>= 0」を使わなければいけないのに「(変数)> 0」を使うといったバグは0以外ではフェイル(失敗)が起こりません。 ですから「境界をテストする」ことは非常に重要です。
minitestについては、別の記事がありますので、参考にしてください。
簡単なドキュメントは付けておくべきです。 仮に公開しなくても、将来自分自身が見直す時に役に立ちます。 2週間、別の仕事をすると、元の仕事内容を思い出すのに結構な時間がかかります。 そのときにドキュメントは役に立つでしょう。
GitHubに公開する場合はReadme.mdのようなファイル名をつけることになっています。 拡張子のmdはMarkdown形式を表します。 Markdownはhtmlと比べ格段に見やすく、書きやすいので勧められる形式です。 Markdownの説明は、次の記事を参考にしてください。
ただし、はてな独自の記法(はてな記法など)はGitHubでは使えません(GitHubのMarkdownはGFM)。
プログラムを公開するならばGitHubは無料で、機能が充実していて、有力な選択肢です。 GitHubとGitについては「はじめてのJekyll+GitHub Pages」の中に書かれていますので、以下を参考にしてください。
が参考になります。 このうち第10章のSSHで使う方法は知らなくても大丈夫です。
今回はアプリ開発の実際を見てきましたが、いかがだったでしょうか。 簡単なアプリで良いのでぜひ作ってGitHubにあげてみてください。 作れば作るほどプログラミングのレベルは上がります。
単項マイナスとは 単項マイナスと括弧 括弧なし単項マイナスを許容する場合のBNF calcの場合
パーサ・ジェネレータとは 少し複雑な文法 四則(加減乗除)計算のBNF Racc で実装 クラス定義、BNFの記述部分 ヘッダー、インナー、フッター コンパイルと実行 演算子の優先順位と結合における左右の優先順位 まとめ
StrScanライブラリのドキュメント 字句解析とは StrScanライブラリ StrScanライブラリを使った字句解析 実例
lbtというgemを作って公開してみた lbtはどんなgemか ファイルの配置 lbt.gemspec Rakefile gemのビルド RubyGems.orgへのアップロード 補足・・rake/gempackagetaskサブライブラリについて
文字列のエンコーディングに頭を悩ませることはほとんどなくなりました。 なぜなら、どのアプリ、システムもUTF-8を使うようになったからです。 Rubyでもエンコーディングの問題が起こることはまず無いでしょう。 ですが、今回はエンコーディングの考え方を整理してみたいと思います。
Fiberを書いたときから、次はスレッドを書こうと思っていましたが、時間がかかってしまいました。 その理由は、期待したとおりのスレッドの効果がなかったためです。 今回はそのことを書きますが、これはRubyのスレッドの抱えている問題なのか、自分のやり方が悪いのかははっきりしていません。
Fiberは「ノンプリエンプティブな軽量スレッド」とRubyのマニュアルに記載されています。
今回はRubyプログラムから自動的にドキュメントを作成するRDocについて書きたいと思います。 私はこのことについて、エキスパートではありません。 この記事も、初心者の体験談だと考えてください。
Ruby/Gtkの記事を先日書いたときに、「これはかなり使える」という手応えを感じたので、WordBook(Railsで作った単語帳プログラム)のGTK 4版を作りました。 プログラムは「徒然なるままにRuby」のGitHubレポジトリに置いてあります。 レポジトリをダウンロードし、ディレクトリ_example/...
今回はGTK 3とGTK 4をRubyで使うライブラリについて書きたいと思います。
今回もRubyとGUIのトピックです。 Glimmerを取り上げます。
Rubyはグラフィックについて弱い印象があります。 しかし、グラフィックはデバイスに関することなので、言語そのものには直接の関係はないはずで、あるとすればライブラリです。 今後グラフィック関係のgemが開発されることに期待しましょう。
Rails7におけるシステムテストについて書きます。
前回作ったWordbook(リソースフル)のテストを書いてみます。 RailsのテストはminitestをRails用に拡張したものです。
今回はRailsの慣例に沿った形でWordbookを作り直します。
今回はWordBookの検索と削除についてです。
今回はRailsにおけるデータの作成と保存、そして変更について説明します。 そのベースになるモデルとデータベースの話から始め、appendとchangeの動作について詳しく説明します。
一般に、HTMLは文書の構造を表し、CSSはその体裁(見栄え)を表します。 Railsは最終的にCSSを含むHTML文書を出力するので、この2つについての理解が必須です。 この記事ではとくにCSSの人気ライブラリであるBootstrapを紹介します。 BootstrapはJavascriptも含んでいます。
Rubyの最も人気のあるアプリケーションであるRuby on Railsを取り上げようと思い、書き始めました。 予想してはいましたが、相当な分量になってしまいました。 そのため、何回かに分けて記事にすることにします。 また、対象となる読者のレベルをどうしようかと考えましたが、「徒然Ruby」が基礎的な内容から始ま...
Rubyのライブラリ管理システムのRubygemsとコマンドgemおよびbundlerについて説明します。
minitestについて連続して2回書いてきました。 「minitestはドキュメントが少ない」という人がいますが、私も同感です。 例えば、モックとスタブの説明も少ないです。 そこで、今回はmock.rbのソースコードを参考に、モックの私的ドキュメントを書いてみました。 あくまで私個人の考えであり、minites...
今回もminitestの話です。 mockとstubに焦点をあて説明します。
アプリ作成の記事でminitestを使いました。 今回はminitestについて、また一般にテストについて、私の考えを書こうと思います。
今回はメソッドの呼び出し制限ついて説明します。 呼び出し制限にはpublic、private、protectedの3つがあります。
今回は特異メソッド、特異クラス定義、名前空間、モジュール関数について説明します。
2023/10/29 追記:この記事は新しく書き直しました。 古い記事で使っていたGitHubのCalcが大幅にアップデートされたためです。 そこで、この記事に合うようなプログラムsimple_calcを新たに作りました。 このプログラムは本レポジトリの_example/simple_calcにあります。
if〜elsif〜・・・〜else〜endは皆さん良く使うでしょうか? これは場合分けで良く使われる方法です。 これと同様の制御構造にcase文があります。 Cのswitch文に似ていますが、より強力な機能を持っています。 if-else-endよりも高い能力があるといえます。
Procオブジェクトを生成するメソッドlambdaについて説明します。
今回はブロックを一般化したオブジェクトProcを説明します。
ブロック付きメソッドの作り方を説明します。
モジュールには名前空間とミックスイン(Mix-in)の2つの機能があります。 ここではミックスインについて説明します。
クラスの親子関係
Rubyの演算子とその再定義について書きます。
今回からクラスとインスタンスを定義、生成する方法を説明します
Kernelモジュールのメソッドはどこでも使うことができます。 そのメソッドの中には便利で有用なものが多いです。
ここでは私が便利だと思ったメソッドを紹介します。
実数
今回はシンボルとハッシュについて説明します。
文字列は最も使うオブジェクトのひとつです。 特にウェブ・アプリケーションでは、コンテンツだけでなくHTMLのタグやCSSを含めすべてが文字列です。 Rubyは文字列オブジェクトのメソッドが充実しており、またパターンマッチのための正規表現も充実しています。
配列は、どのプログラミング言語にもあると思います。 複数の要素を一括して扱うことができるのが配列です。 Rubyの配列はメソッドが充実しているので、プログラムを効率的、機能的に書くのに役立ちます。
今回の目標はインスタンスです。 インスタンスを説明するために、ローカル変数と文字列オブジェクトを事前に扱います。
今回はメソッド定義です。 メソッド定義はRubyの核心ですが、今回はトップレベルに限って説明します。 この限定によって、内容はかなり易しくなっています。
ブロックはRubyの特長です。 ブロックのおかげで記述が非常にすっきりと分かりやすくなります。 今回はブロックをイテレータの本体として使う方法を説明します。
ここではRubyの最も基本的なオブジェクトである整数について説明します。
「徒然なるままに」をネットで調べてみると、「することもなく、手持無沙汰なのにまかせてという意味」とありました。 まさに、自分の現状を言い当てた言葉。 しかも、ブログに書くネタもなかなか思いつかない日々。