おもこん

おもこんは「思いつくままにコンピュターの話し」の省略形です

Ruby-GNOME レポジトリへのコントリビューション

今まで他のレポジトリにプルリクエストを出すことは、ほとんどしてきませんでした。 というのは、自分が貢献(コントリビューション)できそうな部分がほとんどなかったからです。 ところが、Ruby-GNOMEプロジェクトについては、多少活動できそうなので、はじめの一歩を踏み出してみました。 つまり、プルリクエストを出しました。

現在Ruby-GNOMEのメインはGTK 4をRubyバインディングすることですが、ほとんど一人の方がコミットしているのみです。 そのため、ドキュメントの方面はあまり手がついていません。 自分は少し前にRuby/GTK4の記事を書いたので、サンプルプログラムの作成ならば協力ができると思いました。

現在は、application1というディレクトリをプルリクエストして、マージしていただきました。 しかし、うかつ者の自分のしたことですから、プロジェクトのオーナーから沢山のサジェスチョンが来ました。 ひとつのプルリクエストがきちんとしたものに仕上がるのに多くの手間がかかり、プロジェクトの方にも迷惑をかけてしまったと思います。

まあ、要領はつかめたので、次回からは前よりはしっかりしたものを作れると思います。

自分はかねがねドキュメントの重要性を主張してきました。 どんなにすばらしいプログラムでもドキュメントがないとユーザの裾野が広がりません。 というのは、プログラムのユーザはハイレベルとは限らないからです。 できるだけ敷居を下げることが普及の条件だと思っています。

これからしばらくは、Ruby-GNOMEのサンプルプログラムの作成をするつもりです。

不定方程式ax+by=1の特殊解の求め方

高校では整数の章で不定方程式ax+by=1を習います。 ただし、文字はすべて整数で、aとbは「互いに素」です。

例えば

 7x+4y=1

の解は


   \begin{cases}
   x &= 3\\
    y &= -5
    \end{cases}

です。 ただし、この他にも(x,y)=(7,-12)、(11, -19)など、無数に解があり、その一般形は


   \begin{cases}
   x &= 3+4t\\
    y &= -5-7t
    \end{cases}

となります。ただし、tは整数です。 この解一般を表したものを「一般解」、そのうちのひとつ(どれでも良い)を「特殊解」といいます。

高校では特殊解から一般解を出すところは説明されているのですが、特殊解の求め方については十分な説明がありません。 また、ネットにおける情報も十分ではないように思います。 そこで、今回は「特殊解の求め方」について書いてみたいと思います。

以下では「特殊解」を単に「解」ということにします。

最も良い方法を手っ取り早く知りたい人は最後の節(合同式を使う方法)だけを読んでください。

総当り方式

総当り方式というのは、整数を代入して試し、解を発見する方法です。 「総当り」というのは、整数全部を試すということではありません。 例えば先程の方程式

 7x+4y=1

の場合、xならば0から3までを試せば解を発見できます。 (0からyの係数4より1だけ小さい数3まで)。 他方、yならば0から6までを試せば解を発見できます。 ですから、xで探すほうが効率が良いです。

  • x=0 ならばy=1/4で不適
  • x=1ならばy=-3/2で不適
  • x=2ならばy=-13/4で不適
  • x=3ならばy=-5で適

高校の試験ではこの方式で短時間で見つけられるような問題が多いです。 しかし、容易に想像できますが、係数が大きくなれば計算は大変になります。 例えば、

 723x+431y=1

の解は


   \begin{cases}
   x &= 31\\
    y &= -52
    \end{cases}

ですが、これを総当り方式でやるのは本当に大変です。 ネット情報では、このようなときに「ユークリッドの互除法を使う」と書かれていることが多いです。 次にこの方法を説明します。

ユークリッドの互除法

ユークリッドの互除法を知っている方はこの節を飛ばしてください。

2つの整数の最大公約数を求める方法のひとつに「ユークリッドの互除法」があります。 例えば、51と36の最大公約数を求めるには、次のようにします。

  • 51を36で割る=>商が1あまりが15
  • そのわり算の「割る数」を「あまり」で割る→36÷15→商が2であまりが6
  • そのわり算の「割る数」を「あまり」で割る→15÷6→商が2であまりが3
  • そのわり算の「割る数」を「あまり」で割る→6÷3→商が2であまりが0

