gofer

『脱出老人』(小学館)なんて本を読んだ。字のごとく、老人が日本を脱出する例を ルポしたものだ。

脱出して何処へ行く。人気はタイとかマレーシアらしいが、この本ではフィリピンが 取り上げられている。著者がフィリピン在住歴が長いので、フィールドワークして あちこちの島を歩いている。

ご存知のようにフィリピンは多数の島から成る国。首都マニラのあるルソン島とか レイテ島とかセブ島とか。

何故、老人は日本脱出をするのか? 洋々な理由と言うか事例が挙げられていた。 寂しさからの脱出、借金からの脱出、閉塞感からの脱出、北国からの脱出、介護疲れ からの脱出、等々。

分かり易いのは北国からの脱出だろう。雪深い寒い所で冬を越すのは、年寄りにとって 限界だー。雪下ろししなくちゃならん、そのかたづけも大変。石油も高騰して、毎月 数万円も暖房費がかかる。もうやだやだ。暖かい南の島へ行きたいぞ。

行った人(移住者)によると、寒さから来る高血圧が直ったとか足腰が痛いのが 直ったとか、天国みたいな生活らしいです。オイラーの近所にも、暖かい所へ移住 した人が居ますよ。気持ち、よーく分かりますよ。

介護からの脱出ってのも肯けるな。国内で相応の施設へ入れようとすると眼の玉が 飛び出るぐらい金をふんだくられる。かと言って、安い所だと何時入れるか分からん、 入れたとしても、虐待にあったり殺されたりする。

だったら、物価の安い所に移住してメイドさんを雇って方が良いって事になるらしい。 最良のメイドさんを見つけるため、何人も何人もメイドを首にしてまた雇いって努力が 必要との事。フィリピンは出稼ぎでの外貨獲得がGDPの10%を占めるとか。メイドさんの 派遣国なんですな。ホスピタリぃー溢れる国、らしいですよ。

メイドさんが海を渡って大量に日本に押し寄せているって言ったら、そりゃ、フィリピンパブの しゃばゆきさんに決まっている。フィリピンは敬虔なカトリックの国。中絶が禁止されて いるから多産が当たり前。かくして女の子はメイドさんとして出稼ぎ。昔の東北地方と 同じ構図だな。

日本で待ち構えるは、若いねーちゃんには見向きもされなくなった親父達。画して、 歳の差カップルが生まれる。日本じゃ、歳の差結婚って、加藤茶とか上戸彩とかが 最近だと千葉真一とかが有名(羨ましがられる)だけど、フィリピンでは余り抵抗感が無いらしい。

その道の猛者によると、良い子を見つけるこつは、(1)日本語を話せる子は駄目。 日本で働いていたから悪知恵を仕込まれている可能性がある。(2)見た目が綺麗。 他にすぐ男を作るから。(3)近所に家族が住んでいる。親族が家に押し入ったり、 たかりに来るから。(4)嘘をつく。(5)肥満体質。らしいです。現地に趣いて 検討する時は上記に注意しましょう。

また、一気にフィリピンへ行くのが億劫なら、フィリピンパブのねーちゃんに、祖国に 居る知人を紹介して貰うもの良い方法とか。地方の子がすれていないのでねらい目だ そうです。英語が出来ない? そりゃ、セブ島あたりへ英語の留学をするのが良さそう ですよ。若いネーちゃん先生がつきっきりで指導してくれるコースがあるそうです。

バラ色の天国目指して、フィリッピンへGo。

goferとは

前回はhugsに手を出した。あのままソースの旅を続けても良かったんだけど、迷宮に入りそうな 予感がする。んでもって、昔あの人が言ってた、goferを読めってのを思い出した。 ちゃんと予習、復習と言うか、やさしいのをやっとけって言う師匠の有り難いお言葉ですな。

こんなのを思い出すって事は、オイラーもきっと手を出しているはず。調べたら、 2年前の今頃にちょっと 触ってましたよ。全く進化が無いと言うか、ぐるぐる回ってます。

コンパイラーも付いていて、ghcの先祖だな。NetBSDはデフォで動きそうなので、NetBSD7で やってみる。

早速、エラーの洗礼を受けた。

make -k
cc -c  -O gofer.c
In file included from gofer.c:22:0:
machdep.c:305:26: error: conflicting types for 'TermParams'
 typedef  struct termios  TermParams;
                          ^

