glob

暫く前から通ってる運動教室。同じメニューの繰りかえしだったのだけど、少しバリエーションが増えた。

大きな筋肉を大きく動かしましょう。それが、効率的にスリムになる方法です。脳味噌コンピューターも大飯喰らいなので、同時に働かせるとより効果があります。

スクワットの変形で、力士がやってる股割りスクワットをしましょう。膝を傷めないように、曲げる方向は、足の向きと必ず一致させてください。

腰を落とした時、先生が言う言葉をreveseして、大きな声で答えてください。答えられるまで、ずっとその姿勢を保ってね。

はいそれでは、でんわ。。。。各自考える。わんで と言えたら、姿勢を戻してね。逆語が出てこなかったら、タイムアウトする20秒ぐらいまで、ずっと腰を落としたまま。

3文字ぐらいなら、簡単だけど、はい次は、おにぎり、いろはにほ、とか、段々と字数が増えていく。

(define (rwl l)
  (if (null? l)
      '()
      (append (rwl (cdr l)) (list (car l)))))

(define (rw s)
  (list->string (rwl (string->list s))))

オイラーは、思わず、こんなコードを思い出しちゃったぞ。

gosh> (rw "hello")
"olleh"

お前の脳味噌は、再帰に耐えられるのか? stack depth 3ぐらいで、stack溢れが生じるんじゃないか。

脳味噌をフル回転させると、大量の血流が必要。それって、血圧上昇を招くんじゃなかろうか? 脳内でプッツンしないか心配。ボケーと生きてる方が幸せかな?

再帰なんて使わないで、脳内にメモ用紙を用意。そこに言葉を書き出す(イメージ)。後は、それを逆に読み上げるだけ、何も頭を捻る事はないぞ。

pythonのglob

glob.pyを見てとやかく言ってる人をみかけず。世の中、使う人ばかりなのかな?

magic_check = re.compile('[*?[]')

def has_magic(s):
    return magic_check.search(s) is not None

def glob1(dirname, pattern):
      :
    if pattern[0] != '.':
        names = filter(lambda x: x[0] != '.', names)
    return fnmatch.filter(names, pattern)

肝は、このあたりかな? ようわからん!

glob on unix v6

/etc/globにバイナリーがあって、ソースがusr/source/s1/glob.cに置いてあった。 下記はその冒頭部分。DownLoad V6 sources

/* global command --

   glob params

   "*" in params matches r.e ".*"
   "?" in params matches r.e. "."
   "[...]" in params matches character class
   "[...a-z...]" in params matches a through z.

   perform command with argument list
  constructed as follows:
     if param does not contain "*", "[", or "?", use it as is
     if it does, find all files in current directory
     which match the param, sort them, and use them

   prepend the command name with "/bin" or "/usr/bin"
   as required.
*/

現代のccでコンパイルできるかと思ったら、エラーがボロボロと出てきた。まさか8進数で書いてあるのが気にいらなかったんでは無いよね。

ソースをざっと見した所、amatchが肝になる部分だ。再帰呼び出しでマッチングを進めている。なるほどね。

もう少し簡便なものは有ったなと思いを馳せていると、浮んできたよ。

正規表現

globって、単純に言うと、shell用のファイル名(or Dir名)に対するパターンマッチングだよね。 それって、心臓部は正規表現。巷に溢れていますなあ。

grep,awk,sed,python,ruby,perl ... きりがない。どれかの奴を見ればいいんだろうけど、 複雑怪奇なやつばかりだろうね。

昔の本で、ビューテフィルコードってのを思いだした。各界の著名人にコードの断片を提示してもらって、感嘆するって趣向の本。オイラーが持ってるのは、訳者のサイン入りのやつ。 引っ張り出してみたら、ぴったりの章があった。

プログラミング作法の本を書いた時、ロブ・パイクが作ったコードをカーニハンさんが賞賛してた。

原本に当る。

Practice of Programming (Downloads Grep program from Chapter 9)

正規表現の仕様は次の通り

c   文字 c そのもの
^   行頭
$   末尾
.   任意の一文字
*   0回以上の繰り返し

