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月に購入。コンパイラーなんて高嶺の花で、ひたすら畳上の水練で、こういう本を読んでたんだな。
今は恵まれ過ぎているよ。時代の流れに感謝しよう。