名前が被っているとな。今から20年も前のソースです。幾ら歩みの鈍いNetBSDでも、その間には 進化して、TermParmsなんてのが創生されたのでしょう。

取りあえずエラーの発生したtypedefをコメントアウトしてコンパイルすると

In file included from gofer.c:22:0:
machdep.c:308:28: error: 'TermParams' has no member named 'c_lflag'
 #define  noEcho(tp)      tp.c_lflag    &= ~(ICANON | ECHO); \

今度は、こういう所でボロボロと落ちるようになりましたよ。って、TermParamsって名前が 無くなったんだから、当たり前。んな訳で、TermParamsをTermParamと言う微妙な名前に 変更、この行の前にある2箇所も釣られて変更。

まるで、微妙にURL名が異なる詐欺サイトを作る気分を味わえるなと思いながら、コンパイル 成功。ちょろいもんだな。コンパイラーを騙すのはと思っちゃうぞ。

goferの試運転

コンパイラーが付いているって事は、Makefileだなって早合点して、(中途半端に)書いて みた。

# For gofer and gofc

SRC = t.gs

LOC = /home/sakae/src/gofer
GOFER = standard.prelude
RUNTIME=$(LOC)/runtime.o
EDITOR = emacs

run:
        sh -c "(export GOFER=$(LOC)/$(GOFER);\
                export EDITOR=$(EDITOR);\
                $(LOC)/gofer $(SRC))"

app:
        rm -f *.c
        sh -c "(export GOFER=$(LOC)/$(GOFER);\
                $(LOC)/gofc $(SRC))"
        gcc -O0 -gdwarf-2 -g3 *.c $(RUNTIME) -lm

コンパイルするには、gofc.h と prelude.h が必要なので、/usr/local/lib/Goferに 入れておく事。(多分、どこかをいじれば、格納場所を変更出来るのだろうけど)ああ、ここに有った。

nb7$ fgrep '/usr/local/lib' *.[ch]
cmachine.c:#define GOFC_INCLUDE  "\"/usr/local/lib/Gofer/gofc.h\""

インタープリタは後で試してみる事にして、まずはコンパイル

nb7$ make app
rm -f *.c
sh -c "(export GOFER=/home/sakae/src/gofer/standard.prelude; /home/sakae/src/gofer/gofc t.gs)"
Gofer->C Version 1.03 (2.30b)  Copyright (c) Mark P Jones 1992-1995

Reading script file "/home/sakae/src/gofer/standard.prelude":
Reading script file "t.gs":

Writing C output file "t.c":
[Leaving Gofer->C]
gcc -O0 -gdwarf-2 -g3 *.c /home/sakae/src/gofer/runtime.o -lm

ちゃんと出来たかハロワで確認。

nb7$ ldd a.out
a.out:
        -lm.0 => /usr/lib/libm.so.0
        -lgcc_s.1 => /usr/lib/libgcc_s.so.1
        -lc.12 => /usr/lib/libc.so.12
nb7$ ./a.out
"Hellow GOFER
"nb7$

文字列がダブルクォートで囲まれている所が、新鮮だな。

構成確認

Hugs98は5万行肥えで太りぎみ。goferはってんで数えてみたら2万4千行ぐらい。でも、これで コンパイラーも含めてでの事。実際はどんな構成になってるの? Makefileを見れば、原料配分が分かるはず。

OBJECTS         = storage.o input.o static.o type.o compiler.o
IOBJECTS        = gofer.o builtin.o  machine.o output.o $(OBJECTS)
COBJECTS        = gofc.o cbuiltin.o cmachine.o $(OBJECTS)

gofer           : $(IOBJECTS)
gofc            : $(COBJECTS)

gofer.o         : prelude.h storage.h connect.h errors.h \
                  command.h machdep.c commonui.c
gofc.o          : prelude.h storage.h connect.h errors.h \
                  command.h machdep.c commonui.c output.c
runtime.o       : prelude.h gofc.h machdep.c markscan.c twospace.c

おおよその所は、上記の構成だな。で、Makefileを見てたら、オブジェクトが出来上がった 所でstripしてるんで、gdbを使うならそこをコメントアウトしとかないと、いやーんな 事になるぞ。