割り切れたときの割る数が最大公約数。したがって、この例では3が最大公約数。

なぜ、これで最大公約数が求まるかは、教科書やネット情報を参照してください。 大事なことは、この方法ならば、

  • さほど多くない計算で最大公約数が求まる
  • かならず最大公約数を求めることができる

ということです。

また、 アルゴリズムがはっきりしているので、プログラムにするのも容易です。 プログラムの本ではリカーシブコールの例として取り上げられていることが多いです。

ユークリッドの互除法を用いた不定方程式の解法

はじめは、簡単な例で説明します。

 7x+4y=1

まず、7と4にユークリッドの互除法を用いて最大公約数を求めます。

  • 7÷4 → 商が1あまりが3
  • 4÷3 → 商が1あまりが1
  • 3÷1 → 商が3あまりが0

これから、最大公約数は1になります。 不定方程式を解くときには下から2番めまでの式を検算の式に変形して使います。 検算の式は、たとえば

 7=4\times 1+3

ですが、ここでは右辺を余りだけにします。

 7+4\times (-1)=3

同様に第2式から

 4+3\times (-1)=1

この式の第2項の3は第1式のあまり、すなわち右辺です。 第1式を第2式に代入して

 4+\{7+4\times (-1)\}\times (-1)=1

これを整理すると


    \begin{align*}
    4+7\times(-1)+4\times(-1)\times(-1)=1\\
    7\times(-1)+4\times 2=1
    \end{align*}

この最後の式から、x=-1, y=2が不定方程式の解です。

この例では係数が小さいので割り算(検算)の式を2つしか使いませんでした。 そのため、代入は1回ですみましたが、係数が大きい場合は数回の代入が必要になることもあります。

  • 割り算の下から2番めの式をベースにする
  • あまりの代入は下から3番めの式から上の式に向かって順番に行う

この方法はアルゴリズムが確定しているので、かならず答えにたどり着くことができるのですが、代入の計算は結構わかりにくく慣れが必要です。 わかりにくい原因は代入後変形が必要なところですが、それを公式化してわかりやすくすることが考えられます。

互除法利用の解法を使いやすくする

さきほどの方法を分析してみます。 互除法の途中の検算の式を

 a+b\times(-q)=r

となっていたとしましょう。 aとbは互除法の途中に出る数字(aは前の式の「割る数」bは「あまり」)で、qとrはこの式における商とあまりです。 下から代入を繰り返して、この式のひとつ下まで来ていたとします。 そのときの方程式が

 bu+rv=1

となっていたとしましょう。 あまりrを代入すると


    \begin{align*}
    bu+\{a+b\times(-q)\}v=1\\
    av+b(u-qv)=1
    \end{align*}

ですので、代入の結果得られる解はx=vとy=u-qvになります。 これを公式とみなして、順にあてはめていけば良いのです。 例をやる前に、今の公式をまとめておきます

  • a÷b → 商がqあまりがr → av+b(u-qv)=1 (下の式から得られる式) → 解は x=v, y=u-qv
  • b÷r → ・・・・・・・ → bu+rv=1 (下から順に得られた式) → 解は x=u, y=v

例として、7x+4y=1を解いてみます。 はじめに左から2カラム目まで(商とあまりの計算まで)行い、一番右のカラムは下から上に戻ってきます。

  • 7÷4 → 商が1あまりが3 → x = -1, y=1-1*(-1)=2
  • 4÷3 → 商が1あまりが1 → x = 1, y=-1
  • 3÷1 → 商が3あまりが0 → 関係ないので無視

これから、x=-1, y=2が解です。

さらに複雑な方程式でやってみましょう

 723x+431y=1
  • 723÷431 → 商が1あまりが292 → x = 31, y=-21-1*31=-52
  • 431÷292 → 商が1あまりが139 → x = -21, y=10-1*(-21)=31
  • 292÷139 → 商が2あまりが14 → x = 10, y=-1-2*10=-21
  • 139÷14 → 商が9あまりが13 → x = -1, y=1-9*(-1)=10
  • 14÷13 → 商が1あまりが1 → x = 1, y=-1

