V言語でアレする(アプリ作成)(2)
フェルミ推定
世の中にV言語の利用者はどれぐらい居るのだろう、なんて事をふと考えてしまった。そんなのggれ(TIOBE)。それじゃ、脳が退化するぞ。IT様の言いなりって、良く無いじゃん。すでに某所でAIが密かに運用されてて、それの陰謀で、誘導ってか洗脳されてたらどうする?
そういう時は、フェルミ推定ですよ。シカゴにピアノの調律師は何人いる、とか、東京都に電柱は何本ある? ってのを自分の頭で考える、あれである。
で、emacsには、残念ながらv-modeが無い。みんなが(emacsユーザーがだな)使い出せば、誰かが書いてくれるだろうな。
パッケージが有れば入れてみるって言うフリーライターが大半だろう。作ってみるかと言う奇特な人は、10万人に一人ぐらい? いや、emacsを使うような人は、emacs-lispを書くだけと言う、本末転倒の人も居るからなあ。オイラーもそうだった、scheme界のslimeと言われているgeiserにgauche用が無かったので、作ってみたしね。
editorの勢力分布はどうなってる? 少し前のデータだけど プログラミング効率を上げるテキストエディタ こんなのが有った。そして、こんなのも、 Windows10のテキストエディタおすすめ20選
これとて、どの方面を調査したか、ターゲットを何処に置いた記事かによって大幅に結果が変わって来る。バイアスがかかっているって事。
で、えいやってんで、emacs使いはロートルな親父(既にバイアス済み)だろう。親父は尻軽に新しい言語なんてのに手を出さない(出すのは、おねーちゃんへのお尻攻撃ぐらい)。えいやってんで、IT従事者の1000人に一人ぐらいだろう、emacs使ってる人は。
すると、その中でv-modeに手を出すとすると、1KX100Kで、100万人に一人って予測。それが実現されていないんで、更に確率(V語を使う人の事ね)は低い。こんなインチキなフェルミ推定じゃ、話にならんな。
俯瞰してみる
前回からの続きでGoで書いたアプリを強引にV語に変換してる。やっと、C語の作成段階までこぎつけた。けど、エラーがてんこ盛りで78個もあった。
sakae@pen:/tmp/nbldv$ v nbldv.v : nbldv.v:209:5: error: os.rm() returns an option, so it should have either an `or {}` block, or `?` at the end 207 | os.rm("am.dat") 208 | os.rm("pm.dat") 209 | os.rm("topdf.plt") | ~~~~~~~~~~~~~~~
こんな具合ね。エラーを含むソースの一部が例示されるのは有り難いんだけど、時と場合によっては、水増しのように思える事が有る。そなら、特徴的なerror: って語でgrepすれば、、なんだけど、それをやっても相変わらず、上記のようになる。
sakae@pen:/tmp/nbldv$ v . |& grep error: : ./nbldv.v:206:2: error: undefined ident: `exec` ./nbldv.v:207:12: error: os.rm() returns an option, so it should have either an `or {}` block, or `?` at the end ./nbldv.v:208:12: error: os.rm() returns an option, so it should have either an `or {}` block, or `?` at the end ./nbldv.v:209:5: error: os.rm() returns an option, so it should have either an `or {}` block, or `?` at the end
原因は、結果の表示がstdoutとstderrを合わせた物になっているからだ。パイプ記号にちょっと仕掛けをして、stderrだけを通すようにすれば良い。
これでエラーだけを簡単に俯瞰出来るようになる。多数のエラーも、一か所を直すだけで劇的に消える事があるぞ。
まあ、修理と一緒だな。壊滅的に壊れているように見えても(色々な症状が観測されても)、何処か一か所の部品を交換するだけで正常になる事がある。探偵物語ですよ。何が効いているか推定して処置、、の繰り返し。
以外にてこずるのが、単独で出てるエラーだったりする。場合によっては、何日もうんともすんともしない場合が有るぞ。
心配の種
目に付くエラーは、気の向くままに潰しているんだけど、気になる(根源的と思われる)やつが出てたので、検討しておく。
./nbldv.v:192:7: error: unknown method: `[]AAy.am` 190 | sz := 70 // 10 week's 191 | if stym <= 1302 { 192 | bld.am().tl(sz).pp("am.dat") | ~~~~
これGoでの意図は、配列をソースにしてam(起床時の)データを選び出し、tlで最後のsz分を抽出、そしてそれをam.datと言うファイルに書き出すって言う、パイプラインだ。
V語では、信号源として、構造体のデータしか使えない(メソッドの第一引数に指定出来ない)っぽい。
struct User { age int } fn (u User) can_register() bool { return u.age > 16 } user := User{ age: 10 } println(user.can_register()) // "false" user2 := User{ age: 20 } println(user2.can_register()) // "true"
取扱説明書に、こんな例が載ってた。そして
V にはクラスがありません。しかし、型にメソッドを定義できます。メソッドは特殊なレシーバ ー引数のある関数です。レシーバーの引数リストは fn キーワードとメソッド名の間に出てきます。 この例では、can_register メソッドには User 型の u と命名されたレシーバーがあります。 慣習として self や this のようなレシーバー名は使わずに、短めで、一文字長の名前が好まれます。
あくまでもクラスの代替なので、信号源は構造体限定なんだろうね(ちょっと自信無し、裏技が有ったりして。
module flag
気分転換で、先にcmdlineからの引数処理について、見ておく。
module main import os import flag fn main() { mut fp := flag.new_flag_parser(os.args) vire := fp.int('ire', 0, 1811, "Input YYMM's data") stym := fp.int('from', `f`, 1302, 'Make PDF at YYMM[DD]') additional_args := fp.finalize() or { eprintln(err) println(fp.usage()) return } println(additional_args.join_lines()) println('$vire $stym') // dump(vire) // dump(stym) }
ちょいと長くなるけど、additionalな説明を出すようにしておくのが、親切かな。下記は実行例だ。尚、fp.intとかの第二引数には、ショートカットを設定出来る。これの指定がバッククォート文字ってのが、紛らわしいな。
debian:hoge$ ./hoge -h flag.UnkownFlagError: Unknown flag `-h` Usage: [options] [ARGS] Options: --ire <int> Input YYMM's data -f, --from <int> Make PDF at YYMM[DD] debian:hoge$ ./hoge ./hoge 1811 1302 debian:hoge$ ./hoge --ire 2104 ./hoge 2104 1302 debian:hoge$ ./hoge -f 1908 ./hoge 1811 1908
再度の挑戦、型メソッド
上で放り出していた心配事に再度挑戦する。要するにむき出しの配列では、メソッドになり得ないんだな。構造体のみにメソッドを組み込めるとな。これがV語界の節理と言うなら、それに従うしかあるまい。光の速度は絶対に超えられないと言う、あれである。
struct AAy { bld [][4]int // main data type }
今までtype宣言して、配列の型を決めていたけど、その配列を構造体の中に押し込んだ。型は構造体の名前に移行する訳だな。
そして、取り合えず
abld := &AAy { bld: [][4]int } abld.am().tl(sz).pp("am.dat") ?
こんな風にしてあげたら、メソッド・チェーンと言うかパイプの施設で文句を言われなくなった。新たに宣言したabldは、実験の為に用意した仮設のものね。それから、&が付いているのは、ヒープにデータを保存してって指示だ。
これでコンパイルすると、新な問題が浮上してきた。こんなの解決出来るの? ひょっとしてドツボに真っ逆さまに転落? 疑心暗鬼、暗中模索、闇夜のカラス、この方向って果たして正しいの? 前代未聞の事に挑戦してる研究者の心境ですよ。
: ./nbldv.v:121:15: error: a struct must have a `next()` method to be an iterator ./nbldv.v:131:15: error: a struct must have a `next()` method to be an iterator :
これだけ見ると、構造体にnext()と言う、わけわかめの関数を組み込め、それは回る性質を持っている必要が有るぞと言う難題である。
./nbldv.v:121:15: error: a struct must have a `next()` method to be an iterator 119 | fn (ds AAy) am() AAy { 120 | mut rs := AAy{ bld: [][4]int } 121 | for _, el in ds { | ~~
確かに構造体を回すって出来ないよな。こうやって詳細を当たれば見えてくるな。ここは、ds.bldにしよう。これなら、配列を指定してるから、forで回せるね。
で、ここを通過したら、
./nbldv.v:168:7: error: field `bld` of struct `AAy` is immutable
確かに、書き換え不能の指定をしてたな。元に戻って、
struct AAy { mut: bld [][4]int // main data type }
これが、正解だな。ひょっとしたら、mutの前にpubが必要かな? まあ、怒られたら付けるだけ。どう怒られる(か)楽しみであります。
printf は何処
次なる困ったちゃんは、フォーマット出力を使って、ファイルに書き出ししたい。が、そんなの見当たらず。
./nbldv.v:100:3: error: unknown function: fprintf 98 | defer { xf.close() } 99 | for _, el in ds.bld { 100 | fprintf(xf, "%d %d %d\n", el[ymdh], el[hi], el[low]) | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
fprintfが駄目ならsprintfでもいいんだけどな。vlibの関係場所を家探ししたよ。全体にこじんまりしてて、好感が持てるぞ。
見つけたのは、strconv/format.md
import strconv fn main() { a := 'World' s := strconv.v_sprintf('Hello %s!', a) println(s) }
ここに、色々な例が出てた。ソースを眺めるだけじゃ目移りしちゃって、こういうの想像出来ないからね。ソースはpythonとかと違って簡潔に書かれているからね。慣れればスイスイと読めると思うよ。なるべく早くV語の流儀に慣れたいものだ。
fn (ds AAy) pp(vfn string) ? { mut s := "" for _, el in ds.bld { s += strconv.v_sprintf("%d %d %d\n", el[ymdh], el[hi], el[low]) } os.write_file(vfn, s) ? }
で、パイプの終端で、データを受け取って、それをファイルに落とすメソッドが完成した。 ファイルへの読み書きは、普通、open -> read(write) -> close とばかり思っていたけど、上記のように、一発で行う方法が用意されてるのね。
だから、書き出したいデータを、上記のように文字列にまとめてしまい、一発書き込みって方法が取れる。文字列の追加も s << 'hoge' みたいな方法を取れるかと思ったら、違うのね。
end of conversion ?
最後まで残ったエラーは下記。
./nbldv.v:109:21: error: cannot use `[][4]int` as type `AAy` in return argument 107 | return ds 108 | } 109 | return ds.bld[..n].clone() | ~~~~~~~
一部を取り出して返値にしたい目論見。でも、冷静に考えたら、構造体内の配列そのものを返しているよ。
mut rs := AAy { bld: [][4]int{} } for _, el in ds.bld[..n] { rs.bld << el } return rs
これで良いのかな。
コンパイル成功、しかし、、
sakae@pen:/tmp/nbldv$ make r v nbldv.v ./nbldv Warn: Req size is to big, apply possible size. make: *** [Makefile:6: r] Error 1
どうやら、上の処置で、無事にコンパイル出来たみたい。だが、結果が芳しくない。
-rw-r--r-- 1 sakae sakae 1063 Apr 14 16:04 topdf.plt -rw-r--r-- 1 sakae sakae 0 Apr 14 16:04 pm.dat -rw-r--r-- 1 sakae sakae 60 Apr 14 16:04 am.dat -rw-r--r-- 1 sakae sakae 853 Apr 14 16:04 zzz.pdf
生成物を消さないで、残すようにしての実験結果。就寝時のデータが取れていない。念の為起床時のデータを確認。
sakae@pen:/tmp/nbldv$ cat am.dat -656628616 0 0 -656628616 0 0 :
えっ、このデータも、とんでもない事になってるぞ。どうすれば良いかな。
コンパイルが出来れば、素直に動くと高を括っていたんだけど(それはHaskell連中の口車だからね)、どうやら違うみたいだ。ここからが、本当の孤独なdebugのスタートなのか。
今までは、V語と言う教師が居て、ちゃんと小言を述べてくれてたから、大して頭を使う事なく、ここまで来れた訳だな。
csv_read
関数の中に、昔懐かしいprintln文(進化してて、配列の内容も表示出来る)を突っ込んで、トレースしてみる。
for { el := reader.read() or { break } for i, v in el { rows[i] = strconv.atoi(v) or {panic(err)} } sbld.bld << rows println(rows) println(sbld.bld) }
その結果
sakae@pen:/tmp/nbldv$ make r v nbldv.v ./nbldv [21033004, 135, 70, 47] [[-439106792, 0, 0, 0]] [21033021, 121, 65, 65] [[-439106792, 0, 0, 0], [-439106792, 0, 0, 0]] [21033104, 119, 67, 48] [[-439106792, 0, 0, 0], [-439106792, 0, 0, 0], [-439106792, 0, 0, 0]] :
rowsの配列の内容は正しい。だけど、それを << でappendすると、正しく入らない。何で?何で?何で?
ちゃんとコンパイルされてるんだから、動作保障されてると思うんだけど。。。。V語が新しすぎてBug踏んじゃった、かな?
もう少し、足掻いてみるか? 所で、こういうのの問い合わせ窓口って有るのかしらん?