インタープリタもコンパイラも、コンパイル機構を使ってるな。マシンは違う物か。 runtime系はそれ自身がマシンになってるんだな。 3者とも、machdepを使ってるって事は、これが仕様書になるのかな。 何となく構成が見えてきたような気がする。

コードをつらつら見て行くと、DEBUGコードを吐けるようだ。イネーブルにすると、

-- test for hugs --
incN   :: Int -> Int
incN n = n + 1

z = incN 1234
main = print z

こんなソース(t.gs)から、こういうインタープリタコードを吐いた。

Compilingname=incN
0x17CA  INT     1
0x17CC  LOAD    1
0x17CE  CELL    $-13001
0x17D0  DICT    1
0x17D2  MKAP    1
0x17D4  UPDAP   0
0x17D6  RETURN
------------------
name=z
0x17D7  INT     1234
0x17D9  CELL    incN
0x17DB  MKAP    1
0x17DD  RETURN
------------------
name=main
0x17DE  CELL    z
0x17E0  CELL    $-13002
0x17E2  CELL    print
0x17E4  MKAP    2
0x17E6  RETURN
------------------

gofcでコンパイルすると、こんなコードになった。

Reading script file "t.gs":
Compiling------------------
name=incN
Arity   = 1
codeGen = $302 + 1
INT     1
LOAD    1
CELL    $-13004
DICT    1
MKAP    1
UPDAP   0
RETURN
------------------
 :

コード本体は同一で、外部機械がコードを解釈出来るように、名前とか引数の数とかの 情報が付いているんだな? あれ? Haskellって、関数の引数は常に一つじゃなかったっけ? カリーが効いてるから。ちと復習。

ウォークスルー Haskell

実装して理解する遅延評価の仕組み

関数型プログラミング言語の定義&実装の仕方の例」

1日でHaskellを作れると極楽浄土へ行けるらしい

The GHC Commentary

遅延評価とはなにか、そしてモナドの話

評価戦略について勉強したこと

基本的なこと

おっと、一番大事なのを忘れてた。

[sakae@fedora gofer]$ wc *.prelude
   917   5033  29483 cc.prelude
    31    187   1064 min.prelude
   866   4690  28087 nofloat.prelude
   609   3357  19402 simple.prelude
   863   4670  27955 standard.prelude

そして、ちょい恥ずかしい告白。どのOSを使うかは、makeに先立って、prelude.hに設定 しておくんだけど、先ほどのNetBSDでは、SUNOSもイネーブルな状態でコンパイルしてた。 だから、名前衝突が起きていたんだ。ちゃんとNETBSDだけを指定したら、NetBSDばかりか、 FreeBSDでもOpenBSDでも動いた。BSDの本家はNetBSDだったんですな。それからLINUXは 勿論Fedoraでも動いていますよ。

standard.prelude

goferの仕様を決めている、standard.preludeを探検しておくか。その前に、haskellの 資料を色々挙げておいたけど、速習版も挙げておこう。

Haskell基礎文法最速マスター

昔流行したシリーズ物。十分古いなんて謙遜されてますが、オイラーはそれよりずっとずっと 古い物を相手にしてますんで、気にせんといて下さいな。

Prelude> let set = [(x,y)| x<-[1..5], y<-['a'..'c']]
Prelude> [ e | e@(n,c)<-set, odd n, c=='d']

e@って、アズパターンって呼ばれるやつだったかな。忘却してますよ。 それはそうと、この方も大事な機能として、合成関数を上げておられた。太古の 昔から有ったのかな? standard.preludeの中を探してみる。

(.)            :: (b -> c) -> (a -> b) -> (a -> c)
(f . g) x       = f (g x)

型シグネチャが3段論法に見えるのはオイラーだけ? ソクラテスは人間だ。人間は死ぬ。 ゆえにソクラテスは死ぬってやつね。aをソクラテス、bを人間、cを死ぬに当てはめると、 そうなるな。数学の根底にあるやつが隠されている。

そうそう、オイラーがHaskellを学び始めた時、型シグネチャ中に出て来る、括弧の意味の 説明を見落としていて、意味不の羅列が並んでる、型シグネチャ嫌いになったのさ。

偉そうに型シグネチャって言ってるけど、普通にC語を書く時は強制させられるから、 Haskellの専売特許って事ではないね。で、括弧は関数を表している。括弧の中身は、入力と 出力の表現だ。上記なら、関数を2つ取って、それをひとつの関数にしますって訳だ。

