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踏んじゃった、かな?

もう少し、足掻いてみるか? 所で、こういうのの問い合わせ窓口って有るのかしらん?


This year's Index

Home