妥当な仕様だと思うぞ。perl軍団のこれでもか、これでもかと新発明するのは、誰が使うんやと思ってしまう。オイラーはすっかり、複雑な奴は書かなくなったからね。

matchstarをlongバージョンに置き換えると、最長マッチになる。

C語のポインターの扱いと再帰を、ほれぼれするぐらい上手に使っている。再帰は決して階乗を書くためだけにあるんじゃないよと、カーニハンさんが言ってる。オイラーには、耳に痛い指摘だな。

じっくり鑑賞するんだぞ。

考古学

上であげた unix v6 のglobを現代に蘇えらそう。懐古趣味です。amatchの使い方を調べる目的なんで、ちょいとmainを追加して、エラーにならない様に微調整したもの。

/* matcher --  ported from unix v6 glob */
#include <stdio.h>
#include <stdlib.h>

int amatch(char* , char*);

int umatch(char *s, char *p) {
        if(*p==0)
            return(1);
        while(*s)
            if (amatch(s++,p))
                return(1);
        return(0);
}

int amatch(char *as, char *ap) {
        char *s, *p;
        int scc, c, cc, ok, lc;

        s = as;
        p = ap;
        if ( (scc = *s++) )
                if ((scc = scc & 0177) == 0)
                        scc = 0200;
        switch (c = *p++) {
        case '[':
                ok = 0;
                lc = 077777;
                while ( (cc = *p++) ) {
                        if (cc==']') {
                                if (ok)
                                        return(amatch(s, p));
                                else
                                        return(0);
                        } else if (cc=='-') {
                                if (lc<=scc && scc<=(c = *p++))
                                        ok++;
                        } else
                                if (scc == (lc=cc))
                                        ok++;
                }
                return(0);
        default:
                if (c!=scc)
                        return(0);
        case '?':
                if (scc)
                        return(amatch(s, p));
                return(0);
        case '*':
                return(umatch(--s, p));
        case '\0':
                return(!scc);
        }
}

int main(int argc, char *argv[]) {

  if ( argc != 3 ){
    printf("Usage: %s pat name\n", argv[0]);
    exit(1);
  }

  if (amatch(argv[2], argv[1])) {
      printf("%s\n", argv[2]);
  }
}

使用例

ob6$ for f in *
> do
> ./a.out 's*' $f
> done
sh.1
sh.h
shf.c
shf.h
syn.c

パターンは、shellの展開から逃れる為、シングルクォートで保護してます。例ではkshのソースツリー内で、頭がsで始まるファイルを列挙。

ob6$ for f in *
> do
> ./a.out '[st]*.h' $f
> done
sh.h
shf.h
table.h
tree.h
tty.h

こちはら、頭がsかtで始まり、尻尾がhで終るファイル。非常に緩い正規表現だけど、ファイルやディレクトリィーを指定するには、十分な仕様だと思うぞ。

本当は、glob内で、dir内を散歩して、要求に合致するのを見つけるんだけど、複雑になるんで、それはやっていない。

lib glob

glob(3)の例題をやってみる。

#include <glob.h>
#include <unistd.h>

int main() {
           glob_t g;

           g.gl_offs = 2;
           glob("*.c", GLOB_DOOFFS, NULL, &g);
           glob("*.h", GLOB_DOOFFS | GLOB_APPEND, NULL, &g);
           g.gl_pathv[0] = "ls";
           g.gl_pathv[1] = "-l";
           execvp("ls", g.gl_pathv);
}

取り敢えず走ってみよう。

Breakpoint 1, main () at z.c:7
7                  g.gl_offs = 2;
(gdb) n
8                  glob("*.c", GLOB_DOOFFS, NULL, &g);
(gdb)
9                  glob("*.h", GLOB_DOOFFS | GLOB_APPEND, NULL, &g);
(gdb) set print pretty
(gdb) p g
$1 = {
  gl_pathc = 28,
  gl_matchc = 28,
  gl_offs = 2,
  gl_flags = 258,
  gl_pathv = 0x53d7c000,
  gl_statv = 0x0,
  gl_errfunc = 0x0,
  gl_closedir = 0x88932ac <_libc_atexit+28>,
  gl_readdir = 0x1608e510 <__fini>,
  gl_opendir = 0x0,
  gl_lstat = 0x0,
  gl_stat = 0x3608ffd0
}