答えはx=31、y=-52です。

 723\times 31+431\times (-52)=1

慣れればできるけれど、やり方を忘れてしまいそうですね。 これに比べて、次の合同式を使う方法は覚えやすいものです。

合同式を使う方法

合同式についてはネット情報などを参考にしてください。 高校では習いませんが、それほど難しくないです。 おそらく授業2コマくらいで基本は十分教えることができます。

合同式ではまず、法を決めます。 3を法にしてみましょう。 このとき、3で割ったあまりが等しい数は合同といい、

 x\equiv y \pmod{3}

と書きます。 法が、書き手と読み手で了解済みであれば省略されます。 たとえば、4と7は両方とも3で割ったあまりが1なので合同です。

 4\equiv 7

合同式はイコールとほぼ同じように和、差、積を行えます。 両辺に同じ数を加えたり、引いたり、掛けたりしても合同は保たれます。 たとえば、さきほどの4と7に10をかけて

 40\equiv 70

です。実際に3で割ったあまりは両方とも1ですから。

不定方程式は合同式で表せます。

 723x+431y=1

これは次のようになります。

 723x\equiv 1 \pmod{431}

なぜなら、431yの部分は431の倍数、すなわち割ったときのあまりが0ですから、723x+431yを431で割ったあまりと723xだけを431で割ったあまりは等しいからです。

また、431xは431で割り切れるので、0に合同ですから

 431x\equiv 0 \pmod{431}

この2つの式から、互助法のように割り算の検算のようなことをしていきます。

2つの式で引き算をする。以下modの部分は略します。

 292x\equiv 1

今の式と、そのひとつ前の式の差を取る。

 139x\equiv -1

一つ前の式から今の式の2倍を引く。

 14x\equiv 3

一つ前の式から今の式の9倍を引く。

 13x\equiv -28

差をとる

 x\equiv 31

これで、x=31が得られました。 元の方程式に代入して、y=-52を得ることができます。

合同式の方法は新たに公式を暗記しなくてすみます。 分かりやすい方法なので、一番お勧めです。

数学の英語----分数と小数

英語で分数のことを「fraction」といいます。 ところで、fractionは元々どういう意味なのでしょうか? 辞書を引くと

fractionは「壊れたもの」が原義で、

  • 一部、小部分、断片、端数
  • a fraction で副詞的に「少し」
  • 分数

などとなっています。 (出典:ジーニアス英和辞典第3版)

これから、例えばりんごが5個と半分あったときの、はんぱ(半分)ことを指すものだったのではないでしょうか。

真分数と仮分数

分子が分母より小さい分数を「真分数」といいます。 例えば1/2や2/5などは真分数です。 これはさきほどの「はんぱ」にぴったりです。 日本語でも真の分数というのは、英語に共通しますね。

英語では真分数を「a proper fraction」といいますが、「proper」は適切な、正式の、という意味で日本語の「真」に相当するものでしょう。

これに対して分子が分母よりも大きい分数を「仮分数」といいます。 例えば5/2は仮分数です。 5/2は2と1/2になるので、「はんぱ」と「本体」が混ざっています。 それで「真」ではなく「仮」の分数となっているのでしょう。

英語では仮分数は「an improper fraction」です。 「improper」は「proper」の反対語で、ふさわしくない、妥当でないという意味です。

帯分数

5/2は2と1/2の和になります。 これを足し算の「+」を略して書くのを帯分数と言います。

 \displaystyle\frac{5}{\;2\;}=2+\frac{1}{\;2\;}=2\,\frac{1}{\;2\;}

一番右側が帯分数です。

仮分数と比べ、帯分数には整数部分があるので、その大きさをイメージしやすいという利点があります。 そのため、小学校では帯分数がよく用いられます。

英語では帯分数を「a mixed fraction」といいます。 「mixed」は混ざった、混成のという意味で、整数と真分数が混ざった分数という意味になります。