? :t show
show :: Text a => a -> String
? :t length
length :: [a] -> Int
? (length . show) pi
7

haskellには伝統的にpiが用意されてるんで、その長さを、関数合成で求めてみた。 勿論、

? length $ show pi
7

でも求まるけど、前者だと、合成した関数に名前を付けておいて、後で、それを呼び出す って特典が有ったり、高階関数として、別の関数に渡したりと便利な使い方が出来る。

? map (length . show) [1,11,111,1111]
[1, 2, 3, 4]
(75 reductions, 132 cells)

昔はCPU資源が乏しかったから、効率的なコードを書くためのヒントとして、75回の簡約と 132セルを使いましたなんて報告が有ったんだな。今のghciには、そんな爪の先に灯を 灯すような機能有ったっけ?

ああ、よく忘れる、演算子の強度が、一番最初に定義してあった。

infixl 9 !!
infixr 9 .
infixr 8 ^
infixl 7 *
infix  7 /, `div`, `quot`, `rem`, `mod`
infixl 6 +, -
infix  5 \\
infixr 5 ++, :
infix  4 ==, /=, <, <=, >=, >
infix  4 `elem`, `notElem`
infixr 3 &&
infixr 2 ||
infixr 0 $

C語用なら、man operatorで参照出来るんだけど、haskellではそんなmanが無いから、 印刷して壁にでも貼っておけ。

const          :: a -> b -> a
const k x       = k

第二引数が無視されるって事は、説明文でも書いてくれって事かな。

? const 3.14 "about pi"
3.14
otherwise      :: Bool
otherwise       = True

ガードとかに良く出て来るやつ。Lispのcondの最後に置いておく、tだな。という事は、 少しはLispの血を引いてるんだ。carとかcdrとかconsは見る影もない姿になっちゃったけど!!

fst3           :: (a,b,c) -> a
fst3 (x,_,_)    = x

昔は3エレメントのタプルが有ったけど、現代ではそれが退化しました。zip系は、 豪華に繁栄してました。まるで、恐竜だな。

zip7 :: [a] -> [b] -> [c] -> [d] -> [e] -> [f] -> [g] -> [(a,b,c,d,e,f,g)]
zip7  = zipWith7 (\a b c d e f g -> (a,b,c,d,e,f,g))

いやー、考古学って面白いですねぇ。後で、hugsのモジュールを眺めていたら、zip7とかが Listモジュールに収録されてた。そんじゃ、現代の版はどうなってる?

ghc

fedoraにhaskell-platformを入れてみた。本体Ver.は、7.8.4だった。

fedoraのghcは少し古いのでFreeBSDに最新式を入れてみた(直ぐに古くなる運命なのは承知。 rubyのレールと同じさ)。コンパイル系はllvmじゃなくて gcc系なのね。早くclangを希望。

The process will require 1 GiB more space.
142 MiB to be downloaded.

Fetching ghc-7.10.2.txz:      100%   73 MiB 243.1kB/s    05:14
Fetching gcc-4.8.5.txz:       100%   63 MiB 243.3kB/s    04:32
Fetching mpc-1.0.3.txz:       100%   80 KiB  81.8kB/s    00:01
Fetching mpfr-3.1.2_2.txz:    100%  346 KiB 177.1kB/s    00:02
Fetching binutils-2.25.1.txz: 100%    4 MiB 118.0kB/s    00:36
Fetching gcc-ecj-4.5.txz:     100%    1 MiB 234.1kB/s    00:06

記念に、上記の関数合成をghcでもやってみた。

nl :: Show a => a -> Int
nl = length . show

期せずして、ポイントフリースタイルになっている。

*Main> nl $ 85 ^ 130
251
*Main> nl (10 ^ 224)
225

前々回だったかに、将棋の手数の総計が指せる手が85ぐらいあり130局面って事を書いたけど、 何桁になるか計算。10の224乗ってのは、仏教用語で甚大な数を表すらしい。そしてそれに 、あからって名前がついているそうだ。

今日の読み物

ふつうの Haskell プログラミング

Haskell 関連の記事のリスト

Haskellのパッケージ管理について調べてみた

Advent Calendar 2015

技評が集めた Advent Calendar 2015 いろいろ見てくと、年が明けますよ。