Nim (5)
『ご当地電力はじめました!』(岩波ジュニア新書)なんて本を読んだ。小供向けの本を取り上げ ちゃった訳だけど、3月に起きた停電がきっかけだったりします。
現在の日本は、10大電力会社による独占供給体制になっている。戦前は、ご当地それぞれに 地元の富豪やら組合が小規模な電力会社を作って、地産地消のまかないをやってたらしい。
それを、国の主導で独占供給体制に持って行った。電気エネルギーは、国の根幹に関わる 事、安定に末永く供給するのが、目的。
黒部ダムの水力発電はその象徴ですなあ。あれだけの大工事を小企業が出来る訳がない。 火力発電もしかり。極めつけは、田舎に作った原子力発電所。東京湾の回りには、ごまんと 火力発電所があるくせに、何故、原子力発電所だけ、田舎に作るの?
安全と言うなら、消費地の近くに作るのが理にかなっているのに。それはね、地方を豊かに するために、原子力、交付金を堂々と支給するため。裏の理由は、利権だったりします。
で、震災でメッキが剥がれてしまった。何で、大都会で消費する電力を田舎に作らせる。 使う所で、作って、それをそこで消費する。そうだよなあ。それが賢い。 余ったやつは、売ってあげよう。
トラックに積んで発送すればいいの? 発電 送電 配電は独占されてる。売ろうとすると 送電網を自分で作って、配電も自分でやる? 地方の小会社では無理。
送電網を借りよう。配電もお願いしよう。その借用料はどのくらい? 正確には、借用料の 事を託送料金と言うそうだけど、独占企業のいやいや度が如実に反映されてて、アメリカの 5倍もするらしい。10社平均で、3.96円/Kwとか。
いやいや理由は、天気に左右される不安定な電力は、沢山送電網に流せないって事らしいです。 NTTも、ファイバー網を貸したくなくて、いろいろ妨害作戦をやってたなあ。あれと同じ構図が。
オイラーが考えるに、余った電気は水素に変換して貯蔵しておけば良い。夏の最盛期には、 沢山取れるからね。産地から、夕取れ電力とか言って、農家のおばちゃんが軽トラで都会へ 売りに行ったりして。
太陽光発電と言うと、地面に太陽電池を展開する事になるので、土地が死んでしまう。 そこで、太陽光の共有ってのが考えられているそうな。作物の中には余り太陽光が無くても 育つ、菜種みたいなのがあるそうな。電池を細長くして、作物と光を共有する事も実験が 始まっているとか。
でも、一番は、屋根だね。全国でも晴天率が高い、長野県飯田市では、地元の有志が 公共施設の屋根を使って発電するように行政に働きかけた。そういうの、建物の目的外 使用って事で、最初はけんもほろろに断られたたしい。が、新市長は、そういうのに 理解があり、許可をくれたとか。それが発端となって、太陽光発電が増殖してるとか。
補助金出します。予算を使い終わったら、それで終了って言う行政より、そういう支援の 方が、ずっとよい成果が出るとの事。
会津地方は山の中。昔から自給自足を旨としてきた。電気だってそうすべ。醸造所の人やら 農協の人やら、有志が出資して、水力発電所を作ったとか。小田原市では、戦前に使って いた、水力発電所を掘り起こして復活させるグループが活動してるとか。
送電網の分離と言うか開放が予定されるようだけど、情報が通り光網は、一足お先に 開放されたようだ。使いてぇって名乗りを挙げたのは、しのぎを削る電話会社。
先日も、光モデムを交換したら回線代がお安くなりますから、契約しませんかと電話が かかってきたぞ。TV電話じゃなかったので、相手があの装束をまとっていたかは確認できず。
交換して頂いた特典として、スマホやガラケーとセットで更にお安くなりますってさ。 で、ちと確認させて下さいって、取調べがあった。パソコン何台つないでますか? 光TVに 加入してますか?
ユニ・キャストだけじゃなくて、マルチ・キャスト対応のルーターが必要かの下調べだな。 あわよくば、自社と提携してるコンテンツ屋に情報流したいんか。 適当に答えて、お引取り願ったぞ。
lisp-1 or 2 ?
nimは、関数名が同じでも、引数の型や個数が異なったものは、別の扱いをしてくれた。 それは最初混乱するけど、慣れれば、快適。
じゃ、変数名と関数名は同じでも許される? Lisp界隈では、これを区別しないLisp-1派 (Scheme族)と、区別するLisp-2派(CommonLisp族)に分かれて、熾烈な論争を展開してる。
Nimはどっち派? オイラー予想では、Lisp-1派と思うんだけど。。。夜眠れなくなるといけないので、 ここらあたりできっちり確認しておこう。きっとC語に倣っているのでしょう。尤も、C語で 確認した事は無いけど。
var my = "hello var" proc my() : string = return "hello proc" echo my
これを走らせると
l12.nim(2, 5) Error: redefinition of 'my'
Uum... 変数名と関数名に同じものは許されないのか。Lisp-1 だな。
じゃ、関数問題に戻って、こういうのはどう?
proc my() : string = return "noARG" proc my() : int = return 123 proc my(w:string) : string = return "hello " & w proc my(i:int) : string = return $i echo my(123) echo my()
引数無しで、返す型が違うやつ
l12.nim(6, 7) Error: ambiguous call; both l12.my(): string and l12.my(): int match for: ()
どっちにするねん? はっきりしいや!! 定義は出来るけど、使えない奴が出来ちゃったって事ですな。 どっちつかずの6行目を削除すれば、涼しい顔して123が表示されます。 後、こんなのも思い付いた。
var aaa : string = my()
これなら、左側で文字列よってきっちり要求してるんで、それに合致するmyを選んでくれるかな。 でも、駄目だった。どっちにすんねんエラーが発生。お隣のHaskell業界はいかがかな。 これぐらいやっとけば、場合を尽くした事になるかな?
場合と言えば、caseはちゃんと場合を尽くしているかチェックしてる? 勿論こういうのは、うろ覚えのハスクル君とかオッカムさん対抗ですけど。
const x = 1 case x: of 1: echo "one" of 2: echo "two"
幼児用の数字を英語に直すやつ。
case.nim(2, 0) Error: not all cases are covered
ちゃんと見てるね。穴を塞ぐには、最後の所にelse: を入れましょう。でも、塞がないと 実行バイナリーが出来ないかと思うと、
const x = "f" case x: of "f": echo "one" of "s": echo "two"
こういうのは、黄色信号が灯って、注意進行出来たりします。
case.nim(2, 0) Warning: use 'else: discard'; non-ordinal case without 'else' is deprecated [Deprecated] [Linking] /home/sakae/nim/t/case one
事故責任。有り難く、仰せに従った方が良さそうですよ。
Nimの追い駆け
Nimの処女地ウブにも入れてやろうと思った。普通に入れたんじゃつまらないので、入れた後に 追い駆け出来るようにしとく。
git clone -b master git://github.com/Araq/Nim.git cd Nim git clone -b master --depth 1 git://github.com/nim-lang/csources cd csources && sh build.sh cd .. bin/nim c -d:withUpdate koch ./koch boot -d:release
ふむ、最初は本体とライブラリーを取ってくるとな。次に種となるnimのC語を取ってくる。 ここは、普通それぞれのOS用のバイナリーで供給されるんだけど、ソース出すから、好きに しろか。
出来上がった種nimを使って、kochって言うnim開発用の料理人を仕立て上げるとな。この時に、 withUpdateを指定しとくと、gitのアップデートが簡単に出来る。kochってのは、ドイツ語。 意味はcookね。
で、コックさんに、nim語から、nimのバイナリーを作ってもらうとな。後は、定常業務として、 koch updateしてから、koch boot してけば、追い駆けになる。残念ながら、余りupdate されてないけどね。だったら、master ブランチより devel ブランチにしたら? 山有り谷有りで、 楽しめる事でしょう。
git logしたら、日本人とおぼしき方が2人いた。頑張ってください。
Nimの手本
nimの書き方は、Webに若干ルールとして載ってる。インデントは2文字分ってのがデフォの ようである。説明的な名前を横幅80文字の中に収めようとすると、なるべく詰めたいのは 理解できますけど。オイラーは、インデント幅4文字です。
なにか適当な手本って事で、kochを見てく。
proc exe(f: string): string = return addFileExt(f, ExeExt) proc findNim(): string = var nim = "nim".exe result = "bin" / nim if existsFile(result): return
exeなんて言う凄い名前の関数を定義してるんで、"nim".exe なんて言う洒落っ気の ある事が出来るのね。"bin" / nim なんて、もう駄洒落もいい所。無骨なドイツ人が こんなコードを書くんですかね?
proc exec(cmd: string) = echo(cmd) if execShellCmd(cmd) != 0: quit("FAILURE") exec("$# cc $# $#" % [findNim(), args, toolname])
pythonみたいな割り当てと言うか分配が、strutilsの % で出来るのね。
このkochは、最後にmainを定義して、それを呼ぶってスタイルかと思ったら、違った。 parseoptを呼び出して、コマンドラインを分解。それを元に各コマンドにディスパッチする スタイルになってた。
もう一つ流派が有って、Pythonのモジュールの最後で、テストコードのごとく、そのモジュールを 呼び出す方法。勿論、nimにも備わってる。以下は、examplesの中の例。
when isMainModule: var port = 80
このモジュールを単独で呼び出すと、isMainModuleがtrueになって、以下が実行されてくってやつね。 コンパイラーマジックだよ感謝しなって、systemの所に書いてあったぞ。
nim-gmp
今までnimを堪能してきた訳だが、goに有るmath/big相当が、nimには無くて、ちと残念と 思ってた。BiggstIntをBiggerIntと早とちりしたしね。
そんな折、nimbleで新しいやつが投行されてるか調べていたら、 nim-gmpなんてのに出会った。これって、 多倍長演算をnimから出来るようにしたものだよな。光が見えてきましたよ。
とりかかる前に、多倍長演算の業界を、 多倍長計算ノート で、予習したよ。そして、いよいよ、 多倍長整数演算ライブラリ (GNU MP) とか、Windows用にGMPのインストールから解説した GMP の使い方を見て、使えるじゃんとなった訳。
以前goでやったのをnim語に書き換えた。必ず登場するfactね。
import gmp import gmp/utils let one: mpz = 1 # convert int to mpz_t var r = init_mpz() # initialized to zero var n = init_mpz("100",10) # construct from string (base: 10) mpz_set(r, n) while true: mpz_sub(n, n, one) mpz_mul(r, r, n) if mpz_cmp(n, one) == 0: break echo r
合ってるかな?
[sakae@fedora gmp]$ nimhs fact.nim /home/sakae/.nimble/pkgs/nim-gmp-0.2.1/gmp/pure.nim(67, 2) Hint: 'GMP_RAND_ALG_LC' is declared but not used [XDeclaredButNotUsed] 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
合ってた。goのmath/bigを見た時、使い方に違和感が有ったんだけど、これはきっとGMPに 合わせた設計だったのね。goの営業戦略が何となく透けて見えますな。
折角良い物が手に入ったので、ウブにも持って行ってやろう。その前に、nimbleのマネージャが どうやって管理してるか見ておく。
[sakae@fedora pkgs]$ tree nim-gmp-0.2.1 nim-gmp-0.2.1 ├── gmp │ ├── extratypes.nim │ ├── header.nim │ ├── pure.nim │ └── utils.nim ├── gmp.nim ├── gmp.nimble └── nimblemeta.json
ふむ、このまま持って行って、libの下にでも置けば動きそう。それようのwrappers置き場なんてのが 出来ているので、そこに置く事にした。そして、ライブラリィパスに登録しておく。 config/nim.cfgに書いておけばOK。
nimがどう認識するかは、
sakae@uB:~$ nim dump -- list of currently defined symbols -- nimunion nimcomputedgoto : -- end of list -- /home/sakae/Nim/lib/pure/unidecode /home/sakae/Nim/lib/js : /home/sakae/Nim/lib/wrappers/nim-gmp :
こんな風にして確認出来る。定義済みシンボルもここで確認出来るので、ソースを読んでる時に 出て来る、when defined(linux) なんてのと付き合わせるのに便利。
sakae@uB:~/src/nim-dev$ ./fact could not load: libgmp.so
エラーだよ。fedoraの時も同様エラーが出て、強権(rootになって)発動させて、リンク作った。 もし、あんたが弱い人なら、どうする?
sakae@uB:~/src/nim-dev$ locate libgmp.so /usr/lib/i386-linux-gnu/libgmp.so.10 /usr/lib/i386-linux-gnu/libgmp.so.10.1.3 /usr/lib/i386-linux-gnu/openssl-1.0.0/engines/libgmp.so
libgmp.so.10が有るので、gmp/pure.nimの中のconstで定義してるのを変更すればOK。 変更後、再コンパイルして実行。ちゃんと動いた。おめでとう。gmp.hが無くても動くように 作ってある所が偉いぞ。(上から目線ですな)
gmpのソースを見てたら、mpz用の == が定義されてたので、サンプルで書いたやつは、プチ 簡単になるな。
こちらを見ると、RSA暗号の実装にも使えるようで、なかなかよさげ。 GMP Library 関数メモ for RSA そして、 RSA 暗号がようやく分かった気がしたのでまとめてみる 方や、どこかでお会いした、 Masami Hagiya 先生も、暗号大好き。
調子こいて、FreeBSDでも動くかと試してみたら、無事に動いた。それはそれで良い事なのだけど、一つの疑問が。
[sakae@fb10 ~/t]$ ldd ./fact ./fact: libm.so.5 => /lib/libm.so.5 (0x2806e000) libc.so.7 => /lib/libc.so.7 (0x28094000)
得意のどんなライブラリィ使ってるので、gmp.soが出てこない。どういう仕組みでgmp.soを 使ってる? nimが吐き出したC語を調べるか。
[sakae@fb10 ~/t/nimcache]$ grep gmp.so *c gmp.c:STRING_LITERAL(TMP18, "libgmp.so.10", 12); gmp.c:STRING_LITERAL(TMP19, "libgmp.so.10", 12); [sakae@fb10 ~/t/nimcache]$ grep TMP18 gmp.c STRING_LITERAL(TMP18, "libgmp.so.10", 12); if (!((TMP17 = nimLoadLibrary((NimStringDesc*) &TMP18)) [sakae@fb10 ~/t/nimcache]$ grep TMP19 gmp.c STRING_LITERAL(TMP19, "libgmp.so.10", 12); )) nimLoadLibraryError((NimStringDesc*) &TMP19);
ふむ、nimLoadLibraryを使ってるとな。で、それが何処にあったかと言うと、system.cの中。 とすると、Nimのlib/system/の所で探せばいいのか。探したら、dyncalls.nimの中に有った。
proc dlopen(path: cstring, mode: int): TLibHandle {. importc, header: "<dlfcn.h>".} proc nimLoadLibrary(path: string): TLibHandle = result = dlopen(path, RTLD_NOW)
公開されていないって事は、素人さんを後悔させないためだな。これらはposixさんの時って事なんだけど、他には windowsとmac用の吸収コードが存在してた。
念のため、manしとくと
NAME dlopen, fdlopen, dlsym, dlfunc, dlerror, dlclose ? programmatic interface to the dynamic linker LIBRARY Standard C Library (libc, -lc) SYNOPSIS #include <dlfcn.h> void * dlopen(const char *path, int mode); :
これで納得しました。プログラム中からダイナミックリンカーを呼んで、dlsymとかを使い、 目当ての関数の位置を探り出して、使うって事なのね。
nim doc
gmpのソースに限らず、長いやつをemacsで覗くのは、ちと苦痛になる事が有る。 そんな時は、Nim DocGen Toolsを使って、htmlに してしまうのが楽だ。
w3mだと見栄えはぱっとしないけど、普通のブラウザなら、左側にインデックスが出てきてて、 そこを眺めてクリックすればよい。
haskellで言う所の、シグネチャが簡単に参照出来る。そして運が良ければ、使い方とかの 実例が得られる事があるぞ。
但し、このツールで作られるhtmlには、モジュールから公開してるものしか取り上げて 貰えない。自分が野良で作ったコードなんてのは、公開する関数とかって概念が無いので、 幾ら、nim doc hoge.nim とかしても、列挙されないので注意。
公開は、関数名の後ろにスターマークを付けるだけ。これで後悔しないぞ。 ああ、コメントは、シャープサイン2個ね、かっこよく、シャープで枠を作ると、怒られるので注意。
libの直下にある、system.nimとか、よく使いそうなものをまとめてinportしてくれる、preludeあたりを 押さえておけば、当面は困らないな。ちなみに、preludeは、
import os, strutils, times, parseutils, parseopt, hashes, tables, sets
こんな風になってたぞ。
c2nim
nim-gmpを入れた時、header.nimってのが付いてきた。これの相当品がpure.nimになるんだけど、 このheader.nimって、ctonimってツールを使って 作り出したもの?
面白そうなので使ってみたい。
[sakae@fedora ~]$ nimble search c2nim c2nim: url: git://github.com/nim-lang/c2nim (git) tags: app, binary, tool, header, C, nimrod description: c2nim is a tool to translate Ansi C code to Nimrod. license: MIT website: https://github.com/nim-lang/c2nim
ふむ、便利ツールの扱いか。
[sakae@fedora ~]$ nimble install c2nim Downloading c2nim into /tmp/nimble_1223/c2nim using git... Initialized empty Git repository in /tmp/nimble_1223/c2nim/.git/ remote: Counting objects: 38, done. remote: Compressing objects: 100% (34/34), done. remote: Total 38 (delta 0), reused 18 (delta 0), pack-reused 0 Unpacking objects: 100% (38/38), done. From git://github.com/nim-lang/c2nim * branch master -> FETCH_HEAD * [new branch] master -> origin/master HEAD is now at 97ab67e Merge branch 'master' of https://github.com/nimrod-code/c2nim Switched to a new branch 'origin/master' Unsatisfied dependency: nim (>= 0.10.3)
やな予感。haskellのcabalみたいにならないでね。安定版は無いのか。
nimble領域には入らなかったけど、/tmpの下にはソースが鎮座してたので、だめ元でコンパイルしてみる。
[sakae@fedora c2nim]$ nim c c2nim.nim : c2nim.nim(11, 41) Error: cannot open 'compiler/llstream'
importの所でモジュールが無いって怒られました。何やら、コンパイラーがらみの便利モジュールが 色々追加されているようです。
しょうがないので、ウブにNimの開発版を入れた。そしてc2nimをコンパイルしようとしたら、 やはり上と同じエラー。nimのパッケージマネージャ、nimbleを入れないと駄目?
待て待て、まだ手は有るだろうに。libの中から、../compilerにリンクを貼り、nim.cfgに一行追加。
path="$lib/compiler"
最初、リンクを貼らないで、pathに直接/home/sakae/Nim/compilerと設定したけど、有効に ならなかった。libは、あくまでlibを基点にしないと駄目みたい。
試運転って事で、超簡単なヘッダーファイルを作った。
sakae@uB:~/c2nim/doc$ cat hoge.h #define abc 123 #define xyz 789
これをnim語に変換してみる。
sakae@uB:~/c2nim/doc$ c2nim hoge.h Hint: operation successful (4 lines compiled; 0 sec total; 516.528KB; ) [SuccessX] sakae@uB:~/c2nim/doc$ cat hoge.nim const abc* = 123 xyz* = 789
4行コンパイルしましたって、2Pass方式なの? それはそうと、ちゃんと変換されましたね。 オイラーが考えても、constになるな。以前、C語をgo語に変換した時も、こういうdefineは、定数に 脳内変換したよ。
定数名に米印が付いているのは、だれでもアクセス可能だよマークだ。他のものを変換しても 、みんなexportされるんだろうね。
次は、いきなりgmp.hを変換してみるかな。勿論ウブを筆頭にlinux族は、lib有れどinclude無しって言う、 変なポリシーが浸透しているので、入っていない。取ってくるのはやぶさかでないけど、これ みよがしに、FreeBSDから借用してきました。何せ、FreeBSDは正統派ですから、libとincludeは、 必ずセットになっています。
sakae@uB:~/c2nim/doc$ c2nim gmp.h home/sakae/c2nim/doc/gmp.h(207, 68) Warning: comment '# Linear congruential. ' ignored [CommentXIgnored] home/sakae/c2nim/doc/gmp.h(248, 27) Error: did not expect ##
どんな所で引っかかったの?
247 #ifndef __MPN 248 #define __MPN(x) __gmpn_##x 249 #endif
これって、GNUの拡張で、シャープ2つは、文字列の結合なのね。c2nimの守備範囲は、ANSI Cだから、 エラー検出したって事ね。ここをコメントにして再度変換すると
sakae@uB:~/c2nim/doc$ c2nim --debug gmp.h : cparse.nim(1635) expression cparse.nim(1413) startExpression cparse.nim(295) eat cparse.nim(136) parMessage clex.nim(139) lexMessage home/sakae/c2nim/doc/gmp.h(362, 33) Error: ')' expected
debugオプションを付けると、c2nimの考え方が少しは理解出来るかなあ? おいらは理解出来ないので、 見なかった事にして(コメント化)先へ進みます。で、エラー次ぐエラーで断念しました。
で、gauche.hを変換出来るかと思ったら、他のヘッダーファイルを沢山呼び込んでおり、c2nimの制限から 断念しました。まだまだ、簡単にはいかないですな。