Perlさんとお話しながらプログラム

最近流行のRubyでも勉強するかと思って環境をダウンロード。
いろいろやってたのだが、あれは勉強しやすい。
というのもirbという対話的にRubyが実行できるのがあるから。
なるほど、これをPerlでパクってみようと思った。


そんな考えを持ってる人は当然多くて、そんなものはたくさんある。
ところがWindowsではうまく使えない。
Perl の対話環境 (jijixi’s diary)を参考に作ってみた。
これもUNIX前提で書いてあるような気もするが、これをさらに単純にすればWindowsにぴったりだった。
この方は、その後、perlshを使っているけど、ActivePerlにTerm::ReadLine::Gnuがうまく入らないので僕はできなかった。
ActivePerlじゃなくてCygwinのPerlでやればよさそうなもんだけどな。
しかしCygwinのPerlは最近使ってないからねぇ。


#!/usr/bin/perl
package perlsh;
#use strict;
#use warnings;
use Data::Dumper;
local $Data::Dumper::Terse = 1;
local $Data::Dumper::Indent = 0;
sub main::p {
my @args = @_;
print Dumper(@args);
}
my $__spool__ = "";
my $__flag__ = 1;
while (1) {
if($__flag__ == 1){ print "> "; }
else { print "* "; }
my $__line__ = <>;
if($__line__ =~ m/_\s*$/){ $__flag__=0; $__line__ =~ s/_\s*$/\n/; }
if($__line__ =~ m/^\s*$/){ $__flag__=1; }
$__spool__ .= $__line__;
if ($__flag__) {
my @__result__ = do {
package main;
eval($__spool__);
};
if($@){
print $@;
}else {
if(@__result__ && !($__line__ =~ m/;;$/)){
print "=> ", Dumper(@__result__);
}
print "\n";
}
$__spool__ = "";
}
}

ActivePerlでやるよと言ってるのに、一行目にインタプリンタを書くってどうなんだというのはあるわな。
もはや習慣だ。けどLinuxでもperl foo.plとかで動作させることが多いので、全く意味のない話だ。
それを言うと、いちいち$__line__ =~ m/_\s*$/なんて、省略できるmをいちいち書くのもどうなんだというのはあるわな。
use strict;とuse warnings;はいままで書かなかったが、どうも決まり文句らしい。
そしてそうなるといろいろ都合がある。いままでローカル変数をmyを付けて宣言してこなかったがそうするとひっかかる。
まぁしかしいちいちmyを使うのめんどくさいとか、あるなら使わなくていいとも言える。
このあたりはご自由に。けどフォーマルなプログラムではuse strict;しろというのはあるわな。
今回は開発中は使ったが、実行してみると都合が悪いので消すことに。いちいちプロンプトでmy叩くのめんどくさい。
$Data::Dumper::Terseあたりはグローバル変数。local付けてる事情はよくわからん。
main::pというサブルーチンを定義。これはプロンプト側から使えるよ。
このpはRubyのpにちなんだもので、変数の中身を適当な形式で表示というもの。
さて、オリジナルとの違いはTerm::ReadLineを使ってないこと。
WIndowsでは全くうまみがないから。なんか改行が入りまくってしっくりこない。
なので手動でプロンプトを表示して、$__line__=<>;で一行読み取りをやってる。
もっともこのままだと改行コードが最後に残るのですが、それはそれでいいとしましょう。
普段の状態は$__flag__が1で、一行で一回の実行を行う。
最後の行に_(アンダバー)をつけると$__flag__が0になって、そこから実行を保留する。
そして空行を入れると$__flag__が1になって、保留してた分を一気に実行する。
見ればわかるだろうが、_は除去されますよ。だから予想外の動作をすることもあるかもしれん。
さて、evelで実行する部分だけ、doブロックになってますね。ここだけmainパッケージにするためかな。
それでエラーが$@に入るので、これをチェック。
そして、返り値を@__result__に受け取ってるので、空でなくて、$lineの最後が;;で終わってないなら表示。
そう、;;で終わるようにすれば返り値の表示抑制できるようにした。
ここで、Dumperを使ってるが、これはData::Dumperパッケージので、適当な形式の文字列に変換するの。
すっ飛ばしてたがmain::pの中でも使ってますね。


さて、実際に使ってみます。
これをperlsh.plとしてパスが通ってるC:\Perl\binにおくことにします。
それでperlsh.plとすれば動きますね。関連づけされてれば。
しかしかっこわるいのでperlsh.batを作成します。

@echo off
perl -x -S perlsh.pl %*

これでいいでしょう。C:\Perl\binにあるbatファイルを参考にしました。
これでperlshコマンドとしてcmdとかで呼び出せるよ。
これで遊んでみますか。

> open IF,'=> 1
> @lines = ;;

> close IF
=> 1
> @lines+0
=> 500
> foreach(@lines){ $a += $_; }
=> ''
> $a/=@lines
=> '158.764'
> $u=0;
=> 0
> foreach(@lines){_
* $u += ($a-$_)*($a-$_);
* }
*
=> ''
> $u /= (@lines -1)
=> '7195.88006412826'
> sqrt($u);
=> '84.8285333135511'
> exit

まぁ慣れれば速いかも知れないけどね。
しかしTerapadでスクリプトを書くよりも速く結果が得られるとは思いますよ。
値が気になったら、@lines+0とかすればわかりますからね。
+0をつけてるのは個数のスカラーを取得するため。$#linesとすると@lines-1が得られますけどね。
まぁ本当は@linesとやって痛い目にあって、+0すればいいやと思っただけです。ええ。
こんな風にぼちぼち勉強できます。
ただ複数行処理するときとそうじゃないときで操作が違うのが難しい。
まぁしかし、foreachとかwhileの簡単な処理なら一行で書けますからね。今回はわざと複数行で書いたけど。
こんな風に、ちょっと手の込んだ電卓をやりたいときには使えそうですね。
そうそう、なぜかカーソルキーで以前のコマンドが呼び出せる。おもしろいけど、なんでだろ?