vlang app
v up
人生で初の体験をします。
ob$ v up Updating V... > git_command: git pull https://github.com/vlang/v master V self compiling ... V built successfully as executable "v". > Done recompiling. > Recompiling vup.v ... Current V version: V 0.4.9 32ad697, timestamp: 2025-03-06 21:05:01 +0200
あっけない!! 何でvupもやるの? vに内包されてると思ってたけど。。いや、それじゃ v自身の問題が有った場合、v up出来なくなる。だから、独立させてるんだな。 それにしても、TZが +2000 って、どのあたり? 変な事に目が行くな。
ob$ v doctor |V full version |V 0.4.9 8dbde18.32ad697 |:-------------------|:------------------- |OS |openbsd, 7.6, GENERIC#1 |Processor |1 cpus, 32bit, little endian |Memory |1.9GB/2.84GB | | |V executable |/var/SRC/v/v |V last modified time|2025-03-06 20:02:49 :
一応、更新されたか、別な角度から確認。ありゃ、昔のバージョンが保持 されてたぞ。
ob$ ./v_old doctor |V full version |V 0.4.9 8dbde18.32ad697 |:-------------------|:------------------- |OS |openbsd, 7.6, GENERIC#1 |Processor |1 cpus, 32bit, little endian |Memory |1.89GB/2.84GB | | |V executable |/var/SRC/v/v_old |V last modified time|2025-02-28 21:32:31
この機構って、リナとかでgrubを使って、もしもの時に古いカーネルを起動するのと 一緒な考えだな。
doctor
Memory欄の2つの数値は、何を表わしているの? ソース嫁よだな。 v/cmd/tools/doctor.v
total_memory := f32(runtime.total_memory()) / (1024.0 * 1024.0 * 1024.0) free_memory := f32(runtime.free_memory()) / (1024.0 * 1024.0 * 1024.0) if total_memory != 0 && free_memory != 0 { a.line('Memory', '${free_memory:.2}GB/${total_memory:.2}GB') } else { a.line('Memory', 'N/A') }
更に深堀します。 free_memory
とかは、どうやって収集してる? モジュールなんで
そこに場所を移します。
ob$ cd vlib/runtime/ ob$ ls README.md free_memory_impl_openbsd.c.v free_memory_impl_darwin.c.v runtime.v free_memory_impl_default.c.v runtime_nix.c.v free_memory_impl_freebsd.c.v runtime_test.v free_memory_impl_linux.c.v runtime_windows.c.v
どんなOSがサポートされてるか丸見えですなあ。勿論OpenBSDのファイルを参照 してみます。
module runtime fn free_memory_impl() usize { $if cross ? { return 1 } $if !cross ? { $if openbsd { page_size := usize(C.sysconf(C._SC_PAGESIZE)) av_phys_pages := usize(C.sysconf(C._SC_AVPHYS_PAGES)) return page_size * av_phys_pages } } return 1 }
ここで利用されてる C._SC_AVPHYS_PAGES
は、多分、C語で定義されてるsysctl関連
だよと推測される。んでもって、sysctl.hあたりを見ると
ob$ grep page sysctl.h #define HW_PAGESIZE 7 /* int: software page size */
関係しそうなのは、こんなのしか引っかからないし、直接的に引くと
ob$ sysctl -a | grep page vm.nkmempages=32768 hw.pagesize=4096
こんなのが出てくる。本当の所は、どうなっているのだろう?
test
testコードが付属してたんで、実行してみた。
ob$ v test . ---- Testing... ---------------------------------------------------------------- OK 6.686 ms /var/SRC/v/vlib/runtime/runtime_test.v -------------------------------------------------------------------------------- Summary for all V _test.v files: 1 passed, 1 total. Elapsed time: 2719 ms, on 1 job. Comptime: 2689 ms. Runtime: 6 ms.
tccが導入されていないので、多大な時間がかかっている。それには目をそむ けておいて、絶対に目を逸らしてはならないのは、コードの中身だ。非常に良質な サンプル・コードが提示されてるからね。
import runtime fn test_physical_memory() { total := runtime.total_memory() free := runtime.free_memory() println('total memory: ${total}') println('free memory: ${free}') assert total > 0 && free > 0 }
もう、関数名をmainに変更して、使って下さいと言わんばかりですよ。
ob$ v run mem.v total memory: 3047378944 free memory: 1754173440
gdb出来る様に、-g付きでコンパイルして走らせる。
(gdb) r Starting program: /tmp/q/q Breakpoint 1, main (___argc=1, ___argv=0xcf7cae04) at /tmp/v_1000/q.01JNPTJ31GSX3KTAJKJ745RCAZ.tmp.c:12226 12226 g_main_argc = ___argc; (gdb) n 12227 g_main_argv = ___argv; (gdb) 12229 GC_set_pages_executable(0); (gdb) 12230 GC_INIT(); (gdb) 12232 _vinit(___argc, (voidptr)___argv); (gdb) 12233 main__main(); (gdb) total memory: 3047378944 free memory: 1779781632 12234 _vcleanup();
全体の流れが判明したので、局所的に追跡。
Breakpoint 2, main__main () at /tmp/q/mem.v:4 4 total := runtime.total_memory() (gdb) s runtime__total_memory () at vlib/runtime/runtime_nix.c.v:12 12 page_size := usize(C.sysconf(C._SC_PAGESIZE)) (gdb) _libc_sysconf (name=28) at /usr/src/lib/libc/gen/sysconf.c:67 67 len = sizeof(value); (gdb) 70 switch (name) { (gdb) 134 if (_pagesize != 0) (gdb) 477 } (gdb)
定数が28番になってcallされた。
runtime__total_memory () at vlib/runtime/runtime_nix.c.v:13 13 phys_pages := usize(C.sysconf(C._SC_PHYS_PAGES)) (gdb) _libc_sysconf (name=500) at /usr/src/lib/libc/gen/sysconf.c:67 67 len = sizeof(value); (gdb) 70 switch (name) { (gdb) 444 mib[0] = CTL_HW; (gdb) 445 mib[1] = HW_PHYSMEM64; (gdb) n 446 len = sizeof(physmem); (gdb) 447 if (sysctl(mib, namelen, &physmem, &len, NULL, 0) == -1) (gdb) 449 return (physmem / getpagesize()); (gdb) 477 }
次は500番か。そして儀式が有って、カーネル用に再組立とな。 ソースをゆっくり鑑賞してみると、こんな風になってた。
/* * sysconf -- * get configurable system variables. * * XXX * POSIX 1003.1 (ISO/IEC 9945-1, 4.8.1.3) states that the variable values * not change during the lifetime of the calling process. : /* 1003.1b */ case _SC_PAGESIZE: if (_pagesize != 0) return (_pagesize); mib[0] = CTL_HW; mib[1] = HW_PAGESIZE; break;
一応関係者の合意の元に、コード化されてるのね。だから皆が アクセスできるヘッダーファイルに定義しときましたとな。
vm$ cd /usr/include/ vm$ grep _SC_PAGESIZE -r . ./unistd.h:#define _SC_PAGESIZE 28 ./unistd.h:#define _SC_PAGE_SIZE _SC_PAGESIZE /* 1170 compatibility */
さて、もう少しvの根幹を探る。
ob$ v -o mem.c mem.v ob$ wc mem.c 6428 23289 219444 mem.c
ちゃんと調査するなら、C語に変換された(面倒この上ない)コードを調べるのさ。 疲れる事請け合いだけどね。
usize runtime__total_memory(void) { usize page_size = ((usize)(sysconf(_SC_PAGESIZE))); usize phys_pages = ((usize)(sysconf(_SC_PHYS_PAGES))); return (usize)(page_size * phys_pages); } usize runtime__free_memory(void) { return runtime__free_memory_impl(); } VV_LOCAL_SYMBOL void main__main(void) { usize total = runtime__total_memory(); usize __v_free = runtime__free_memory(); :
ああ、そうでもないか。
おまけで、作成した mem.c から実行バイナリーを手動作成する方法。gcが 曲者なのよね。(gc.h and libgc.a)
cc mem.c -I/var/my/srcs/v/thirdparty/libgc/include -L/var/my/srcs/v/thirdparty/tcc/lib -lgc
by python
何か軽いvlangのアプリを作成してみたい。そこで思いついたのが、前前回だかにやった、 箱ひげ図を書く時の、外れ値を除去する部分。これだけをアプリにしたい。
import numpy as np import matplotlib.pyplot as plt # データを読み込む(空白またはタブ区切り) data = np.loadtxt("scall.log") # time と offset のデータを取得 time_values = data[:, 0] offset_values = data[:, 1] # IQRを用いた外れ値除外関数 def remove_outliers(data): Q1 = np.percentile(data, 25) Q3 = np.percentile(data, 75) IQR = Q3 - Q1 lower_bound = Q1 - 1.5 * IQR upper_bound = Q3 + 1.5 * IQR return data[(data >= lower_bound) & (data <= upper_bound)] # 外れ値を除外 filtered_time = remove_outliers(time_values) filtered_offset = remove_outliers(offset_values)
vlang app
どんな名前にしよう。matzさん曰く、名前重要、これを決めるのが第1歩です。 データのうちの外れ値を除外なんだから、 バリ取りなんてのを思いつくな。それとも純日本的に出る杭(データ)は打たれるとか。 あるいは、はみでる(データ)ものは村八分とか。村八分が、オドロオドロしてて 面白そう。これを国際的に通用する名前に昇華させよう。
単純に、m8 とでもするか。イギリスの諜報機関である、M6 の上を行くデータの 暗殺者(ジェームス・ボンド)みたいで格好いいぞ。
仕様
軽く考えておくと
- 一つのデータ列のファイル名を入力とする
- 1.5 * IQR 以上(以下)のデータを除去した、ファイルを作成
- 元ファイルは改名する(file.org)する
こうしておけば、diffとかで、何が除去されたか容易に判別できる。個人使用なので エラーチェックは、なるべくサボル。
資料
言語なんで真似する事で上手になる。生れてきた赤ちゃんが、いきなりしゃべる訳は ない。ママやパパや爺婆の真似をして、段々しゃべれるようになっていく。
前回読書したファインマンさんの本にも、そんな事が登場してたぞ。外国語を 習得するなら、生きた辞書を手に入れろ。生きた辞書ってのは男性ならガールフレンドの 事ね。何冊手に入れた? なんて話が堂々と語られていた。
韓国へ長期出張の某。メキメキ韓国語の腕を上げた。同僚は、あいつスゲーなーと 褒めちらす。一方、韓国人の社員は、彼の言動を聞いてニヤニヤ。何デ? だって、某のしゃべる言葉が、女言葉ばっかりだったから。夜の日韓親善もホドホドに。
sakae@lu:v$ ls examples/word_counter README.md cinderella.txt word_counter.v sakae@lu:v$ ls vlib/math/stats/ stats.v stats_test.v
word_counter.v
は、骨格の作成例。 stats_test.v
は、統計属のうちの四分位点
を求める例だ。
doc
本家を見れば、それで済む事だけど、ネットが不通になった場合でも、大丈夫。 手元でドキュメンテーションを読めるよと。v doc してみると、vlib内のdir名が 単に列挙されるだけ。これで当たりをつけるんだな。
Available modules: ================== arrays arrays.parallel benchmark : v.gen.golang v.gen.js v.gen.js.sourcemap v.gen.js.sourcemap.vlq v.gen.native v.gen.wasm v.gen.wasm.serialise : vdoc: No valid V files were found. Use the `-m` flag when generating docs from a directory that has multiple modules.
色々な言語に翻訳できる様に誠意努力中ですね。
sakae@lu:tmp$ v doc math.complex | grep angle fn (c Complex) angle() f64
普通にテキスト処理なんで、こういう使い方もできる。ブラウザー上で やるには、ちと億劫な仕事だ。
sakae@lu:tmp$ v doc -o ZZ.html math.stats Generating html in "ZZ.html/doc.css" Generating html in "ZZ.html/normalize.css" Generating html in "ZZ.html/doc.js" Generating html in "ZZ.html/dark-mode.js" Generating html in "ZZ.html/math.stats.html" Creating search index... Copying favicons... sakae@lu:tmp$ w3m ZZ.html/math.stats.html
でも、これが普通の利用方法かな。
Playground
本家に遊び場なんてのが用意されてる。どんな物かRunボタンを押してみる。
Hello, Playground!
build logに切り替えると、こんなのが出てきた。
OK (0.502 sec real, 0.502 sec wall)
ならば、ちょいと追加を試してみると、
Running code... Can't run code. The server returned an error: code.v:15:1: error: invalid expression: unexpected keyword `import` 13 | // Join us on Discord: https://discord.gg/vlang 14 | // Enjoy! 15 | import readline { read_line } | ~~~~~~ 16 | 17 | input := read_line('What is your name: ')! Exited with error status 1 Please try again.
危険な行為は許しませんと出てきた。これじゃ何もできないじゃん。
repl
そこで、replですよ。引数無しで起動すると、説明のバナーが出てきて、待ちになる。
>>> import readline >>> dare := readline.read_line('Your name: ')! Your name: sakae
玩具の範疇を越える本格的な遊び場。 rubyで言うirb 機能がコンパイラー言語のくせに、ちゃんと用意されてる。昔のLisp環境を彷彿 させるな。
核になりそうな、四分位点を求めるやつを確認しとく。
import math import math.stats fn test_quantile() { // Assumes sorted array // test for int, i64, f32 array assert stats.quantile[int]([1, 2, 3], 1)! == 3 assert stats.quantile[i64]([i64(1), 2, 3], 1)! == 3 o = stats.quantile[f32]([f32(1.0), 3, 5, 7], 0.22)! assert math.alike(o, 2.319999933242798)
注意点がコメントされてる。与える配列はソートしとけ。使い辛いな。いや、これで いいのです。内部でソートしちゃうってやり過ぎだろう。ソートなんて時間がかかり そうなんで、ユーザーに委ねるのが正解。場合によってはソート済みのものも有る かも知れない。そんなデータをまたこの関数内でソートしちゃうと、場合によっては 大変な時間がかかる事がある。データの性質を知ってるのはユーザーだけだからね。 ちゃんとソートしたデータを渡してってのは、真っ当な判断だ。
>>> import math >>> import math.stats >>> stats.quantile[int]([1, 2, 3], 1)! 3 >>> stats.quantile[int]([1, 2, 3, 4, 5, 6, 7, 8, 9], 0.5)! error: cannot use `float literal` as `int` in argument 2 to `math.stats.quantile` 8 | 9 | println(stats.quantile[int]([1, 2, 3], 1)!) 10 | stats.quantile[int]([1, 2, 3, 4, 5, 6, 7, 8, 9], 0.5)! | ~~~
扱かうデータを少し増量して、その中点を求めようとしたら、エラーだよ。何処に トラップが有るか判ったものじゃない。
>>> stats.quantile[f64]([f64(1.0), 2, 3, 4, 5, 6, 7, 8, 9], 0.5)! 5.0 >>> stats.quantile[f64]([f64(1.0), 2, 3, 4, 5, 6, 7, 8, 9], 0.25)! 3.0
例に習って変更してみた。この関数をそのまま使う限り、データは、浮動小数点 で与える必要があるな。
modify ?
ある意味、使い難い関数が、どう定義されてるか、眺めて億。
// quantile calculates quantile points // for more reference // https://en.wikipedia.org/wiki/Quantile pub fn quantile[T](sorted_data []T, f T) !T { if sorted_data.len == 0 { return T(0) } index := f * (sorted_data.len - 1) lhs := int(index) if lhs < 0 || lhs >= sorted_data.len { return error('index out of range') } else if lhs == sorted_data.len - 1 { return sorted_data[lhs] } else { if lhs >= sorted_data.len - 1 { return error('index out of range') } delta := index - T(lhs) return T((1 - delta) * sorted_data[lhs] + delta * sorted_data[(lhs + 1)]) } }
結局、配列の何番目ってのを求めるのが仕事なんで、この関数を一寸改造すれば、 何とかなりそうな気がする。
おまけで、皆大好き平均の算出法を見て億。
// mean calculates the average // of the given input array, sum(data)/data.len // Based on // https://www.mathsisfun.com/data/central-measures.html pub fn mean[T](data []T) T { if data.len == 0 { return T(0) } mut sum := T(0) for v in data { sum += v } return T(sum / data.len) }
型に追従してくれるって、エェーと何って言うんだっけ?
又、長くなってしまった。次回に続く。
README
歴史的プレゼン なんて本を読んだ。
そんなプレゼンなんて、仕事をしてなきゃ関係無いでしょ。そんな事は無いと 著者は言う。例えばプレゼンの原点である自己紹介。これなら今後も有りそうだな。 何かのサークルに加入した時とかの自己紹介。あるいは、某勉強会でrubyはpythonよりも ずっとお得ですってアッピールする場合とか。
python -> ruby を訴えるプレゼンなら、
pythonって言うオドロオドロしい蛇の写真を用意、気持悪いですネェ。一方 rubyと言う 輝かしい宝石の写真を用意。これなら持ってると値上りして将来も安泰とかね。 これをプレゼンの随所に挟み込み、観衆を熱狂させる事。これで成功間違いなし。
沢山の事は言うな。せいぜい3つの事柄について述べよ。大事な事は7回繰り返せ。 プレゼンの技法は、通販の喋りに沢山利用されてるぞ。ジャパネットの未来を 訴える話法は超優秀とな。