仮分数が帯分数よりも有利なのは、計算です。 とくに掛け算、割り算では仮分数の方が帯分数よりも簡単に計算できます。

 \displaystyle \frac{32}{5}\times\frac{15}{4}=\frac{32\times 15}{5\times 4}=24

    \begin{align*}
    6\,\frac{2}{\;5\;}\times3\,\frac{3}{\;4\;}
    &=6\times 3+6\times\frac{3}{\;4\;}+3\times\frac{2}{\;5\;}+\frac{2}{\;5\;}\times\frac{3}{\;4\;}\\
    &=18+\frac{9}{\;2\;}+\frac{6}{\;5\;}+\frac{3}{10}\\
    &=18+4+\frac{1}{\;2\;}+1+\frac{1}{\;5\;}+\frac{3}{10}\\
    &=24
    \end{align*}

帯分数のまま掛ける方法はあまりに面倒なので、先に仮分数に直してから計算するのが正しいやり方です。

いずれにしても計算に有利な仮分数を中学、高校以降は多用するようになります。

繁分数

分数の中に分数があるものを繁分数といいます。 たとえば、

 \displaystyle\frac{\frac{2}{\;3\;}}{\frac{5}{\;7\;}} = \frac{14}{15}

左辺が繁分数、右辺はそれを普通の分数に直したものです。

繁分数は英語で「a complex fraction」または「a compound fraction」といいます。 「complex」は複合の、入り組んだ、という意味、「compound」は複数の部分からなる、合成の、という意味です。

小数

分数は小数に直すことができます。

 \displaystyle 0.4=\frac{2}{\;5\;}

小数も分数もはんぱな部分を表す点では同じで、英語では小数もfractionです。 ただし、decimal(小数の、10進法の)をつけて「a decimal fraction」といいます。

日本語では小数と分数は言葉の上では全く別物ですが、英語では両方ともfractionというのが面白いところです。

なお、分数を小数と区別して表したいときは、「a common fraction」といいます。 「common」は普通のという意味です。

日本語の「分数」は分けた数、分割した結果、という意味から来ていると思います。 その点では、分数は比と関連付けた言葉ということができます。

英語の半端な数、端数という意味のfractionとは言葉の成り立ちが違う感じがしますね。

徒然Ruby(38)RDoc

今回はRubyプログラムから自動的にドキュメントを作成するRDocについて書きたいと思います。 私はこのことについて、エキスパートではありません。 この記事も、初心者の体験談だと考えてください。

どのようなプログラムに有効か?

クラスを定義するプログラムに対して有効です。 トップレベルのメソッドだけからなるプログラムだとあまり意味がありません。

というのは、RDocはプログラムを解析して、クラスとメソッドを抜き出してドキュメントを作るものだからです。 クラスのないプログラム、例えばトップレベルのメソッドだけで作ったプログラムでは、抜き出すものがありません。 そのようなプログラムにRDocを適用しても、できあがったドキュメントはとても悲しいものになってしまいます。

RDocの使い方。

Rubyプログラムのあるディレクトリで「rdoc」とコマンドラインに打ち込めば最低限の動作はします。 docディレクトリにHTMLファイルができあがるので、ダブルクリックして見てみましょう。 クラスとメソッドが表示されますね。

名前だけでは寂しいですから、ちょっと説明を書き加えましょう。 そのためには、そのクラスまたはメソッドの直前にコメントを書きます。 例えば次のようなコメントをクラス定義の直前に書き加えます。

=begin rdoc
方程式 ax+by=1 を表すクラス
インスタンスを生成するときにaとbを引数で与える。
solveメソッドで解を得ることができる。
=end

このように、=begin=endで挟まれた部分はコメントになります。 #を使ったコメントでもOKです。 このようなコメントはできあがったHTMLのクラスのところに表示されるようになります。

メソッドについても同様です。

マークアップ

RDoc独自のマークアップがあります。

  • 段落の間は空行で区切る
  • -または+で始めるとリストを作ることができる
  • []で囲んだ文字を項目とし、項目リストを作ることができる。::で項目と説明を区切る方法も可能
  • 1.で順序付きリスト、a.で文字による順序付きリストになる
  • ===・・・で見出しになる

