Lisp覚書

関数

Lispで関数を呼び出すには関数名をカッコで囲む。関数にパラメータを渡したければそれも一緒にカッコに入れる。

(defun function_name (arguments)
 ...)

まず関数の名前と引数を記し、その後ろに関数を実装するコードを書いていく。

トップレベル定義

Lispではグローバルに定義される変数をトップレベル定義と呼ぶ。新しいトップレベル定義はdefparameter 関数で作ることができる。

(defparameter *small* 1)

アスタリスクはearmuffsと呼ばれる習慣。ローカル変数と区別するのに便利。

REPL(read-eval-printループ)の機能

式をタイプするとLispはそれを直ちに評価して結果を返す。CLISPを終了するときは(quit)とタイプする。
Lispでプログラムを書いているときには、わざわざスクリーンに値を表示させるような関数を書くことは少ない。関数内で表示せずとも返した値は自動的にREPLの機能によって表示される。
他の言語でreturnと書くようなところをLispでは関数の最後の式が自動的に返されるのでその必要がない。

数当てゲーム(バイナリサーチ)

(defparameter *big* 100)
(defparameter *small* 1)

(defparameter *big* 100)
(defparameter *small* 1)

(defun guess-my-number()
  (ash (+ *small* *big*) -1));;(+ *small* *big*)は2つの変数の値を足すコード。加算結果はash関数に渡される。ashは渡された数値を2進数で考え、ビットをシフトする。第二引数に1を渡すと左シフト、-1を渡すと右シフトする。

;;関数の呼び出し
(guess-my-number);;50

(defun smaller()
  (setf *big* (1- (guess-my-number)));;setfでグローバル変数*big*の値を変える。guess-mynumberを呼び*small*と*big*を足して右シフトした値を得て、それを引数として1を引いた関数1-(1-は関数名で、「1を引け」と動詞のように読むと良い)を呼び出す。
  (guess-my-number));;smaller関数によって新しい推測値を表示させるために再びguess-my-numberを呼ぶ。

(defun bigger()
  (setf *small* (1+ (guess-my-number)))
  (guess-my-number))


;;実行
(bigger)
;;75
(smaller)
;;62
(smaller)
;;56

;;グローバル変数をリセットするstart-over
(defun start-over()
  (defparameter *small* 1)
  (defparameter *big* 100)
  (guess-my-number))

(start-over);;50

ローカル変数の定義

ローカル変数の定義にはletコマンドを使う。

(let (変数定義)
     ...本体...)

ローカル関数の定義

ローカル関数の定義にはfletコマンドを使う。最初の2行で関数を宣言する。この関数はfletの本体の中で使える。

(flet ((関数名 (引数)
      ...関数本体...))
  ...本体...
  ||<)

一つの引数nを取るローカル変数fを定義し、本体の中で5を引数として関数fを呼び出す。
>|lisp|
(flet ((f (n)
      (+ n 10)))
     (f 5));;15

コードモードとデータモード

Lispではデフォルトではコードモードになっている。
コードモードにいるとき、その入力はフォームの構造に沿っていなければならない。
フォームとは最初の要素が特別のコマンドになっているようなリストのこと。
フォームを読む際に、リストの残りの要素はすべて引数として関数に渡される。

データモードで書かれてものはすべてデータとして扱われる。
データは「実行」されない。
データモードで固定した情報をデータとしてコード中に埋め込んでおくことができる。
Lispにデータモードであることを教えるにはシングルクオートをリストの手前につける。
これを「クオートする」という。
クオートによって「次に来るのはコマンドじゃないよ、プログラムで使うデータの塊だよ」とLispに教える。

コンスセル

Lispのリストはコンスセルでつなぎ合わされている。
コンスセルはリンクリストである。

cons関数

2つのデータをLispプログラムの中で結び付けたいときに使う。
cons(constructの略)を呼ぶとLispコンパイラはそれぞれのオブジェクトへの参照を入れておく、コンスセルのための小さなメモリをアロケートする。
例えばシンボルchickenをシンボルcatとくっつけると、consはひとつのコンスセルを返す。

(cons 'chicken 'cat);;(CHICKEN . CAT)

コンスセルの表現は繋げられた要素の間にドットをおいて全体をカッコで囲んだもの、リストとは異なる。
Lispではコンスセルの連なりとリストは全く同じもの。

