トップ «前の日記(2007-08-03) 最新 次の日記(2007-08-05)» 編集

日々の破片

Subscribe with livedoor Reader
著作一覧

2007-08-04

_ 鋼鉄の息子

Fortressっていうプログラミング言語を作ってる人は、CLtLの執筆者ではあるけれど、Schemeを作ったのはSussmanですね。と思いこんでたら、He is a co-creator of the Scheme programming language. なのか。

_ プログラムの書き方

PCからPCへあるサブディレクトリをまるごとコピーして、その中のプログラムを実行しようとしたらエラーになった。というのは、一緒に入っているデータファイル(ここではWindowsのiniファイルとする)が実に40個ほどあるのだが、すべてに共通するパラメータに、ドライブレターの指定があって、それがもとのPC用にD:となっているからだ。すべてを新しいPCの環境に合わせてC:にしなければならない。

さっそく、エクスプローラを起動して、最初のiniファイルをダブルクリックしてメモ帳を開いた新人のN君に対して、先輩のOさんが「ちょっと待った」と声をかけた。「君は、これから40個のファイルを修正するつもりかい?」

「もちろんですよ。だって、そうしなければ動かないじゃないですか」とN君は不思議そうにOさんの顔を見る。

「よく考えてみろよ。40個といえば、大した数だ。それをいちいち手で直していたら、直し忘れだって出てくるのじゃないか?」

「大丈夫です。エクスプローラを詳細表示モードにして更新時刻でソートすれば、直し忘れたファイルはすぐに見つかりますよ」とN君は得意そうに言う。

●手軽な検証手段が確保されている

「じゃあ、とりあえず、今開いたファイルだけ直してみなよ」とOさん。

N君は、メモ帳の「編集」メニューをクリックして「置換」を選択して、「検索する文字列」に「D:」、「置換後の文字列」に「C:」を入力して「すべて置換」を実行した。

「おいおい、『すべて置換』だと間違えて置換する心配はないのかい?」

と言うOさんに、どうしてもケチをつけたいようだな、と気づいたN君はきっぱりと、「Oさんもごぞんじでしょう。この中で:が使われているのはドライブレターの指定だけですよ。だから大丈夫でしょ。」

●機械的な操作を適用可能

「ということはだ」とOさんはにこにこしながらN君に言った。「君はエクスプローラの機能を使って、修正したかどうかを確認すると言ったね。つまり、コンピュータに作業を任せるということの意義を理解しているということだ。しかも、『すべて実行』を使って書き換えられるという条件もわかっている。いちいち人間、つまり君がD:はどこだと探して、それをC:に置き換えるなんてする必要はないってことをだ」

「もちろんです」

保存して、次のファイルを開こうとするN君を制してOさんは続けた。

「そこまで、コンピュータに作業を任せることができるなら、プログラムにやらせてみたらどうだ?」

「え? でもプログラムって難しいんじゃないですか?」

「そりゃ、難しいプログラムは難しいさ。でも、これからやろうとしていることは、そんなに難しいわけじゃない。僕らがやらなければならない作業手順をプログラムで置き換えていくだけのことだ。それに、プログラムにしておけば、次に似たような作業が必要になったとき――ご存じのように元のPCは潰してしまったけど、しばらくするとまた返されてくるからコピーバックしなきゃならない。」

「あ、つまり、元のiniファイルも保存しておかなきゃだめですね」

と、N君は気づいて、originalという名前のフォルダをエクスプローラを操作して作り始めた。そのリアクションの素早さを嬉しそうに眺めながらOさんは続けた。

「それはひとつの方法だよね。でも、作業を逆に実行してもすむよね。つまり、コピーして、C:をD:に変えるということだ」

「でも、それはめんどうですよ」

と、N君は、元のフォルダーのiniファイルを全部選択して、originaloへ右クリック−ドラッグ−ドロップしてコンテキストメニューから「コピー」を選んだ。

「でも、君は、今、まさにそれをしようとしてるじゃないか」