その他についてはRubyのドキュメントを参考にしてください。

オプション

オプションはたくさんありますが、良く使われるのはタイトルとメイン画面(ホーム画面)でしょう。

  • --titleオプションでタイトルを設定。これがブラウザのタブになる(HTMLのtitleタグ相当)
  • --mainで指定したファイルがホーム画面になる。README.mdなどを指定することが多い

Rakeとの連動

RakefileにRDocでドキュメントを生成するタスクを定義できます。 例えば、

require 'rdoc/task'

RDoc::Task.new do |rdoc|
  rdoc.main = "README.md"
  rdoc.title = "Math Programs"
  rdoc.rdoc_dir = "doc"
  rdoc.rdoc_files.include("README.md", "*.rb")
end

これで、

  • メイン画面がREADME.md
  • タイトルが「Math Programs」
  • HTML生成先のディレクトリが「doc」
  • 対象ファイルがREADME.mdとRubyファイル(拡張子がrbのファイル)

となります。 rakeの引数にrdocなどをつけるとタスクが起動されます。

  • rdoc: RDocでドキュメントを生成
  • clobber_rdoc: ドキュメントをクリア(削除)=>初期状態に戻る
  • rerdoc: ドキュメントを一から生成。HTMLドキュメントのディレクトリ内は再生成したものだけになる

GitHubに「Math-programs」というレポジトリを作りました。 そこのdocディレクトリ以下にRDocで生成したドキュメントがあるので、参考にしてください。 このタイプのドキュメントはRubyを使っている人は良く見ているはずです。 例えばRakeのドキュメントがそうです。

みなさんもライブラリを作ったら、RDocでドキュメントを作ってみましょう。

リンド・パピルスの分数

QuizKnockというユーチューブ・チャンネルをごぞんじですか? クイズ番組をユーチューブにしたチャンネルです。 頻繁に更新されているので、日常的に視聴している人も多いのではないでしょか。 近頃、リンド・パピルスの数学がクイズになっていました。

最後の問題で、分子が1の分数の話がでてきます。 これを単位分数というそうです。

 \displaystyle\frac{5}{\;6\;}=\frac{1}{\;2\;}+\frac{1}{\;3\;}

左辺の分数は右辺では単位分数の和になっています。 古代エジプトでは、このように分数は単位分数の和で表していたそうです。 ただし、その分母はすべて異なるものです。 すべての分数は単位分数の和にできます。 このことは、ウィキペディアの「エジプト式分数」のページに詳しく書かれています。

自分もエジプト式分数を見つける方法を考えてみました。

簡単な例

3/4を単位分数の和にしてみましょう。 これは簡単で

 \displaystyle\frac{3}{\;4\;}=\frac{1}{\;2\;}+\frac{1}{\;4\;}

さきほどの5/6でもそうですが、右辺の最初の分数の分母は元の分数の分母より小さくなっています。 これが手がかりになります。

分数が単位分数の和に分解できること

もしも任意の分数が、

 \displaystyle\frac{a}{\;b\;}=(分母がbより小さい分数)+(単位分数)

となればこの分数は単位分数に分解できます。

  • もしも第1項が単位分数ならば、すでに単位分数の和になっている
  • そうでなければ、第1項は「(より分母が小さい分数)+(単位分数)」にすることができる。

これを繰り返すとどんどん第1項の分母が小さくなりますが、最も小さくなったとすると分母は2になります。 分母が2の分数は1/2しかありませんから単位分数です。

いずれの場合も右辺は単位分数の和になることがわかります。

不定方程式

 \displaystyle\frac{a}{\;b\;}=\frac{c}{\;d\;}+\frac{1}{bd}

となったとしましょう。 ここで、文字はすべて2以上の整数で、d<bとします。 すなわち、分数a/bは「より小さい分母の分数と単位分数の和」になったとします。 分母を払うと

 ad-bc=1