構造体を確認

(gdb) n
10                 g.gl_pathv[0] = "ls";
(gdb) p g
$2 = {
  gl_pathc = 39,
  gl_matchc = 11,
  gl_offs = 2,
  gl_flags = 259,
  gl_pathv = 0x77f5ef00,
  gl_statv = 0x0,
  gl_errfunc = 0x0,
  gl_closedir = 0x88932ac <_libc_atexit+28>,
  gl_readdir = 0x1608e510 <__fini>,
  gl_opendir = 0x0,
  gl_lstat = 0x0,
  gl_stat = 0x3608ffd0
}

ちょっと収録数が増えた。確かにglobは動作してんな。

Breakpoint 2, glob (pattern=0x37f59324 "*.c", flags=2, errfunc=0x0,
    pglob=0xcf7f9eb0) at /usr/src/lib/libc/gen/glob.c:173
173             struct glob_lim limit = { 0, 0, 0 };

昔のunixだと、/etc/globに置いてあったのが、今ではライブラリィーの一員。ソースの場所も分かったので、旅してこい。

globは有名人なので、glob(3)の他にも、別の章に説明がある。

glob(7)

ob64$ man -s7 glob
GLOB(7)                Miscellaneous Information Manual                GLOB(7)

NAME
     glob - shell-style pattern matching
     :
     [!..]   Like [..], except it matches any character not inside the
             brackets.

     \       Matches the character following it verbatim.  This is useful to
             quote the special characters `?', `*', `[', and `\' such that
             they lose their special meaning.  For example, the pattern
             "\\\*\[x]\?" matches the string "\*[x]?".
     :
HISTORY
     In early versions of UNIX, the shell did not do pattern expansion itself.
     A dedicated program, /etc/glob, was used to perform the expansion and
     pass the results to a command.  In Version 7 AT&T UNIX, with the
     introduction of the Bourne shell, this functionality was incorporated
     into the shell itself.

現代のglob正規表現は、昔よりも拡張されてる。これがshellに取り込まれたのは、v7の時代とな。

関連情報で、fnmatch(3)なんてのも紹介されてた。これって、オイラーが考古学で修復したやつじゃん。ああ、怖いregexもあるぞ。芋蔓式に次々に出てくるなあ。

case golang's glob

golang方面は、どうなってる? ぐぐったら、色々と出てきた。

package main

import "fmt"
import "path/filepath"
import "syscall"

func main() {
    files, _ := filepath.Glob("./*.[ch]")
    for _, f := range files {
        printPathAndSize(f)
    }
}

func printPathAndSize(path string) {
    var s syscall.Stat_t
    syscall.Stat(path, &s)

    fmt.Print(path)
    fmt.Print(": ")
    fmt.Print(s.Size)
    fmt.Println(" bytes")
}

filepathのパッケージを調べたら、これでもかこれでもかって具合に色々な関数が用意されてたぞ。さすがだな。

ソフトウェア作法

手元に、こんな本がある。購入したのは1987年11月って書いてあった。家にある一番古い本になるかな。引っ越し時の処分を免れてきたんだから、思い入れがあるんだな。その割には再読もしてなかった。

内容は、Unixのtool類(wc,cp,grep,sed等)を、作ってみせる本。どう考えて、どう設計し、コーディングしテストしたかってのが丁寧に解説してある。

使ってる言語は、ratforって言う特殊なやつ。それをfortran語に変換してから、コンパイルするらしい。

fortranは再帰が使えないので、著者はボヤキつつ、それを回避するコードを提示してた。ちょっとまどろっこしい気もするが、まあ気にしないでおこう。

通読してみるかな。

家で一番古い本は、プログラミング言語C だった。1986年5月に購入。コンパイラーなんて高嶺の花で、ひたすら畳上の水練で、こういう本を読んでたんだな。

今は恵まれ過ぎているよ。時代の流れに感謝しよう。