「だって、それはしょうがないじゃないですか」

「だから、その作業をプログラムにしようということだ。というか、すでにoriginalフォルダにoriginalじゃないファイルが紛れているわけだが……」

「でした」といって、N君は更新日時でソートしてダブルクリックしてメモ帳でオープンして編集メニューから……

「ああ、なるほど」と、N君。「なんとなく、Oさんの言ってることがわかりました」

#こういう文章って、なんかめんどうだな。

_ プログラムの書き方(2)

「じゃあ、プログラムを書こう。まず、エクスプローラからコマンドヒアで、このディレクトリにコンソールを開いて……ねぇ。インストールされてねぇ」とOさん。

「なんですか、それ?」とN君。

#ここで、Oさんがめんどうになって帰ってしまうという展開にすると楽だな。

_ プログラムの書き方(3)

ぶつぶつ言いながら、Oさんはコマンドプロンプトを開くと、cd [スペース] と打ち込んでから、エクスプローラのアドレスバーの文字列をドラッグするとコマンドプロンプトへドロップした。

「だー、Vistaか!」

何も起きないので、Oさんは呪詛をまきちらした。

「シフト右クリックで、パスとしてコピーですよ」と助け舟を出すN君。

_ プログラムの書き方(4)

Oさんはコマンドプロンプトで、notepad drivechange.rbと打ち込んだ。そんなファイルは無いから作るかというメモ帳に、OKをクリックしておもむろに、説明を始めた。

「最初に、こう書く」

#!/usr/local/bin/ruby -Ks

「Oさん、これ、Unixマシンじゃないですよ」と頭大丈夫か、と言わんばかりのN君。

「そりゃそうだが、Rubyがシーバン行を参照して、rubyが起動される設定ならば、オプションを読むっていう仕様なんだから、しょうがないのだ。-Ks ってのは、シフトJISを使うよ、という指定。もっとも、この中には出てこないとは思うけどね。とりあえず、やっとく」

「ああ、よくプログラミング言語入門に出てくる、『おまじない』というばかな言い回しで示される決まりのことですね」と、N君は辛辣だ。

「確かに、ばかな言い回しだとは思うが、こんなあたりまえのことを説明するのはくそめんどうだから、しょうがないってことが良くわかったよ」とOさん。

「まあ、それはそれとして、手順を考えてみよう。人間がやる場合には、エクスプローラから拡張子がiniのファイルを順番にダブルクリックして処理していくことになるよね」と言いながらOさんは次のリストを打ち込んだ。

Dir.open('.').each do |file|
 next unless /\.ini\Z/i =~ file
  p file
end

保存して、コマンドプロンプトで、「ruby drivechange.rb」と打ち込む。すると、コンソールに拡張子がiniのファイルが""付きで出力された。

「pってのは、デバッグ用の出力処理だけど、見ての通り、これで拡張子がiniのファイルだけを処理できる仕組みができたのはわかるかい?」

「結果はわかるけど、全然、意味はわかりません。Dirってなんですか、openってなんですか、かっこはなんですか?」とN君。

「いちいち説明していると面倒だから、おまじないだ。でもDirってなにかとか、openってなにかとか、薄々はわかってるんだろ」とOさん。

「まあ、確かに。nextもわかるかな。でもunlessと次の/とか=~とかはまったくわかりません」

「それは宿題」とあっさり先へ進むOさん。

Dir.open('.').each do |file|
  next unless /\.ini\Z/i =~ file
  File.open('tmpfile', 'w') do |d|
    s = File.open(file, 'r')
    d.write s.read.gsub(/d:/i, 'C:')
    s.close
  end
end

「これで、tmpfileっていういい加減な名前の中間ファイルを書き込みモードで開いてから、iniファイルの1つから読み込んだ内容のd:をc:に書き換えたものを書きだしてるわけだ。だから、実行すると、最後のiniファイルの内容から置き換えができたtmpfileっていうファイルが残る」と、Oさんははしょった説明をしながら、コマンドプロンプトに「ruby drivechange.rb」と打ち込んだ。「tmpfileができてるはずだから、確認してくれ」