(cons 'pork (cons 'beef (cons 'chicken())));;(PORK BEEF CHICKEN)

空のリスト()はCommon Lispではシンボルnilと同じもので、リストの終端は空のリストである。
3つの要素をコンスすれば、3つの要素を持つリストになる。

car

関数car(Contents of the Address part of the Register)はセルの最初のスロットにあるデータを取り出すのに使う。

(car '(pork beef chicken));;PORK

関数cdr(Contents of the Decrement part of the Register)は2番めのスロットの値を取り出すのに使う。リストの場合はリストの残りの部分を返す(リストの最初の要素を取り除く)。

(cdr '(pork beef chicken));;(BEEF CHICKEN)

list

list関数は長いリストを一気に作ることができる。

(list 'pork 'beef 'chicken);;(PORK BEEF CHICKEN)

空と偽

条件式の評価において空のリストは偽として扱う。

progn

prognコマンドを使って一つの式の中に余分なコマンドを押し込むことができる。最後の式の評価値をフォーム全体の値として返す。progn 特別式は、任意の数の引数を受け付け、先頭の引数から順に全ての式を評価する。

(defvar *number-was-odd* nil)
(if(oddp 5)
         (progn (setf *number-was-odd* t)
                'odd-number)
         'even-number);;==>ODD-NUMBER
*number-was-odd*
==>T

whenとunless

ifの中でひとつ以上の処理を書きたいときにprognを使わなくても、whienやunlessを使えば条件が真の時に囲まれた式をすべて実行する。

(defvar *number-is-odd* nil)
(when (oddp 5)
      (setf *number-is-odd* t)
      'odd-number)
==>ODD-NUMBER

*number-is-odd*
==>T

(unless (oddp 4)
        (setf *number-is-odd* nil)
        'even-number)
==>EVEN-NUMBER

*number-is-odd*
NIL

cond

condは複数の節を引数として受け取る。各節の先頭に条件をチェックする述語があり条件が成立した場合残りの式を評価する。条件が不成立であれば、次の節に移る。条件は常に上から順に検査され、最初に条件を満たしたものがcondの返り値になる。

(defvar *arch-enemy* nil)
(defun pudding-eater (person)
  (cond ((eq person 'henry) (setf *arch-enemy* 'stupid-lisp-alien)
         '(curse you lisp alien - you ate my pudding))
        ((eq person 'johnny)(setf *arc-enemy* 'useless-old-johnny)
         '(i hope you choked on my pudding johnny))
        (t '(why you eat my pudding stranger ?))))
(pudding-eater 'johnny)
==>(I HOPE YOU CHOKED ON MY PUDDING JOHNNY)

*arc-enemy*
==>USELESS-OLD-JOHNNY

(pudding-eater 'george-clooney)
==>(WHY YOU EAT MY PUDDING STRANGER ?)

case

caseでは比較対象になる値を並べて置くだけで良い。

(defun pudding-eater(person)
  (case person
    ((henry) (setf *arch-enemy* 'stupid-lisp-alien)
     '(curse you lisp alien - you ate my pudding))
    ((johnny) (setf *arch-enemy* 'useless-old-johnny)
     '(i hope you choked on my pudding johnny))
    (otherwise '(why you eat my pudding stranger ?))))

andとorを使って条件分岐を行う

andやorは条件判断にも使うことができる。orに与えられた式のうち真になるものが見つかったら直ちに、Lispは残りの式を評価せずにすぐに心を返す。同様に、andに与えられた式のうち偽になるものが見つかったら直ちに残りを評価する手間をかけずに偽値を返す。次のコードはある数が偶数の場合だけグローバル変数を真値にセットする。

(defparameter *is-it-even* nil);;*IS-IT-EVEN*
(or (oddp 4) (setf *is-it-even* t));;T
*is-it-even*;;T

;;奇数を与えると変数の値が変わらないことがわかる。
(defparameter *is-it-even* nil);;*IS-IT-EVEN*
(or (oddp 5) (setf *is-it-even* t));;T
*is-it-even*;;NIL

Land of Lisp

Land of Lisp

perl CGI

以下のHTMLファイルは/cgi-binディレクトリにあるCGIプログラムquestionnaire.cgiを呼び出す

<!DOCTYPE HTML PUBLIC "-//W3//DTD HTML 4.0//EN">
<!-- 簡単なアンケートのHTML questionnaire.html-->
<html>
    <head>
        <title>アンケートの例</title>
    </head>

    <body>
        <h1>アンケートの例</h1>

        <p>次の質問にお応えください</p>

        <form action="cgi-bin/questionnaire.cgi" method="post">
            <dl>
                <dt>お名前</dt>
                <dd><input type="text" name="name" value=""></dd>
                <dt>最もよくお使いになっているOS</dt>
                <dd>
                    <ul>
                        <li><input type="radio" name="os"
                                  value="Windows"
                                  checked>Windows</li>
                            <li><input type="radio" name="os"
                                       value="Linux">Linux</li>
                            <li><input type="radio" name ="os"
                                       value="FreeBSD">FreeBSD</li>
                            <li><input type="radio" name ="os"
                                       value="Solaris">Solaris</li>
                            <li><input type="radio" name="os"
                                       value="MacOS">MacOS</li>
                            <li><input type="radio" name="os"
                                       value="Other">その他</li>
                    </ul>
                </dd>
            </dl>
            <p>
                <input type="submit" name="send" value="送信"><br>
                <input type="reset" value="リセット">
            </p>
        </form>
    </body>
</html>

CGIモジュール

#!/usr/bin/perl
#このcgiプログラム・スクリプトはUNIXでは実行権限を与えておく必要がある
#(chmod a+x questionnaire.cgi)

#アンケート処理CGIプログラム・スクリプト
#ユーザーが使っているOSの合計を調べる
#questionnaire.cgi

#モジュール読み込み
use CGI;
$query = new CGI;

#統計ファイルの存在チェック
if(-f "../data/statistic.log"){
    #存在するならファイルをオープンする。失敗したらエラーページを表示
    open(F, "../data/statistic.log")|| &error("ファイル /data/statistic.logをオープン出来ません($!)。\n");
    while(<F>){
        #改行文字を除く
        chomp();
        #名前とOSを取得
        ($name, $os) = split(/\t/);
        #配列@nametotalに名前を蓄積
        push(@nametotal, $name);
        #ハッシュ$ostotalに合計値を蓄積
        $ostotal{$os}++;
    }
    close(F);
}

#アンケートを解析
#名前からタブ文字を取り除く(タブ文字をファイルのでーたの区切りとして使っているため)
$name = $query->param('name');
$name =~ s/\t//g;
#名前に重複がないか解析
foreach(@nametotal){
    if($_ eq $name){
        #等しい物があった時にはエラーページを表示
        &error("お名前 $name はすでに登録されています\n");
    }
}

#変数OSに入っているOS名をキーとする値をインクリメント
$ostotal{$query->param('os')}++;

#記録に追加。失敗したらエラーページを表示
open(F, ">>../data/statistic.log")|| &error("ファイル /data/statistic.logに書き込めません($!)。\n");
print F $name, "\t", $query->param('os'), "\n";
close(F);

#表示 (UNIXではEUC-JPとする)
print $query->header(-charset=>'EUC-JP'),
    $query->start_html(-title=>"現在のアンケート結果");
print "<h2>使用OS</h2>\n";
print "<ul>\n";
#合計数を表示
print "<li><strong>$os</strong>: $count</li>\n" while(($os, $count) = each(%ostotal));
print "</ul>\n";
print $query->end_html();

sub error{
    #エラーページ表示のサブルーチン
    my($message) = @_;

    print $query->header(-charset=>'EUC-JP'),
        $query->start_html(-title=>"エラー");
    print "<h1>エラーが発生しました</h1>\n";
    print $message;
    print $query->end_html();
    #異常終了
    exit(1);
}

初めてのPerl 第6版

初めてのPerl 第6版

perl 簡単なWebブラウザ

#簡単なWebブラウザ
#browser.pl

#モジュールの使用宣言
use IO::Socket;

#URLを入力
print "URLを指定してください>";
$url = <STDIN>;

#URLをホスト名とファイルパスに分割
chomp($url);
if($url =~ /http:\/\/([^\/]+)(\/.*)/){
    $host = $1;
    $file = $2;
}else{
    die "そのURLには対応していません。\n";
}

#ソケットオブジェクト作成
$client_socket = new IO::Socket::INET(
    PeerAddr => $host,
    PeerPort => 'http',
    Proto => 'tcp',
    TimeOut => '5'
);
unless($client_socket){
    print "Socket Error:$!\n";
}

#入力をサーバーに送信
print "$host $file" . "\n";
print $client_socket "GET $file HTTP/1.0\n\n";

#出力を取得し、表示(複数行が返ってくるので、取り終わるまで繰り返す)
while($receive = <$client_socket>){
    print $receive;
}

#ソケットを閉じる
$client_socket->close();

#実行結果
# $ perl browser.pl 
# URLを指定してください>http://cruel.org/
# cruel.org /
# HTTP/1.1 200 OK
# Date: Fri, 08 Jul 2016 07:36:18 GMT
# Server: Apache/2.2.11 (Unix) PHP/5.2.9 mod_ssl/2.2.11 OpenSSL/0.9.8e
# Last-Modified: Wed, 15 Apr 2009 10:28:21 GMT
# ETag: "707c09-24-4679568177b40"
# Accept-Ranges: bytes
# Content-Length: 36
# Connection: close
# Content-Type: text/html; charset=UTF-8

# hello
# </body>
# </html>

初めてのPerl 第6版

初めてのPerl 第6版

perl ソケット

サーバースクリプト

#クライアントからの文字入力を受けてそれをそのまま返すサーバースクリプト
#echoserver.pl

#モジュールの使用宣言
use IO::Socket;

#ソケットオブジェクト作成
$server_socket = new IO::Socket::INET(
    LocalPort => '10000',
    Proto => 'tcp',
    Listen => 5,
    Reuse => 1
);
unless($server_socket){
    print "SOcket Error:$!\n";
}

#待機
$client_socket = $server_socket->accept();

#クライアントからの入力を表示
    while($string = <$client_socket>){
        print "受理:$string";
    }

#ソケットを閉じる
$client_socket->close();
$server_socket->close();

クライアントスクリプト

#文字入力をサーバーに送るクライアントスクリプト
#echoclient.pl

#モジュールの使用宣言
use IO::Socket;

#ソケットオブジェクト作成
$client_socket = new IO::Socket::INET(
    PeerAddr => 'localhost',
    PeerPort => '10000',
    Proto => 'tcp',
    TimeOut => '5'
);
unless($client_socket){
    print "Socket Error:$!\n";
}

#入力をサーバーに送信
while($string = <STDIN>){
    if($string =~ /^QUIT/){
        #QUITという文字列で始まる入力があると終了
        last;
    }
    print $client_socket "$string";
}

#ソケットを閉じる
$client_socket->close();

コマンドプロンプトウインドウを2つ起動し、まずはサーバースクリプトを実行する。
別のコマンドラインからクライアントスクリプトを実行するとこのスクリプトがサーバーに接続される。クライアント側で文字列を入力しenterキーを押すとサーバー側にその文字列が表示される。

実行結果

サーバー側

$ perl echoserver.pl 
受理:from client
受理:hello

クライアント側

$ perl echoclient.pl 
from client
hello
QUIT

初めてのPerl 第6版

初めてのPerl 第6版

perl fork関数

#forkを使って2つの処理を作る
#fork関数を使って処理を分岐し、親プロセスと子プロセスでそれぞれ異なる文字列表示を実行する
#fork.pl

if($pid = fork()){
    #親プロセス
    for($i = 1; $i <= 3; $i++){
        print "親プロセス\n";
        sleep(3);
    }
    waitpid($pid, 0);
}
elsif(defined $pid){
    #子プロセス
    for($i = 1; $i <= 5; $i++){
        print "子プロセス\n";
        sleep(1);
    }
}else{
    die "fork出来ません:$!\n";
}
#実行結果
# 親プロセス
# 子プロセス
# 子プロセス
# 子プロセス
# 子プロセス
# 親プロセス
# 子プロセス
# 親プロセス

初めてのPerl 第6版

初めてのPerl 第6版

perl 外部プログラムの実行 exec

#excec関数による実行
#exec.pl

#lsを実行
exec("ls");
print "エラーが発生しました:$!\n";

初めてのPerl 第6版

初めてのPerl 第6版

perl 外部プログラムの実行

#system関数を使ってunixのlsコマンドを実行する
#shellに関数による実行
#shell.pl

#lsを実行
system("ls");

初めてのPerl 第6版

初めてのPerl 第6版

perl 現在のディレクトリのファイル一覧を表示する

#現在のディレクトリのファイル一覧を表示する
#ディレクトリの内容を読み込み表示する
#readdir.pl

#現在のディレクトリ(.)をオープンする
opendir(DIR, ".") || die ".をオープンできません:$!\n";

#配列に読み込む
@files = readdir(DIR);

#クローズする
closedir(DIR);

#表示
foreach(@files){
    print "$_\n";
}


#実行結果
# $ ls
# readdir.pl	test1.txt	test2.txt	test3.txt
# $ perl readdir.pl 
# .
# ..
# readdir.pl
# test1.txt
# test2.txt
# test3.txt

初めてのPerl 第6版

初めてのPerl 第6版

perl 指定したディレクトリを削除する

#指定したディレクトリを削除するスクリプト
#rmdir.pl

print "削除するディレクトリ名を指定してください>";
$directory = <STDIN>;
chomp($directory);

if(-d $directory){
    #ディレクトリがすでにある
    #ディレクトリを削除
    rmdir($directory) || die "$directoryを削除出来ません。:$!\n";
}else{
    #ディレクトリがないかディレクトリではない
    print "$directoryは存在しません。またはディレクトリではありません。\n";
}

#実行結果
# $ perl rmdir.pl 
# 削除するディレクトリ名を指定してください>newDirectory
# $ perl rmdir.pl 
# 削除するディレクトリ名を指定してください>newDirectory
# newDirectoryは存在しません。またはディレクトリではありません。

初めてのPerl 第6版

初めてのPerl 第6版

perl ディレクトリの作成

#指定したディレクトリが存在するかどうか調べ、存在しない時は新しく作成する
#指定したディレクトリを作成
#mkdir.pl

print "作成するディレクトリ名を指定してください>";
$directory = <STDIN>;
chomp($directory);

if(!-d $directory){
    #ディレクトリがまだない
    #ディレクトリを作成
    mkdir($directory, 0755) || die "$directoryを作成できません:$!\n";
}else{
    #既に存在
    print "$directoryは既に存在します。\n";
}

#実行結果
# $ perl mkdir.pl
# 作成するディレクトリ名を指定してください>newDirectory
# $ perl mkdir.pl 
# 作成するディレクトリ名を指定してください>newDirectory
# newDirectoryは既に存在します。

初めてのPerl 第6版

初めてのPerl 第6版

perl ファイルの削除時にY/Nで確認する

#ファイル名を引数として受け取り、ファイルの有無を確認した後、
# 「<ファイル名>を削除しますか?(Y/N):」というメッセージを出して、Yが押された時のみファイルを削除するスクリプト

#ファイルの削除時に確認する
#remove_ask.pl

$filename = $ARGV[0];

if(-f $filename){
    while(){
        print "$filenameを削除しますか?(Y/N):";
        $answer = <STDIN>;
        chomp($answer);

        if($answer eq 'Y'){
            unlink($filename) or die "$filenameは削除できませんでした:$!\n";
            last;
        }elsif($answer eq 'N'){
            last;
        }else{
            print "無効な値が入力されました。\n";
        }
    }
}else{
    print "$filenameは存在しません。\n";
}


#実行結果
# $ perl remove_ask.pl  output.txt
# output.txtを削除しますか?(Y/N):y
# 無効な値が入力されました。
# output.txtを削除しますか?(Y/N):n
# 無効な値が入力されました。
# output.txtを削除しますか?(Y/N):Y

初めてのPerl 第6版

初めてのPerl 第6版

perl 既存のファイルに権限を追加する

#指定した引数をファイル名として扱い、そのファイルの所有者がユーザ自信であり、読み込み権限を持っていれば、既存の権限に加えて所有者とグループの書込権限を付けるスクリプト

#既存のファイルに権限を追加する
#append_mod.pl

if(@ARGV > 0){
    if(-o $ARGV[0]){
        #所有者のファイルかどうか調べる

        #ファイル情報を取得
        ($device, $inode, $mode, $nlink, $userid, $groupid, $rdev, $size, $accesstime, $modifytime, $changetime, $blocksize, $blockcount) = stat($ARGV[0]);

        #権限を追加
        chmod($mode | 0220, $ARGV[0]);
    }else{
        print "所有のファイルではありません\n";
    }
}else{
    print "ファイル名を指定してください\n";
}

初めてのPerl 第6版

初めてのPerl 第6版

perl statでファイルの詳細な情報を表示する

#指定したファイルの詳細な情報を表示する
#stat.pl

print "ファイル名を指定してください>";
$file = <STDIN>;
chomp($file);

#statで情報を取得
($device, $inode, $mode, $nlink, $userid, $groupid, $rdev, $size, $accesstime, $modifytime, $changetime, $blocksize, $blockcount) = stat($file);

#各情報を表示
printf("デバイス:%d\n".
       "i-node: %d\n".
       "権限値: %o\n".
       "リンク:%d\n".
       "ユーザID: %d\n".
       "グループID:%d\n".
       "デバイス識別子:%d\n".
       "サイズ:%d\n".
       "アクセス時間:%d\n".
       "更新時間:%d\n".
       "i-node変更時間:%d\n".
       "ブロックサイズ:%d\n".
       "ブロック数:%d\n",
       $device, $inode, $mode, $nlink, $userid, $groupid, $rdev, $size, $accesstime, $modifytime, $changetime, $blocksize, $blockcount
   );

#実行結果
# デバイス:16777222
# i-node: 820392
# 権限値: 100700
# リンク:1
# ユーザID: 501
# グループID:20
# デバイス識別子:0
# サイズ:0
# アクセス時間:1467785883
# 更新時間:1467736439
# i-node変更時間:1467787740
# ブロックサイズ:4096
# ブロック数:0

初めてのPerl 第6版

初めてのPerl 第6版

perl chmod関数で権限を変更する

#chmod関数を使って指定したファイルの読み込み、書込、実行権限をそれぞれ変更する

#ファイル権限の変更
#chmod.pl

print "ファイル名を指定してください>";
$file = <STDIN>;
chomp($file);

#初期状態を表示
print "初期状態:\n";
&fileinfo();

print "権限値を100にします\n";
chmod(0100, $file);
&fileinfo();

print "権限値を200にします。\n";
chmod(0200, $file);
&fileinfo();

print "権限値を400にします。\n";
chmod(0400, $file);
&fileinfo();

print "権限値を600にします。\n";
chmod(0600, $file);
&fileinfo();

print "権限値を700にします。\n";
chmod(0700, $file);
&fileinfo();

sub fileinfo{
    #ファイルの情報を表示
    print "\t$fileは読み込めます\n" if (-r $file);
    print "\t$fileは書き込めます\n" if (-w $file);
    print "\t$fileは実行できます\n" if (-x $file);
}

#実行結果
# ファイル名を指定してください>output.txt
# 初期状態:
# 	output.txtは読み込めます
# 	output.txtは書き込めます
# 権限値を100にします
# 	output.txtは実行できます
# 権限値を200にします。
# 	output.txtは書き込めます
# 権限値を400にします。
# 	output.txtは読み込めます
# 権限値を600にします。
# 	output.txtは読み込めます
# 	output.txtは書き込めます
# 権限値を700にします。
# 	output.txtは読み込めます
# 	output.txtは書き込めます
# 	output.txtは実行できます

初めてのPerl 第6版

初めてのPerl 第6版

perl ファイルの情報を調べる

#引数をファイル名とみなして、各情報を調べる
#ファイルのテスト
#filetest.pl

if(@ARGV > 0){
    #最初の引数をファイル名とみなす
    print "$ARGV[0] は通常のファイルです\n" if(-e $ARGV[0]);
    print "$ARGV[0] はディレクトリです。\n" if(-d $ARGV[0]);
    print "$ARGV[0] はシンボリックリンクです\n" if(-l $ARGV[0]);
    print "$ARGV[0] はあなたが所有者です。\n" if(-o $ARGV[0]);
    print "$ARGV[0] は名前付きパイプです\n" if(-p $ARGV[0]);
    print "$ARGV[0] は読み込めます。\n" if(-r $ARGV[0]);
    print "$ARGV[0] は書き込めます。\n" if(-w $ARGV[0]);
    print "$ARGV[0] は実行できます。\n" if(-x $ARGV[0]);
    print "$ARGV[0] はバイナリファイルです。\n" if(-B $ARGV[0]);
    print "$ARGV[0] はテキストファイルです。\n" if(-T $ARGV[0]);
    print "$ARGV[0] はソケットファイルです。\n" if(-S $ARGV[0]);
}else{
    print "ファイル名を指定してください\n";
}

#実行結果
# $ perl filetest.pl output.txt
# output.txt は通常のファイルです
# output.txt はあなたが所有者です。
# output.txt は読み込めます。
# output.txt は書き込めます。
# output.txt はバイナリファイルです。
# output.txt はテキストファイルです。

初めてのPerl 第6版

初めてのPerl 第6版