となります。 これは不定方程式です。aとbが互いに素ならば解c、dが存在し、dをb以下にとることができます。 このようにして、この問題は不定方程式に帰着させることができます。

具体例をあげましょう。 3/7を単位分数の和にしてみます。

 3d-7c=1
 3d\equiv 1\equiv 15 \quad\pmod{7}
 d=5,\;c=2
 \displaystyle\frac{3}{\;7\;}=\frac{2}{\;5\;}+\frac{1}{35}

ここまでで、より小さな分母の分数と単位分数の和になりました。 第1項に同じことをします。

 2d-5c=1
 2d\equiv 1\equiv 6\quad\pmod{5}
 d=3,\;c=1
 \displaystyle\frac{2}{\;5\;}=\frac{1}{\;3\;}+\frac{1}{15}

これより

 \displaystyle\frac{3}{\;7\;}=\frac{1}{\;3\;}+\frac{1}{15}+\frac{1}{35}

となります。

分解のしかたは一意でない

この方法で分解すると、答えはひとつしかでてきませんが、分解は一意ではありません。 たとえば、

 \displaystyle\frac{2}{21}=\frac{1}{12}+\frac{1}{84}=\frac{1}{14}+\frac{1}{42}

このように、2種類以上の単位分数の和が存在します。

上記のアルゴリズムが良いとはいえない。

上記の不定方程式を使ったアルゴリズムで求めた単位分数の和が良いとはいえません。 例えば、5/6は

 \displaystyle\frac{5}{\;6\;}=\frac{1}{\;2\;}+\frac{1}{\;3\;}

という簡単な分解がありますが、不定方程式のアルゴリズムを使うと

 \displaystyle\frac{5}{\;6\;}=\frac{1}{\;2\;}+\frac{1}{\;6\;}+\frac{1}{12}+\frac{1}{20}+\frac{1}{30}

となります。 このように和が長くなるのは良いとはいえないでしょう。 ちなみに、右辺は高校の数列で習う部分分数分解のパターンになっています。

 \begin{align*}
  & \frac{1}{\;2\;}+\frac{1}{\;6\;}+\frac{1}{12}+\frac{1}{20}+\frac{1}{30}\\
  &= \frac{2-1}{\;1\times 2\;}+\frac{3-2}{\;2\times 3\;}+\frac{4-3}{3\times 4}+\frac{5-4}{4\times 5}+\frac{6-5}{5\times 6}\\
  &= \left(\frac{1}{\;1\;}-\frac{1}{\;2\;}\right)+\left(\frac{1}{\;2\;}-\frac{1}{\;3\;}\right)+\left(\frac{1}{\;3\;}-\frac{1}{\;4\;}\right)+\left(\frac{1}{\;4\;}-\frac{1}{\;5\;}\right)+\left(\frac{1}{\;5\;}-\frac{1}{\;6\;}\right)\\
  &= 1-\frac{1}{\;6\;}\\
  &= \frac{5}{\;6\;}
  \end{align*}

ウィキペディアの記事には、単位分数に分解する別の方法も紹介されているので参考にしてください。

プログラム

アルゴリズムが確定しているので、これをプログラム化できます。 Rubyで作ったものをGitHubにあげました。

GTK -- 少し待ってから次の作業をする方法

少し待たなければいけないケース

GTK 4 は、マルチスレッドであるので、他のオブジェクトの作業完了まで待たなければいけないことがあります。

そういうときは、作業完了を知らせるシグナルを捕まえれば良いのです。 たいてい、これで解決できます。 具体的にはシグナルに、「他のオブジェクトの作業完了後にこちらで行いたいプログラム」をハンドラーとして結合します。

ところが、そのようなシグナルがない場合もあります。 自分が困ったのは、GtkColumnViewで、リストを表示するプログラム(Tcsv)でした。 私の想像では、次のようなことが行われると考えています(確証なし)。

  1. リストにアイテムを追加する
  2. リスト(GListModel)が、"items-changed"シグナルを発行する
  3. そのシグナルを受けてGtkColumnView(または関連オブジェクト)が、「GtkScrollWindowの縦方向のスクロールに関連するGtkAdjustment」のupperプロパティを1行分増やす。
  4. GtkAdjustmentは、その変更完了時に"changed"シグナルを発行する。
  5. そのシグナルにもとづき、GtkScrolledWindowの縦方向のスクロールバー(GtkScrollBar)を調整する

