<< 前 ホーム 次 >>

bakaid: 200511131

おいおい、なんでAdobe Readerがアップデートされただけでシステム再起動し
なきゃいけねぇんだよ。何か勘違いしてんじゃねぇか? Adobeさんよ。

こちとら、『はい、そうですか』っつって落とせるほどPC遊ばしてるわけじゃ
ねぇんだよ。もちっとユーザのこと考えろや。

MSのAPIがダッセーってんならしっかり圧力かけろや。天下のAdobe様だろ?

--

``Software Tools in Pascal''読んでることは書きましたね。そのサンプル・
コードを動かすのにFree Pascal使ってるんです。

.deb化されてるOSSのPascal処理系はいくつかあるんですけど、1つがこのFree
Pascal。それにGNU Pascalもあります。あとp2cというCへのトランスレータも
あるみたいですね。p2cは試したことがないです。

Free Pascalの特徴は、Borland系Pascalへの互換性に重点を置いてるところ。
その逆に、GNU Pascalの特徴はISO Pascalへの互換性に重点を置いてるところ
です。

自分みたいな半端な使い方をするんじゃ、どっちがいいともいえないんですけ
ど。ただ、ドキュメントはFree Pascalのほうが充実してます。といっても、
どちらも肝心のチュートリアルみたいなものはないんですけど。それでも、
GNU Pascalのinfoマニュアルはまだ未整備という観があるのに対して、Free
Pascalのほうはリファレンスはしっかりしてます。

STiPのコードはえらく古くて、Free Pascalの機能を活かせばモダンな書き方
もできるんでしょうけど、とりあえずそれが目的じゃないので。でも、こうし
た古いサンプルをサックリ動かせないのがPascalの困ったところ。C言語は
K&Rが出たころからそう大して変わってませんけど、Pascalは激変してま
すよね。

STiPでは、OS依存の部分はライブラリで隠してるんですけど、それを移植しな
いとFree Pascalで動かないのは当たり前の話で。それをニワカPascalユーザ
の自分がやんなきゃいけないというのは結構辛いもんがあります。

サンプルの中でmessageという手続きが頻出します。これは標準エラー出力に
文字列を出力するというごく単純なものです。でも、このころのPascalには標
準エラー出力というものがなかったらしく、その実装は本で紹介されてません。

Free Pascalのドキュメントを何度も検索してようやくできたのがこれ:

procedure message (msg : string);
begin
    writeln(stderr, msg);
end;

なんのことはない結論なんですけど、これが分かるまでえらい苦労しました。
他にも、text型が本で使われてるUCB PascalとFree Pascalとで微妙に違って
たりとか、いろいろあるんですけど。

標準エラー出力はともかく、ファイル操作で互換性がなくなってるといったこ
とはCじゃちょっと考えられないところです。Pascalの当初の目的は教育用と
いうことだったので、そもそもSTiPのような使い方をすること自体問題だった
ともいえるんでしょうけどね。ただ、この時代は多分、CよりもPascalのほう
が広く使われてたんでしょうね。STiPが出たのが1981年、K&Rが出たのが
1978年ということなんで。このころの3年差は今ほど大きな差じゃないでしょ
う。

--

それから、STiPは#includeを使ってるんです。このころのPascalは分割コンパ
イルができなくって、それはKernighan氏が書いた例の``Why Pascal is Not
My Favorite Porgramming Language''で指摘されてるところです。ちなみに、
この論文はSTiPを出す直前に発表されたものです (正確には発表されなかった
らしいんですけど)。

で、その#includeを処理するプリプロセッサも本の中で作ることになるんです
けど、それが出てくるのは後半のほうなわけで。そこまで待ってらんないです
から、cppを使うことを思い立ちました。生でcpp使った経験がないのでどんな
ものかと思ったんですけど、概ね良好な結果でしたね。ワケワカラン出力され
るときもあるんですけど。

program outer (input, output);
uses unix, sysutils;  { for syslog }
#include "globdefs.p"
#include "prims.p"
#include "utility.p"
#include "archive.p"
begin
    initio;
    archive;
end.

これがarchiveをコンパイルするときに一番外に来るファイルです。で、これ
を:

$ cpp <outer.p

なんてやると:

# 1 "<stdin>"
# 1 "<built-in>"
# 1 "<command line>"
# 1 "<stdin>"
program outer (input, output);
uses 1, sysutils; { for syslog }
# 1 "../globdefs.p" 1
{ globdefs (FreePascal) -- gloabl constants, types and variables (p.324) }

const

{ standard file descriptors. subscripts in open, etc. }
...

なんていう出力になります。なんか``#''で始まる余計が行があるのでそれを
削ります:

$ cpp <outer.p | grep -v '^#'
program outer (input, output);
uses 1, sysutils; { for syslog }
{ globdefs (FreePascal) -- gloabl constants, types and variables (p.324) }