N君は言われるままに、エクスプローラからできたてのtmpfileを……「ダブルクリックできないですよ。せめて、tmpfile.ini……とやるともしかしたら、iniファイルだからまずいことになるのかな? では、tmpfile.txtにしてくれりゃいいのに」とぶつぶつ言いながら、一覧からプログラムを選択でメモ帳を開いて中を見る。「なるほど」

「うまくいくようだから、完成させてしまう。つまり、1ファイル処理するたびに、tmpfileを元のファイルに上書きしてけば良いわけだろ」と言いながらOさんはプログラムを修正する。

require 'fileutils'
 
Dir.open('.').each do |file|
  next unless /\.ini\Z/i =~ file
  File.open('tmpfile', 'w') do |d|
    s = File.open(file, 'r')
    d.write s.read.gsub(/d:/i, 'C:')
    s.close
  end
  FileUtils.cp 'tmpfile', file
end

「で、実行だ」と、再度コマンドプロンプトから「ruby drivechange.rb」と打ち込む。

F5を押して、すべてのファイルの更新時刻が変わったのを確認したN君は、適当に開いてはファイルの中のD:がC:に置き換わったのを確認して、「ほー、こりゃ楽だ」

「で、完成」と、drivechange.rbを開いたメモ帳を閉じようとするOさんを制したN君、「あと1行追加したいな」と、言いながら

require 'fileutils'
 
Dir.open('.').each do |file|
  next unless /\.ini\Z/i =~ file
  File.open('tmpfile', 'w') do |d|
    s = File.open(file, 'r')
    d.write s.read.gsub(/d:/i, 'C:')
    s.close
  end
  FileUtils.cp 'tmpfile', file
end
FileUtils.rm 'tmpfile'

「なんだ、知ってるじゃん」とOさん。

「いや、知りませんが、cpでコピーできるなら、rmでリムーブできるのかと思って」と言いながらN君もコマンドプロンプトから「ruby changedrive.rb」と打ち込んだ。それからエクスプローラをF5して、tmpfileがなくなったことを確認して、「で、お勧めは?」。

「とりあえず、File、FileUtils、Dir、String、正規表現、あとは制御構造と演算子を覚えりゃいいんじゃないかなぁ。オンラインリファレンスがあるはずだから、適当に検索して探したら」と言いながら立ち去ろうとするOさん。実は、考えていたよりも時間がかかったので、N君に人間プログラムをさせればよかったかな、と少し後悔してたりして。

「で、まとめると」とN君。「機械的な作業だな、と気づいたら、作業手順を考えて、それを外側から順番にプログラムとして記述していく、ということですね。それにくわえて、破壊的な作業をする前に、本当にその時点までのプログラムが予期した動作をするか確認する、と」

「まあ、そうかな」

「で、とりあえず、このプログラムで使ってるものを調べる。つまり、ファイル操作、選択、文字列(という用語をなぜか知っている)、正規表現」

「まあ、そうだな」

たのしいRuby 第2版 Rubyではじめる気軽なプログラミング(高橋 征義/後藤 裕蔵)
本日のツッコミ(全4件) [ツッコミを入れる]
_ ただただし (2007-08-04 07:03)

後半、N君の洞察力が良すぎという気がします:-)

_ rubyco (2007-08-04 09:04)

fileutilsではなくpathnameを最初から使うというほうがよいのでは、と思いましたがいかがでしょう。

_ arton (2007-08-04 09:28)

>N君の洞察力<br>願望が入ってますよね。<br>>pathname<br>fileutilsのとりあえずの楽さというか。N君はUNIXコマンドを知っているという前提(シーバン行へのツッコミ)があると、fileutilsを教えるとすごく楽そう。本当を言えば、僕があまりpathnameを使わないからなんですが。

_ arton (2007-08-04 10:07)

とか言わずにpathname使うように変えてみますね。(後で)


2003|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|

ジェズイットを見習え