行の追加と同時に、新たな行(最終行)を表示するために末尾までスクロール(GtkAdjustmentのvalueプロパティをupperから1ページ分(page-sizeプロパティ)前にセットする)したかったので、次のようなことをしました。

行追加直後にGtkAdjustmentのvalueプロパティを更新する

この方法は、GtkAdjustmentのupperプロパティが更新されていないのでうまくいきません。 つまり、行の追加がGtkAdjustmentやGtkScrollWindowに反映されていないのです。 そこで、次のように方法を変えました。

GtkAdjustmentの"changed"シグナルを捕らえてvalueプロパティを更新する

この方法は、GtkAdjustmentの変更とGtkScrollBarの変更はできるのですが、画面がスクロールされませんでした。 私の想像では、GtkAdjustmentのvalueプロパティ変更がGtkScrolledWindowに伝わっていないと思われます。

GtkAdjustmentは2回シグナルを発行します。

  • upperが変更されたときに"changed"シグナルを発行する
  • valueが変更されたときに"value-changed"シグナルを発行する

GtkAdjustmentのドキュメントによると、これらのシグナルが続けて発行されるとき、それらはリカーシブ(再発行)ではなくリスタート(再スタート)されるとのことです。 つまり、シグナルは2回発行されずに、1回めの途中でそれが(キャンセルされ)再度スタートから発行手続きが始まる、ということです。 このせいかどうか分かりませんが(そうだとするとおかしな点もある)GtkScrolledWindowはupperの変更に対応しているだけで、valueの変更に対してスクロールしていません。

このあたりで行き詰まりました。

少し待ってから作業する

GtkScrolledWindowがリストの1行追加野処理を完了するまで、GtkAdjustmentのvalueを更新するのを待つ必要がありそうです。 しかし、それに適するシグナルがGtkScrolledWindowにはありません。 そうなると、「一定時間待ち、その間にGtkScrollWindowに作業してもらう」ことになります。 それを探していてg_timeout_add_onceを見つけました。

guint
g_timeout_add_once (
  guint interval,
  GSourceOnceFunc function,
  gpointer data
)
  • interval: 待ち時間。単位はミリ秒。この時間だけ待った後にfunctionが呼び出される
  • function: 待ち時間後に呼び出される関数。関数の形式は後述。
  • data: 関数に渡す引数

関数の形式は次のとおりです。

void
(* GSourceOnceFunc) (
  gpointer user_data
)

関数の引数はさきほどのdata(第3引数)です。

これを使って、リストに1行追加し、0.1秒待ってvalueプロパティを更新したら、期待した結果を得ることができました。 0.1秒というのは、経験値であり、理論的にそれが妥当かどうかはわかりません。 経験的には0.1秒あれば1行追加に対してGtkColumnViewもGtkScrolledWindowも対応が完了していました。

この方法が最適解とはいえないと思います。 しかし、現実には有効な方法でしたので、ここに記録しておくことにしました。

この関数を使ったtcsvwindow.cが次のレポジトリに収められています。 昨日のアップデートです。 scroll関数が上記のfunctionに相当します。

CSVファイルを編集する「tcsv」バージョン0.7

Cをしばらく休む予定だったのが、そうもいかず、tcsvの開発をしていました。 今日、バージョン0.7をGitHubにアップロードしました。

今回のバージョンは0.7で依然として開発版ですが、実用的なものになっています。 これでtcsvの開発は一区切りついた形です。

今回新しい版を作成したのは、GTK 4チュートリアルのセクション30で得られた技術を取り入れたからです。 また、プログラムのデータ構造をGtkStringListからGListStore+TCsvStrに変更したのも大きいです。

これで、GtkColumnViewとGtkTextを使った編集、そしてカレント・レコード(カレント行)を作る技術については、一応完成したといえます。 詳しくは、上記レポジトリのドキュメントを参照してください。