term
Table of Contents
fdisk
ちょっと息抜きにOpenBSDでのfdiskをやってみる。味噌はvnconfigを利用して 普通のファイルをdiskに見立てる技だなあ。河豚板さんからの情報によります。
ob$ dd if=/dev/zero of=mydisk bs=1M count=4000 4000+0 records in 4000+0 records out 4194304000 bytes transferred in 60.370 secs (69476627 bytes/sec) ob$ doas vnconfig vnd0 /home/sakae/mydisk ob$ doas fdisk vnd0 Disk: vnd0 geometry: 81920/1/100 [8192000 Sectors] Offset: 0 Signature: 0x0 Starting Ending LBA Info: #: id C H S - C H S [ start: size ] ------------------------------------------------------------------------------- 0: 00 0 0 0 - 0 0 0 [ 0: 0 ] Unused 1: 00 0 0 0 - 0 0 0 [ 0: 0 ] Unused 2: 00 0 0 0 - 0 0 0 [ 0: 0 ] Unused 3: 00 0 0 0 - 0 0 0 [ 0: 0 ] Unused ob$ doas fdisk -iy vnd0 Writing MBR at offset 0. ob$ doas fdisk vnd0 Disk: vnd0 geometry: 81920/1/100 [8192000 Sectors] Offset: 0 Signature: 0xAA55 Starting Ending LBA Info: #: id C H S - C H S [ start: size ] ------------------------------------------------------------------------------- 0: 00 0 0 0 - 0 0 0 [ 0: 0 ] Unused 1: 00 0 0 0 - 0 0 0 [ 0: 0 ] Unused 2: 00 0 0 0 - 0 0 0 [ 0: 0 ] Unused *3: A6 1 0 29 - 81919 0 100 [ 128: 8191872 ] OpenBSD ob$ doas fdisk -gy vnd0 ob$ doas fdisk -v vnd0 Primary GPT: Disk: vnd0 Usable LBA: 34 to 8191966 [8192000 Sectors] GUID: 6b16cdd8-db24-4e03-b46a-554dbc1d1e1b #: type [ start: size ] guid name ------------------------------------------------------------------------ 0: OpenBSD [ 64: 8191903 ] b1f99615-5eba-47fe-8e94-cbd3c194dd5c OpenBSD Area Secondary GPT: Disk: vnd0 Usable LBA: 34 to 8191966 [8192000 Sectors] GUID: 6b16cdd8-db24-4e03-b46a-554dbc1d1e1b #: type [ start: size ] guid name ------------------------------------------------------------------------ 0: OpenBSD [ 64: 8191903 ] b1f99615-5eba-47fe-8e94-cbd3c194dd5c OpenBSD Area MBR: Disk: vnd0 geometry: 81920/1/100 [8192000 Sectors] Offset: 0 Signature: 0xAA55 Starting Ending LBA Info: #: id C H S - C H S [ start: size ] ------------------------------------------------------------------------------- 0: EE 0 0 2 - 81919 0 100 [ 1: 8191999 ] EFI GPT 1: 00 0 0 0 - 0 0 0 [ 0: 0 ] Unused 2: 00 0 0 0 - 0 0 0 [ 0: 0 ] Unused 3: 00 0 0 0 - 0 0 0 [ 0: 0 ] Unused
詳細は、/usr/src/sbin/fdisk/part.c あたりを見て楽しめ、との事。 rdroot/boottmp/usbfadmの大作もお忘れなく。fdiskの編集コマンドを作成して それを実行するとは大変な苦労だなあ。
bug gone
前回発見したバグを潰しておく。
fn make_colection(sec string) ![]string { org :=os.ls('/usr/share/man/man${sec}')! mut rv := []string{} for s in org { rv << s.before('.${sec}') } rv.sort() return rv }
元はbefore('.')だったものに、セクション番号を追加しただけ。
ob$ ./mc 5 login login.conf
これで安心。待て、ループ内の不変式は、ループ外へ移動させとくのが常識だろう。 毎回計算するのは無駄。でも賢いコンパイラーなら、そんなの自動でやってくれるよ。
そんな事より、beforeの実装を見ておく方が有益だぞ。
// before returns the contents before `sub` in the string. // If the substring is not found, it returns the full input string. // Example: assert '23:34:45.234'.before('.') == '23:34:45' // Example: assert 'abcd'.before('.') == 'abcd' // TODO: deprecate and remove either .before or .all_before pub fn (s string) before(sub string) string { pos := s.index_(sub) if pos == -1 { return s.clone() } return s[..pos] }
ここでもスライスが大活躍してました。いや、それより、こちらの方が重大な責務を 負っている裏方ですよ。
// index returns the position of the first character of the input string. // It will return `-1` if the input string can't be found. @[direct_array_access] fn (s string) index_(p string) int { if p.len > s.len || p.len == 0 { return -1 } if p.len > 2 { return s.index_kmp(p) } mut i := 0 for i < s.len { mut j := 0 for j < p.len && unsafe { s.str[i + j] == p.str[j] } { j++ } if j == p.len { return i } i++ } return -1 }
今回の場合は、こちらが発動されるのか。
// index_kmp does KMP search. @[direct_array_access; manualfree] fn (s string) index_kmp(p string) int {
term
今度は前回ちょろっと作成したコード部分で出力を充実させてみよう。目標は補完 の候補をemacsの様にドロップダウン・リストの形式で表示する事。候補リストが段々 絞られてリストが縮小してく様を見てみたいんだ。
これには前前回にひょんな事から登場したtermライブラリィーで何とかなりそうな雰囲気が あるぞ。早速、調べてみると、例が出てた。ちょっと改変した。
import term import os fn main() { term.clear() // clears the content in the terminal width, height := 20, 5 println(term.header_left('TITLE', '=')) term.set_cursor_position(x: width / 2, y: 1 + (height / 2)) println(term.bg_blue('hello world')) term.set_cursor_position(x: 0, y: height) for { if var := os.input_opt('press q to quit: ') { if var != 'q' { continue } break } println('') break } println('Goodbye.') }
実行結果。
==== TITLE ===================================================================== hello world press q to quit: q Goodbye. sakae@lu:mc$
大分、期待してたのとは違う。色落ちしちゃってるけど、青くなってるのは、hello world 文字列のバックグラウンドだけ。全体が青になると思っていた。タイトルの = がエリア サイズの20を無視して、本来の端末幅まで広がってる。widthで宣言した20でクリッピング してくれないの? 兎も角、期待した様な画面制御は実施してくれていなかった。ちゃんとやろうとすると、 茨の道だな。 どうせ茨ならグラフィック関係に道が開けている、 A V module for designing terminal UI apps の方が良さそうだな。
>>> import term >>> term.yellow('hello') hello ;; 黄色で文字列が表示されました。 >>> term.yellow('hello').split('') ['', '3', '3', 'm', 'h', 'e', 'l', 'l', 'o', '', '3', '9', 'm'] >>> mut out := '' >>> for x in s { out += '${x.hex()} ' } >>> out 1b 5b 33 33 6d 68 65 6c 6c 6f 1b 5b 33 39 6d
termって基本色付け師なんですね。最後はschemeで関数を実行するっぽくhexに変換 してみた。これを見ると、 エスケープ・シーケンスな文字列変換の寄せ集めだ。
なお、yellow()は、入力の値によってのみ出力が決まる純粋関数だ。それに対して
set_cursor_position()
みたいな関数には、出力が無い。そんな関数に意味が
有るんか? ええ、この関数を呼び出すとカーソルの位置が変更されるって言う、
副作用バリバリの関数なんだ。この種の関数も結構定義されてるぞ。
examples of term
ちょっとつまらないので、豊富なサンプルを炙り出してみた。
sakae@lu:examples$ find . -name '*.v' | xargs grep 'import term' ./veb/websocket/server.v:import term ./flag/animated_help_text.v:import term.ui as tui ./flag/flag_layout_editor.v:import term.ui as tui ./brainvuck.v:import term ./path_tracing.v:import term ./term_display_sixel.v:import term ./poll_coindesk_bitcoin_vs_usd_rate.v:import term ./term.ui/rectangles.v:import term.ui as tui ./term.ui/pong.v:import term ./term.ui/pong.v:import term.ui ./term.ui/term_drawing.v:import term.ui ./term.ui/event_viewer.v:import term.ui as tui ./term.ui/cursor_chaser.v:import term.ui as tui ./term.ui/vyper.v:import term.ui as termui ./term.ui/text_editor.v:import term.ui as tui ./terminal_control.v:import term ./process/wrapping_interactive_python.v:import term ./term_key_pressed.v:import term ./vmatrix.v:import term ./vmatrix.v:import term.termios ./pendulum-simulation/modules/sim/runner.v:import term ./websocket/ping.v:import term ./websocket/client-server/client.v:import term ./websocket/client-server/server.v:import term ./game_of_life/life.v:import term
vmatrix.vって以前python版を試したね。どんな違いが有るか比べてみるのの一興 かと。
term.ui/text_editor.v
は昔懐かしいスクリーン・エディターだ。かの昔に富士通のFM-11と言う
パソコンを使っていた時、自作したんだった。このパソコンは面白い事に、2CPU構成に
なってて、いわゆるsub CPUと呼ばれる奴がIO制御を担当してた。メインCPU側では、IOの
リクエスト・テーブルを用意してコールする。大型コンピュータで言うIOプロセッサーの
発想を継承してた。だから、とっても画面制御なんかは、やり易かった。
例の奴は典型的なイベント・ドリブンな作りになってる。グラフィックなアプリだと皆、 こういう構造にはなるな。皆大好きなvscodeも一緒だからね。
fn event(e &tui.Event, mut a App) { mut buffer := a.ed if e.typ == .key_down { match e.code { .escape { exit(0) } .enter { buffer.put('\n') } : 48...57, 97...122 { // 0-9a-zA-Z if e.modifiers == .ctrl { if e.code == .s { a.save() } } else if !(e.modifiers.has(.ctrl | .alt) || e.code == .null) { buffer.put(e.ascii.ascii_str()) } } else { if e.modifiers == .alt { if e.code == .comma { a.visit_prev_file() return } if e.code == .period { a.visit_next_file() return } } buffer.put(e.utf8) } }
このコードを読むと、Ctl-s でファイルを保存。Alt-, 若しくは、Alt-. で、 編集対象ファイルを切り替え。それ意外の場合は、画面に反映だ。 ここでは省略しちゃったけど、マウス操作のイベントにも対応してる。 これを充実させていけば、emacsになるか。viみたいにモードが無いeditorは、 ウェルカムであります。
イベントをシステムコールと思えば、これはもうカーネルと作りが同じ構造ですよ。 幾ら行数が多くても(654行)作りが単純なんで読むの苦労は無い。
test of term
上で見てきた様に、termモジュールは、端末の状態を変化させる関数が定義されている
(例: set_cursor_position
)。これらは、どう試験されているんだろう? テストに
目覚めた今では、興味津々であります。
ちょいと予想してみる。
- そもそも無理だから、テストはしない。
- コードにフックが仕掛けてあり、テストの時だけエスケープ文字列が返る。
- 頑張って秘密の方法を編み出して、テストしてる。
さあ、どれか。2の予想を確認するには、提供されてる関数の定義を見れば一発だ。
// Sources for ANSI Control Sequences // https://github.com/RajeshPatkarInstitute/Panim // https://www.gnu.org/software/screen/manual/html_node/Control-Sequences.html // https://en.wikipedia.org/wiki/ANSI_escape_code // Support for Windows // https://en.wikipedia.org/wiki/ANSI.SYS // #include <windows.h> // C.SetConsoleMode(C.ENABLE_VIRTUAL_TERMINAL_INPUT) // Setting cursor to the given position // x is the x coordinate // y is the y coordinate pub fn set_cursor_position(c Coord) { print('\x1b[${c.y};${c.x}' + 'H') flush_stdout() }
ご丁寧に出所元も提示されてた。特に変な仕掛けは施してない。 それじゃ、テストコードにご対面。
fn test_get_cursor_position() { original_position := term.get_cursor_position()! cursor_position_1 := term.get_cursor_position()! assert original_position.x == cursor_position_1.x assert original_position.y == cursor_position_1.y term.set_cursor_position( x: 10 y: 11 ) cursor_position_2 := term.get_cursor_position()! term.set_cursor_position( x: 5 y: 6 ) cursor_position_3 := term.get_cursor_position()! term.set_cursor_position(original_position) eprintln('original_position: ${original_position}') eprintln('cursor_position_2: ${cursor_position_2}') eprintln('cursor_position_3: ${cursor_position_3}') // 0,0 is returned on dumb terminals if cursor_position_2.x == 0 && cursor_position_2.y == 0 { return } if cursor_position_3.x == 0 && cursor_position_3.y == 0 { return } assert cursor_position_2.x == 10 assert cursor_position_2.y == 11 assert cursor_position_3.x == 5 assert cursor_position_3.y == 6 }
冒頭で軽くチェックしてからセットしゲットして、それを比較とな。 運良く、対称的な関数が存在してたから、事無きを得たけど、term.clear() なんてのは、テスト出来るのかな?
// clear clears current terminal screen. pub fn clear() bool { if os.is_atty(1) <= 0 || os.getenv('TERM') == 'dumb' { return false } print('\x1b[2J') print('\x1b[H') flush_stdout() return true }
定義は、これ。で、テストは諦めたようだ。画面がクリアーされちゃうと、ユーザー からクレームが来るかも知れないって小心者なんだろうか? そんなのテストだから しょうがないでしょって強い態度に出る風潮は無くなったのか。
いやいや、それは違うで。画面がクリアーされたかどうかを確認する術が無いのさ。 だからテストが出来ないんだ。どうしてもやりたかったら、端末の前に座っている ユーザー(猫でもいいけど)に参加を強いる事になる。センサー代りになってもらって クリアされたならtrueを、何の変化も無かったらfalseを入力してもらう。これぞ、 ユーザー参加型テストさ。
module term // utf8_getchar returns an utf8 rune from standard input pub fn utf8_getchar() ?rune { :
犬も歩けば棒に当たる、かも知れないので、こんなのが将来役にたつかも知れないな。 心に留めておこう。
div by 0
ちょいと前回の割り算のゼロ割りは、未定義が疑問だったので質問。
Q: 割り算について質問します。ゼロで割るのは数学的にもコンピュータ的にも禁止 されてます。素人考えでは、答は無限大でも良さそうに思えます。 何故禁止なんでしょうか?
A: とても良い質問ですね! 「ゼロで割ると無限大でいいんじゃないの?」という疑問は、誰もが一度は抱く素直な発想です。 でも、数学的にもコンピュータ的にも「ゼロ除算(0で割ること)」が禁止されているのには、**深い理由**があります。以下でわかりやすく説明しますね。
—
### ✅ まず、「割り算」とは何か?
割り算(例えば \( a ÷ b \))は、本質的に「掛け算の逆」です。
つまり、「\( a ÷ b = c \)」というのは「\( b × c = a \)」を意味します。
たとえば:
\[ 6 ÷ 3 = 2 \quad \text{(なぜなら } 3 × 2 = 6 \text{)} \]
—
### ❌ ゼロで割るとはどういうこと?
たとえば「\( 6 ÷ 0 = ? \)」と考えたとします。
この意味は「**0 に何を掛けたら 6 になるか?**」という問いになります。
しかし、**どんな数を 0 に掛けても、結果は常に 0** ですよね?
\[ 0 × 1 = 0,\quad 0 × 9999 = 0,\quad 0 × ∞ = \text{未定義} \]
つまり、**「0 × 何か = 6」になるような「何か」は存在しない**んです。 だから、数学的に「答が存在しない」=「定義できない」となるわけです。
—
### 🔁 「無限大」でいいんじゃないの?という疑問
この疑問も自然です。たとえば、こんなことを考えるかもしれません:
\[ \frac{1}{1} = 1,\quad \frac{1}{0.1} = 10,\quad \frac{1}{0.01} = 100,\quad \dots \]
→ 確かに、**分母が0に近づくと答が大きくなる**から、 「ゼロで割れば無限大でいいのでは?」と思えるわけです。
でも!
- \(\lim_{x \to 0^+} \frac{1}{x} = +∞\)
- \(\lim_{x \to 0^-} \frac{1}{x} = -∞\)
というように、**ゼロに近づく方向によって答が正にも負にもなる**ため、 「0で割る」ことを「∞」と決めつけると**矛盾や混乱**が生じてしまいます。
—
### 💻 コンピュータ的にも困る理由
プログラムで「0で割る」とどうなるかというと…
- **クラッシュする**(ゼロ除算エラー)
- 例外(division by zero)として処理が止まる
- 一部の言語ではNaNやInfinityが返るが、予測不能なバグの元
たとえば:
int x = 5 / 0; // 実行時エラー!
これは安全面からも、システム保護のためにも**厳しく禁止**されてます。
—
### 🧠 結論
- ゼロで割ると、「何を掛けても元に戻らない」ため**数学的に定義できない**。
- ゼロに近づけて「無限大」とする考えは、**方向によって値が変わる**ので不安定。
- コンピュータでは**システム的にエラーやクラッシュの原因**になるため禁止。
—
気になったら、**「極限」や「不定形(∞÷∞など)」の概念**も面白いですよ! 興味あればそこも深掘りしますね。
README
SHAPE なんて本を読んでいる。幾何学の考え方を社会に適用したらどうなるって新鮮な 切り口の本。じっくり読まねば。気分転換に、 他人屋のゆうれい なんてのも読み始めた。それともう一冊、ドリトル先生航海記。
家から図書館の蔵書検索をしたら在庫してた。ただ家での検索では、収納棚番号は 不明。図書館で端末を使えば棚の位置が出てくるけど、もうWindowsの日本語 入力方法を失念してしまっている(ええ、年初から完全なSKKユーザーです)。
IT弱者を装って、ちゃっかり司書さんに聞いちゃいましたよ。日本十進分類で整理 されてましたね。図書事業をCCCへアウトソーシング(例: 佐賀県武雄市)すると、独自の分類番号で 管理されるみたいだけど、普通の分類方法で良かった。家からの検索でも 惜しまずに分類番号を公開してよね。配列のインデックスぐらいは公開しても 問題無いっしょ。待て、配列のインデックス検索なら、
933 = 国会図書館.index('ドリトル先生航海記')
どうやら、こんな風に調べておくと良さそう。 一般的には、これで当りをつけるみたい。 日本十進分類(NDC)検索 でも、オイラーは分類に弱いからなあ。例えばホームセンターへコーヒー・フィルターを購入 しに行ったとする。どこら辺に有る? ドリップ用品の付属品と考えれば、台所用品 売り場。豆を買うついでに、フィルターもお忘れなくって思えば、コーヒー売り場に あって、しかるべくだ。右往左往してAI監視カメラにマークされるぞ。
んな訳で、3冊をパラレル読書してる。が、ゆうれい本が面白くて、俄然優先度が 上昇。いわゆる一気読み状態。こういう時は、20-20-20のルールを励行すべし。 目をいたわりましょうってんで米国の眼科医が推奨してる。 20分事に、20フィート(6m)離れた所を、20秒間みつめましょう。 ついでに、肩と首のストレッチで肩を廻す、首も廻すせば完璧。