const

{ standard file descriptors. subscripts in open, etc. }
...

で、コンパイルに失敗するんで、よく見てみると:

uses 1, sysutils; { for syslog }

なんていう行があります。これはもともと:

uses unix, sysutils;  { for syslog }

という行。なぜかunixが1に置き換えられちゃってます。cppのinfoを読むと、
余計な置換をしてほしくないときには:

$ cpp -ansi <outer.p | grep -v '^#'

とやるんだそうです。ん〜、奥深い。

--

それと、Pascalのコードは当然Emacsで入力してるわけですけど、そのPascal 
モードがまた気に喰わない。タブ幅が3だし、変数や引数のリストをきれいに
そろえようとする。STiPではタブ幅は4ですし、自分もそっちのほうが好みで
す。引数リストをそろえるっていうのは:

i     : integer
state : boolean;

みたいなヤツですね。これ、自分は大嫌いです。やるとしても、せいぜいカッ
コに合わせる程度:

function doit(i : integer;
              state : boolean);

もちろん、これは行を折り返さないといけないときであって、引数を1行に1つ
しか書かないなんてこともやりません。

定数宣言を並べるときとか、そろえたほうが見やすくて抜けを防げるっていう
場合もありますけどね。

何度も書いてますけど、コードは上からシーケンシャルに読むものです。全体
のパッと見の印象は重要じゃありません。きれいきれいにしたいならワープロ
でコードを書いたほうがいいでしょ。そのほうがエディタでゴチャゴチャやる
より全然いいですし、自分は止めませんよ。

で、hookです:

(defun my-pascal-mode-hook ()
  (setq pascal-indent-level 4)
  (setq pascal-auto-lineup nil))
(add-hook 'pascal-mode-hook 'my-pascal-mode-hook)

これでも引数リストをvarがあるかないかで合わせようとするガンコっぷりな
んですけどね。

--

STiPで不満な点の1つは、構造体が思ったほど活用されてないことです。ポイ
ンタが使えないとか、いろいろと制約はあったんでしょうけど、もうちょっと
データ構造をはっきりさせたほうが良かったんじゃないかと思います。

構造体の話にしてもそうですけど、『STiPをモダンな言語で書き直したものが
あったほうがいい』と思う理由がいくつかあります。もう1つの理由がGCです
ね。GCがないと参照変数使いまくりになっちゃって、コードをパッと読んだだ
けじゃどの変数が変わるのか見当がつきません。

もう1つがテスト。こないだも書いたとおり、STiPでもテストの重要性は強調
されてます。でも、それがコードや設計の進め方といったものに反映されては
いません。結局、ビッグバンになっちゃってます。コードをガーッと入力して、
最後にコンパイルして実行するっていう。そうすると、バグが出たときに、ど
こに問題があるのか見つけるのが難しいんですね。本とニラメッコしてプログ
ラムの先頭から1行1行目で確かめるのがオチです。確かにテストまでやると、
紙数が大幅に増えちゃうっていうのは問題なんですけど。

最後に外部ソートのプログラムをRubyで書き直したものを載せておきます。
今じゃUNIXのsortコマンドで必要十分以上なんですけど (確か、内部ソートと
外部ソートを自動的に切り換えてくれるはず)、ちょっと面白い実装になった
んで。

# sort -- external sort of text lines
MAXLINES = 3

class Templine
  @@index = '0'

  def initialize
    @filename = prefix + @@index.succ!
    @input = nil
    @line = nil
  end
  attr_reader :filename, :line

  def prefix
    return 'stemp'
  end

  def open_input
    @input = open(@filename)
  end

  def read_line
    @line = @input.gets
  end

  def close_input
    @input.close
    File.delete(@filename)
  end
end

def divide_into_files(input)
  templines = []
  lines = []
  loop do
    lines.clear
    MAXLINES.times do
      line = input.gets
      break unless line
      lines << line
    end
    break if lines.empty?
    lines.sort!
    templines << Templine.new
    File.open(templines[-1].filename, 'w'){|output| output.write(lines.join)}
  end
  return templines
end

def merge(templines, output)
  loop do
    templines.sort! do |x, y|
      if x.line.nil? && y.line.nil?
        0
      elsif y.line.nil?
        -1
      elsif x.line.nil?
        1
      else
        x.line <=> y.line
      end
    end
    break unless templines[0].line
    output.print(templines[0].line)
    templines[0].read_line
  end
end

templines = divide_into_files($stdin)
templines.each{|ech| ech.open_input}
templines.each{|ech| ech.read_line}
merge(templines, $stdout)
templines.each{|ech| ech.close_input}

本家Permlink

<< 前 ホーム 次 >>


Copyright © 1905 tko at jitu.org

バカが征く on Rails