Raspberry Pi (raspbian) に Ruby/tk をインストール
Ruby/tk のインストールはライブラリを指定してあげないとうまくいかないので、多少面倒です。
とりあえず標準で入っている ruby 2.7 のまま
$ sudo gem install tk
とすると、これはエラーになってしまいます。
$ sudo apt install libx11-dev tk-dev $ sudo gem install tk -- --with-tcltkversion=8.6 --with-tcl-lib=/usr/lib/arm-linux-gnueabihf/ --with-tk-lib=/usr/lib/arm-linux-gnueabihf/
でうまくできました。
ちなみに macOS では
rbenv install 3.1.2 rbenv global 3.1.2
して
~/.zshrc に
eval "$(rbenv init -)"
を追加すれば最新版になる。Ruby tk をインストールし直し。
gem install tk -- tcltkversion=8.6 --with-tcl-lib=/opt/homebrew/opt/tcl-tk/lib/ --with-tk-lib=/opt/homebrew/opt/tcl-tk/lib
としておきました。
Python でもマルチスレッドプログラムを書いてみたらハマった
昨日の Ruby/tk で書いたマルチスレッドプログラムを Python/tkinter に移植したら、予想外の挙動に出会いました。
Ruby/Tk でマルチスレッドプログラム - minofoto and miscellaneous notes
import tkinter as tk import threading from datetime import datetime from time import sleep def update_calendar(label): while True: now = datetime.now() label.configure(text="{:04d}/{:02d}/{:02d}".format(now.year, now.month, now.day)) label.update() sleep(2) def update_clock(label): while True: now = datetime.now() label.configure(text="{:02d}:{:02d}:{:02d}".format(now.hour, now.minute, now.second)) label.update() sleep(1) root = tk.Tk() label1 = tk.Label(root, text="label1") label2 = tk.Label(root, text="label2") label1.pack() label2.pack() thread1 = threading.Thread(target=update_calendar(label1), daemon=True) thread2 = threading.Thread(target=update_clock(label2), daemon=True) thread1.start() thread2.start() root.mainloop()
これを実行すると
となってしまい、thread2 で更新されるはずの label2 に時間が表示されません。
これは、threading.Thread で引数の渡し方が間違っていたからのようです。正しくは
thread1 = threading.Thread(target=update_calendar, args=(label1,), daemon=True) thread2 = threading.Thread(target=update_clock, args=(label2,), daemon=True)
と、引数を (arg, ) とタブルにして、args=(label1,) のように 渡すべきでした。Ruby と比べるとちょっとややこしいですね。とはいえ、元の間違いのように threading.Thread(target=function(arg)) とした場合でもエラーにならずに function(arg) を実行するというのは不思議な挙動です。thread2 が実行されなかったことから、おそらくサブスレッドではなくメインスレッドで update_calendar(label1) が実行され、そこが無限ループなので止まっていたのでしょう。
Python の threading でちょっと嵌ってしまうポイントでした。
Ruby/Tk でマルチスレッドプログラム
Ruby/Tk でウィンドウ上のオブジェクトを定期的に更新するデモプログラムを作ってみました。
独立したスレッドからオブジェクトをそれぞれ更新するようにしてみます。試しに時計を表示させてみます。
require 'tk' # ウィジェットの作成 button = TkButton.new(text: "exit", command: proc {p "exit"; exit}) label1 = TkLabel.new(text: "", font: ['',24]) label2 = TkLabel.new(text: "", font: ['',24]) label3 = TkLabel.new(text: "") label1.pack label2.pack label3.pack button.pack Tk.mainloop
これで枠組みだけできました。
こんどは時計表示を更新する関数を作成
# カレンダーを更新 def update_calendar(label) now = Time.new label.configure(text: sprintf("%04d 年 %02d 月 %02d 日", now.year, now.month, now.day)) end # 時計を更新 def update_clock(label) now = Time.new label.configure(text: sprintf("%02d : %02d : %02d", now.hour, now.min, now.sec)) end
この関数を呼び出すために、Tk.mainloop の前に以下の記述を追加します。
update_calendar(label1) update_clock(label2)
時間が表示されました。
今度はこの時計を定期的に更新します。上記の2行をこのように書き換えます。
# 要素ごとにスレッドを立てて更新する Thread.new { while true update_calendar(label1) sleep(60) end } Thread.new { while true update_clock(label2) sleep(1) end }
Thread.new {} で並列に実行されるサブスレッドを作成します。スレッドが実行するのは {} の中の while true ~ end の無限ループ。
カレンダー表示の更新の後、 sleep(60) で 60 秒間待つため、1分間に1回カレンダーは更新されますが、時計の方は 1秒に一回更新されるはずです。
全部のコードはこんな感じ
require 'tk' # カレンダーを更新 def update_calendar(label) now = Time.new label.configure(text: sprintf("%04d 年 %02d 月 %02d 日", now.year, now.month, now.day)) end # 時計を更新 def update_clock(label) now = Time.new label.configure(text: sprintf("%02d : %02d : %02d", now.hour, now.min, now.sec)) end # ウィジェットの作成 button = TkButton.new(text: "exit", command: proc {p "exit"; exit}) label1 = TkLabel.new(text: "", font: ['',24]) label2 = TkLabel.new(text: "", font: ['',24]) label3 = TkLabel.new(text: "") # ウィジェットの配置 label1.pack label2.pack label3.pack button.pack # 要素ごとにスレッドを立てて更新する Thread.new { while true update_calendar(label1) sleep(60) end } Thread.new { while true update_clock(label2) sleep(1) end } Tk.mainloop
これで、時計を毎秒更新する一方、カレンダーは毎分更新するマルチスレッドプログラムができました。
Ruby で GUI プログラム
macOS に homebrew で新しい ruby を入れます。
brew install ruby
'export PATH="/opt/homebrew/opt/ruby/bin:$PATH"' >> ~/.zshrc
gem で Ruby/Tk をインストール。gem install tk はそのままでは通らないので、ライブラリを指定しないといけませんでした。homebrew のライブラリを探して指定しました。
gem install tk -- tcltkversion=8.6 --with-tcl-lib=/opt/homebrew/opt/tcl-tk/lib/ --with-tk-lib=/opt/homebrew/opt/tcl-tk/lib Building native extensions with: 'tcltkversion=8.6 --with-tcl-lib=/opt/homebrew/opt/tcl-tk/lib/ --with-tk-lib=/opt/homebrew/opt/tcl-tk/lib'
これで無事インストールできました。テストプログラムを書いてみます。
#!ruby # require でライブラリを読み込み require 'tk' # ボタンを新規作成 引数は "属性: 値" で指定。ボタンを押したときの挙動は command: proc {} で指定できる。 button1 = TkButton.new(text: 'Hello Ruby/Tk', font: ['',24], command: proc { exit }) # ボタンを配置。とりあえず pack でお任せ配置 button1.pack # mainloop を回すと、ループが無限に回る Tk.mainloop
ちゃんと動きました。
円周率の計算(Leibniz の式)
10年ほど使った Macbook もさすがに新しいソフトウェアがインストールできなくなってきたので、新しい Macbook Air を買った。円安のせいで高くなっていて財布に痛い。
体感的に「うわっ、速い!」と感じるようなことがないのは、1900年台から2000年代にかけてのパーソナルコンピュータの大幅な進化は終わってしまい、OS やらいろんなアプリケーションが目に見えて進化したということがないからだろう。とはいえ、速いと話題になった Apple Silicon の速さを確かめてみようと、あえて遅い Leibniz の式で円周率を計算させてみた。
整数で計算させるところは、前記事の Machin の式と同じ方法だ。計算結果の表示を間引くために余計な計算負荷があるかもしれない。
start = Time.now def Reipniz() factor = 4*100000000000000000000000 sum = factor div = 3 for i in 1..1_000_000_000 sum += -factor/div + factor/(div+2) div += 4 if i % 0x100000 == 0 puts "#{i} #{sum}" end end return sum end puts Reipniz() puts Time.now-start
結果はこの 10^9 回の計算で、314159265408978323814573 と小数点以下8桁まで計算できた。計算にかかった時間は core i7 3 GHz で 625 秒程度、M2 では 248 秒程度だった。計算中に core i7 はファンが回り出したが、M2 は静かだ。また、この計算はシングルコアしか使っていないはずだが、core i7 が 2コア、M2 は 4+4 コアであることを考えると、確かにめちゃくちゃ速くなっている。
という計算をして、高かったことを自分で納得しようとしているだけなのかな。
円周率の計算(Machin の式)
Ruby で遊んでいたら、高校生時代に友達と円周率を計算して遊んでいたことを思い出した。当時はまだコンピュータを持っておらず、関数電卓で円に内接する多角形の面積を計算したり、解析概論から Machin の式を見つけ出して計算したりしていた。友達が PC-9801 を持っていたが、どうやって倍精度計算をするか、その限界を越えるにはどうしたらいいか、といった話をグダグタとしていた。
Machin の式はこんな感じ。
この式が良いのは、冪乗があるものの、加減乗除だけで構成されていて単純なことだ。
ruby で書くと以下のようになる。
ruby は整数の計算は桁数の制限がないことと、この式はひたすら項を加減していくだけなので、全部の項に factor をかけておくだけで、小数の計算精度を考えずに計算できる。
def Machin() prec = 100 matched_index = 0 loop = 30 factor = 1 for i in 1..prec do factor *= 10 end factor1 = 16 * factor factor2 = 4 * factor sum = factor1 / 5 - factor2 / 239 div = 3 for i in 1..loop sum += -factor1 / (div * 5**div) + factor1 / ((div+2) * 5**(div+2)) sum -= -factor2 / (div * 239**div) + factor2 / ((div+2) * 239**(div+2)) div += 4 puts "#{sprintf("%8d",i)} #{sum}" end return sum end $start = Time.now puts "Machin の式による円周率" Machin() puts Time.now-$start
古い MacBook Pro (Core i7-4578U 3GHz dual core) で実行してみると意外に速い。1 msec 程度で小数点以下 30桁近く計算できているようにみえる。
$ ruby machin.rb Machin の式による円周率 1 31416210293250344250468325171164080697062446182134305548606396697574415082581156863085774715303620079 2 31415926824043995172402598360735758604897579967201355063017547035914903487748109950086128003694245533 3 31415926536235547619955045938203118459271322712991732260010405689815976684675012175940708988985388463 4 31415926535898358474857006722516843955090730354281179593931838398495000890439868309465181373830789786 5 31415926535897932947473748577153435433787472210278399039415887536036600624560331366617000933453000146 6 31415926535897932385393245926718652825091820036365355561499505447671429038153835703541996763189559804 7 31415926535897932384627502076754923578603952858843005625356465856445914191569978625053179643878350850 8 31415926535897932384626435346265610108418224608598626348719124702975728463343742259229373035461723957 9 31415926535897932384626433834967774246425946616320634070726846710697736185351464266957897915043585053 10 31415926535897932384626433832798181320873204152280609055092074978365028242887424241942263143313044890 11 31415926535897932384626433832795033456017380588539006987908612446065286640820240779409963401710977707 12 31415926535897932384626433832795028848774340169568717799892980661436541525752937262258421934060087564 13 31415926535897932384626433832795028841981785615050020762231123465728069457680425238581026447563602177 14 31415926535897932384626433832795028841971709044323951350064488696988037559753790469840994549636967408 15 31415926535897932384626433832795028841971694016301271684489234984290065900959680994986868403485258600 16 31415926535897932384626433832795028841971693993784982166044833812760297891191671226977100393717248832 17 31415926535897932384626433832795028841971693993751109427006988812698398829976010400673834116408142191 18 31415926535897932384626433832795028841971693993751058287322259793318396065343944029846128154609955813 19 31415926535897932384626433832795028841971693993751058209867272216277219504082451389153487461969263172 20 31415926535897932384626433832795028841971693993751058209749625351706126852156677062114887680753448201 21 31415926535897932384626433832795028841971693993751058209749446196953453517888686699842356993297743027 22 31415926535897932384626433832795028841971693993751058209749445923497087355714539445875926316609121046 23 31415926535897932384626433832795028841971693993751058209749445923078806095979536420509089060197389981 24 31415926535897932384626433832795028841971693993751058209749445923078165048578878411934919267800531363 25 31415926535897932384626433832795028841971693993751058209749445923078164064377944349349611767129831896 26 31415926535897932384626433832795028841971693993751058209749445923078164062864424622439667791268307192 27 31415926535897932384626433832795028841971693993751058209749445923078164062862093586998418682337244371 28 31415926535897932384626433832795028841971693993751058209749445923078164062862089991840979712133067678 29 31415926535897932384626433832795028841971693993751058209749445923078164062862089986288946017364411264 30 31415926535897932384626433832795028841971693993751058209749445923078164062862089986280361562208912209 0.001099
思ったより早かったので、調子に乗って、桁数を増やしてみた。また、項を加えても変わらない桁までを計算できたとみなして、その桁だけを表示するように変えてみた。
## digit 桁(前の計算でマッチしたところ)から文字列の長さまでの s1, s2 を比較する。 ## 計算が一致しているところまでを正しく計算できているとみなして、その桁数を返す def check_match(s1,s2,digit) matched_index = -1 for i in digit..s1.length do if s1[i] == s2[i] matched_index = i else return matched_index end end return -1 end def Machin() prec = 100000 matched_index = 0 loop = 10000 str = "314" factor = 1 for i in 1..prec do factor *= 10 end factor1 = 16 * factor factor2 = 4 * factor sum = factor1 / 5 - factor2 / 239 div = 3 for i in 1..loop sum += -factor1 / (div * 5**div) + factor1 / ((div+2) * 5**(div+2)) sum -= -factor2 / (div * 239**div) + factor2 / ((div+2) * 239**(div+2)) div += 4 #puts "#{sprintf("%8d",i)} #{sum}" str_prev = str str = sum.to_s matched_index = check_match(str_prev, str, matched_index) puts "#{i} 回目の計算 (#{matched_index}桁 / #{(Time.now - $start)} sec)" puts "3.#{str[1..matched_index]}" #計算結果の見た目を整える if matched_index < 0 break end end return sum end $start = Time.now puts "Machin の式による円周率" Machin() puts Time.now-$start
実行してみると、見た目で桁数が増えていくので楽しい。
$ ruby pi-machin.rb Machin の式による円周率 1 回目の計算 (2桁 / 1.757707 sec) 3.14 2 回目の計算 (3桁 / 1.844319 sec) 3.141 3 回目の計算 (7桁 / 1.924601 sec) 3.1415926 4 回目の計算 (9桁 / 1.999731 sec) 3.141592653 5 回目の計算 (12桁 / 2.077495 sec) 3.141592653589 6 回目の計算 (16桁 / 2.154938 sec) 3.1415926535897932 7 回目の計算 (18桁 / 2.233316 sec) 3.141592653589793238 8 回目の計算 (21桁 / 2.31235 sec) 3.141592653589793238462 9 回目の計算 (24桁 / 2.404382 sec) 3.141592653589793238462643 10 回目の計算 (27桁 / 2.483269 sec) 3.141592653589793238462643383
......
273 回目の計算 (764桁 / 24.741014 sec) 3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196442881097566593344612847564823378678316527120190914564856692346034861045432664821339360726024914127372458700660631558817488152092096282925409171536436789259036001133053054882046652138414695194151160943305727036575959195309218611738193261179310511854807446237996274956735188575272489122793818301194912983367336244065664308602139494639522473719070217986094370277053921717629317675238467481846766940513200056812714526356082778577134275778960917363717872146844090122495343014654958537105079227968925892354201995611212902196086403441815981362977477130996051870721135000
25秒ほどで 760桁ほど計算できた。ただ、結果が合っているのかは確認していません...
パンチカードを並べ替える
Ruby で遊ぶネタが他にないかな、と思っていたら、子供の頃パンチカードを作って遊んだのを思い出しました。
数字を書いたカードの上部に穴を開け、2進数でその数字を示せるように、パンチカードに切り欠きを入れます。
たとえば2のカードは、右から2番目の穴の上を切って溝に、3のカードは1番右と2番目の穴を切ります。
そうしておくと、竹串を順番に穴を通して引っかかったカードだけを並べ替えていくと、カードを順番に並べることができるというもの。
これを書いてみました。
まずカードを前後に動かす操作。b が穴の場所を示す2進数です。
def put_forward(array, b) int_array_1 = [] int_array_2 = [] for i in array if i & b == b int_array_1.push(i) else int_array_2.push(i) end end int_array_1 += int_array_2 end def put_backward(array, b) int_array_1 = [] int_array_2 = [] for i in array if i & b == b int_array_2.push(i) else int_array_1.push(i) end end int_array_1 += int_array_2 end
こうしておいて、
int_array_ordered = 1..31 int_array_rand = int_array_ordered.sort_by{rand} int_array_new = int_array_rand.dup for i in [0b00001, 0b00010, 0b00100, 0b01000, 0b10000] int_array_new = put_backward(int_array_new, i) end print int_array_ordered, "\n" print int_array_rand, "\n" print int_array_new, "\n"
とするとちゃんとソートされていることがわかります。
調べると、基数ソートという名前がついているようですね。
一応こんな関数を書くとソートできます。
def punchcardsort(array) len = (array.max).to_s(2).length sort_key = [] for i in 1..len sort_key.push(2**i) end for i in sort_key array = put_backward(array, i) end return array end
しかしもちろん、ruby に用意されている sort を使った方がずっと速いようです。
Python, Ruby, awk
つづき
素数計算するプログラムを awk でも書いてみた。普通に書くと BEGIN{} の中に全部書くことになりそうだけど、それだと awk らしくないので、
function is_prime(n) { j = 2 if (n < 2) return false while (j*j <= n) { if (n % j == 0) return false else j++ } return true } BEGIN{ c = 0; } { if (is_prime($0)) { prime[c++] = $0 } }
として以下のように実行してみた。1段目の awk は整数を生成するだけ。2段目はパイプで受け取った数字の素数判定をするだけ。また、計算速度を比較するために、print はやめて、配列に素数を保存するだけにした。
$ time gawk 'BEGIN{for (i=1; i<=1000000; i++) print(i)}' | gawk -f prime.awk
real 0m14.624s
user 0m14.940s
sys 0m0.071s
実行時間はおよそ 15秒。
Python だと約10秒。
$ time python prime.py
real 0m10.286s
user 0m10.151s
sys 0m0.063s
一応コードも上げておく。
def is_prime(n): if n <2: return False i = 2 while i*i <= n: if n % i == 0: return False else: i += 1 return True prime =[] for i in range (1000000): if is_prime(i): prime.append(i)
Ruby はなんと3秒。
$ time ruby prime.rb
real 0m3.081s
user 0m2.997s
sys 0m0.047s
コードはこちら。
class Integer def is_prime() if self < 2 then return false end i = 2 while i*i <= self if self % i == 0 return false else i += 1 end end true end end prime = [] for i in 1..1000000 do if i.is_prime() prime.push(i) end end
こんなに差がつくとは思いませんでした。ちなみに、awk を全部 BEGIN{} に入れてしまうと
function is_prime(n) { if (n < 2) return false j = 2 while (j*j <= n) { if (n % j == 0) return false else j++ } return true } BEGIN{ for (i=1; i<=1000000; i++){ if (is_prime(i)) prime[c++] = $0 } }
real 0m15.649s
user 0m14.805s
sys 0m0.219s
パイプで渡したときとほぼ同じだけど、ちょっとだけ遅くなりました。
Python と Ruby
プログラミングは素人だ。というより、自分がプロだと言える分野はないなと思う。それはさておき、
最近趣味と仕事で Python プログラミングをする機会があった。Python を選んだ理由は、ライブラリが充実していてノウハウが簡単に見つかるというだけだ。とはいいながら、以前から気になりつつもほとんど使ったことがない Ruby もちょっとだけ触ってみた。Ruby は既存のクラスにもインスタンスメソッドを追加できる。
100000 までの素数を表示する。
require 'time' start = Time.now() class Integer def is_prime() if self < 2 then return false end i = 2 while i*i <= self if self % i == 0 return false else i += 1 end end true end end for i in 1..100000 do if i.is_prime print(i, " ") end end puts "\nTime", Time.now-start
Ruby は Integer クラスに勝手にインスタンスメソッドを定義できてしまう。これを Python で書くと
import time start = time.time() def is_prime(n): if n <2: return False i = 2 while i*i <= n: if n % i == 0: return False else: i += 1 return True for i in range (100000): if is_prime(i): print(i, end=' ') print("\nTime", time.time()-start)
となる。こちらは関数で書くしかなさそう。いや、Ruby だって関数で書けるんだけど。それはさておき、どちらの言語が好きかと言われたら難しい。インデントだけでブロックを表示する Python より Ruby のほうがわかりやすい気がする。書籍もネット上の情報も Python の方が多いし、仕事で使うなら Python なんだろうけど。
ちなみに実行速度は Python のほうが少しだけ早かった。が、どうやら画面表示の速度がボトルネックらしい。表示のところをコメントアウトすると Ruby の方が少し速かった。
そういえば、学生時代に C++ で遊んでみたときは、どうにもオブジェクト指向というものがよくわからなかった。なぜか今はほとんど違和感がなくなった。どうやら、抽象的な概念を抽象的なまま理解したらしい。
うまく説明できないが、オブジェクトとは作るためにあるのではなく、使うためにあるのだと感じる。上のプログラムも、for i in 1..100000 do
から書くと if i.is_prime と書きたくなるが、is_prime の定義から書き始めると、関数で書きたい気持ちになる。
だからたぶん、使った経験が少なかった頃は、オブジェクトを作ることもできなかったのだろう。既存のライプラリをたくさん使い、オブジェクトを使うことに慣れたので、自分で書けるようになったのだろう。たぶん。
論文査読依頼が来たときのメモ
論文査読 (peer review) 依頼が来たときのための自分用メモ。たぶん後で書き換える。
- ジャーナルがハゲタカジャーナルなどではないか、まともなところか調査する。
- 自分が査読できる分野かどうか、タイトルと要旨をよく読む。
- 査読のしかた
www.jaog.or.jp
https://www.ipsj.or.jp/magazine/9faeag000000yx8j-att/6101ronbun.pdf
https://www.jstage.jst.go.jp/article/jjsp/33/2/33_98/_pdf/-char/ja
論文の査読のしかた (TAKENAKA's Web Page)
効果的な査読(ピア・レビュー)を行うには|ThinkSCIENCE株式会社|英文校正・学術論文翻訳
早く論文査読をする方法 -前半- – Systems Android Robotics
研究論文の査読レポートの書き方 - 日本の科学と技術
https://www.jstage.jst.go.jp/article/jagn/23/2/23_34/_pdf
- ほか参考
査読される研究者は査読もする | お役立ちコンテンツ|アカリク
論文の査読 - Inoue Methods
査読依頼を断っていいのはどんなとき? ジャーナル編集長が解説 | ワイリー・サイエンスカフェ
波乱の2021年
今年はコロナ禍に入って2年目。
SARS-CoV-2 の第一波は横浜のクルーズ船の騒ぎの次に、昨年の北海道雪まつりから始まった。夏の第二波は札幌には来なかったものの、今年の初めは第三波の中で始まり、5月のα株による第四波、そして8月の第五波はδ株が猛威を振るった。さいわいにも、職域接種でモデルナワクチンを打ってもらうことができた。職域接種は、会社の横のつながりで飛行機に乗って東京で受けるという裏技だったために、移動中に δ株に感染してはかなわないと、KN95 マスクや KF94 マスクを装備し、万全の警戒態勢での移動となった。さらに出張と組み合わせて出張先のホテルで1日発熱に耐えた翌日に打ち合わせというハードなスケジュールだった。それでも mRNA ワクチンを接種できたことは幸いだった。
そうやってコロナ禍の中でも結果を出して行く一方で、会社の先行きは細るばかり。いくら自分たちが結果を出しても、その先が見えないという絶望的な状況になった。おかしなマイクロマネジメントが始まり、一番良く働いていた中堅2人が不満をぶちまけて続けざまに退職してしまった。代わりの人材募集の代わりにコンサルに業務を丸投げするとのこと。そのうえ社内体制が改悪され、自分が持っていた権限を事実上失ってしまった。これまでのように現場での判断も自由にできなくなった。毎週の会議はパワハラすれすれの取締役の独壇場になり、発言も自由にできなくなった。
もともと多少あった睡眠の問題が悪化し、夜中に必ず二三回目が覚めるようになった。朝起きると無意識に「ああ疲れた」と呟くようになり、昼間も疲れがとれない。メンタルクリニックで睡眠薬など処方してもらって、ようやく眠れるようになった。
そんな中、会社からはほとんど無視されながら、社外の共同研究者と論文を出すことができた。また、これまでの私を知っている方から、面白そうな転職先を紹介してもらえた。お話を聞かせてもらって、「よしここだ」とすぐに決断し、正式な内定をもらう前、給与すらわからない段階で退職を願い出た。紹介してくれた方の信用があったからこその大博打だったが、面倒なパワハラに合わないため、そしていろんな方への義理を果たし、嘘をつかずに信用を守り切るためのギリギリの線だった。妻の仕事も、たまたまキャリアアップになりそうなところが公募を出していて、そちらも内定をもらうことができた。しばらく落ち着くつもりだった札幌から東京への引っ越しは大変だけど、これもまた縁だと思う。
新しい仕事は面白そうだけど、自分が本当にこの仕事に貢献できるのかな、と不安もある。この歳になってよくこんな無茶できるよな、と自分で呆れている。でも、これまでの失敗を繰り返さない要素は揃えられたと信じている。来年は、引っ越しのタイミングと ο 株の感染爆発が重ならないことを祈るばかりだ。
2022年は新天地への異動で幕を開ける。人生は面白い。本年もよろしくお願いします。
しあわせについて
JAF メイト11月号の冒頭に北川悦吏子さんが書いている「しわわせの呪縛」というエッセイがとても良かったのでメモ。短いエッセイで、著作権侵害になっちゃいそうな気もするので、内容は紹介しない。JAF メイトが手元にある方はぜひ読んでみてほしい。
私が人生の中で「しあわせな瞬間」として特別に記憶しているのが、とある春の夕暮れのこと。自分の息子が少し大きくなり、手がかからなくなってきた頃、アパートのベランダでニラを植えて育て始めた。とはいっても、プランターに近所のホームセンターで買ってきたタネをまいただけ。息子が遊び疲れて家族みんなが昼寝をしてしまった夕暮れに、ひとりでベランダに出てニラが育ってきたのを眺めていた。周囲は一軒家ばかりの住宅街で、家々から夕飯の支度か、片付けなのか、生活感のある物音がカタコトと響いてきていた。陽が傾き、空の色が青からくすんだブルーに近づいていった。ただ風だけが通りを抜けてゆき、ニラがそよそよと揺れた。
なぜあれが自分にとって忘れられない「しあわせな瞬間」のひとつなのか、不思議な感じをずっと抱いていた。あの頃、仕事は大変で先の見通しも立たず、かなり辛かったはずなのに。でもあれが幸せなのだということが、北川さんのエッセイを読んで腑に落ちた。幼い頃もそういうしあわせな瞬間がたくさんあったと思う。今の生活でも、なるだけそれを増やせるようにしていきたい。
とても良い文章を読ませてもらったと思う。
追記
もう一つ、今日はとても嬉しいメールをいただき、丁寧にお返事した。失礼かもしれないが、相手をきちんと見て、感情が丁寧にこもっているメールはラブレターみたいだな、と思った。性別とか役割とか関係なく、相手を深く思いやると、限りなく恋文に近くなる。いや、若い頃に書くラブレターは必死すぎて「しあわせ」だなんて思いもしなかったので、やっぱり違うかな。
今年は遅いですが、もうすぐ雪の季節になりそうです。
顔の記憶の話
ここ数日の一番のハイライトは、ずっと音信不通だった息子の顔を見られたこと。なぜか床屋に居候していた。ちょっと浮かない顔をしていた。
40年前、片思いをしていた女の子が体育館でニコッと微笑みかけてくれたことを思い出した。
どちらも夢の話。現実にはそんなことはなかった。
最近ある薬を飲むようになってから、毎日のように夢を見る。正確には、睡眠サイクルが変わって明け方に見た夢を覚えていることが多くなったのだろう。夢を見ることがなくなってからもう十年以上、いや二十年以上経っている気がする。
こんな歳になっても小さなことから人は変わることができる。
若山牧水と釈迢空
小学生の頃、自殺願望があった。
通っていた近所の小学校の向こう側には、夜になると真っ暗な裏山があり、狐が棲んでいた。夜中にそこに忍び込み、木の枝にロープを吊るせば首を吊ることができる、そんなことを思い描いていた。なぜそう思っていたのか定かではない。たぶん理由などなかったのだろう。漠然とした孤独感と寂寥感、形のない自殺願望を小学校に上る前から抱いていたことは覚えている。
外面的には、健康に大きな問題はなく、食べるのに困ることもなく、住むところにも困ってはいない。家族にも恵まれ、客観的に見ればむしろ恵まれた生活をしていたはずだ。子供が死に関する話をすると激しく当惑される。それもよくわかっていたから、そんな話を親にも他人にしたことはほとんどない。でも実際のところ、生きていると辛いことはたくさんあって、たまに楽しいことや素晴らしいことがある、生きるとはそんなものだと思ってきた。世の中の多くの人はどうやら違うらしいということは、もう少し大きくなってから理解するようになった。「死にたいなんて思ったこともない」「学校が嫌だなんて夢にも思ったことがない」という人が実在する、いやむしろ、そちらのほうがどうやら多数派のようだ、ということを知ったのはたぶん中学生ぐらいか、それより後だったかもしれない。
子供の頃、お風呂で父が「自殺がいかに痛くて苦しくて辛いか」という話を始めたことがあった。富士の樹海で首を吊った人があわやのところで助けられ、「痛くて辛かった。助けてもらってありがたい」と言ったというようなエピソードを語ってくれた。あまりに唐突な話への違和感から、その話はずっと忘れなかった。今思えば、父もまた私と同じような漠然とした不安や自殺願望を抱きながら生きてきた人なのかもしれない。息子に同じものを感じ取った父が私にくれたメッセージだったのかもしれない。こういう感情的な傾向や性格は遺伝するものなのだろうか。いずれにせよ、その記憶は私をずっと支え続けた。死にたいという気持ちが浮かび上がると、決まってその時の父の声がふんわりと、私を押しとどめてくれた。
実際には、首を吊るロープを用意するといった具体的な行動に及ぶことはなかった。ただ子供らしからぬ漠然とした孤独感、寂寥感、不安感をずっと抱えつつ、表向きは子供らしく生きてきたのである。
そんな子供がある日、国語の教科書に、こんな歌を見いだした。
「幾山河越えさりゆかば寂しさの果てなむ国ぞ今日も旅行く
葛の花踏みしだかれて色あたらし この山道を行きし人あり」
たったひとりで果てしない旅路をゆく若山牧水の寂寥感に続いて、釋迢空の鮮烈な葛の花の色が、決して届かないわけではない「何か」を予感させる。
この教科書を読んだ多くの子供たちは、読んですぐ忘れてしまっただろう。しかし少なくとも私は、この二首が並んでいることに衝撃を受けた。生きることへの漠然とした不安、寂寥感をそのまま受け止めてくれる若山牧水の歌。それだけで終わらせずに、かすかな希望の色を葛の花に託して見せてくれた。よくもまあ小学生に向かってこの鮮烈なメッセージを発してくれたものだ、と名も知らぬ教科書の編纂者に今更ながら感嘆し、感謝する。ひょっとしたら、私のような子供がいることを知るどこかの先生が贈ってくれたメッセージなのかもしれないとすら思う。父の言葉とともに、この二首は、辛いことが起きるたびに膨れ上がる心の底の空洞を埋め続けてくれた。歩き続ける長い長い山道には、いつもこの景色が重なって見えていた。
さて、私は見知らぬ誰かに、こういうメッセージのバトンを繋ぐことができるだろうか。この歌に出会った頃ほどの鮮烈な感受性はもうずっと前に失ってしまったけれど、それでも、こんどは私が誰かに向かって、あの葛の花のような希望のメッセージを伝えることができるのだろうか。
10年日記
10年前を思い返すと、東日本大震災の年だ。
当時、物理系の論文原稿を書き上げ、トップジャーナルに投稿してエディターリジェクトとなり、早く再投稿しようとボスにプレッシャーをかけていた。マニアックではあるがそこから応用にも研究を広げる余地がある良い論文だったと思うけど、1回のリジェクトでボスには投稿を後回しにされてしまい、あの論文はたぶんまだ当時のボスの PC に眠ったままだと思う。
家庭内はぼろぼろで、家に精神的な居場所がなくなった頃でもあった。仕事の後にコンビニでお弁当とビールを買って、一人で暗い居間で遅い夕食を取っていた記憶がある。休みの日も家にいられないので自転車で多摩川沿いを走っていた。
初夏の頃になり、父から妙に明るい声で電話があった。ステージ4の癌で余命数ヶ月だという。山が好きな父への感謝を込めて、夏には山に連れて行こうと計画も立てていたところだった。言葉を失うというのはこういうことかと思った。余命の長さより、残された人生の QOL を取りたいという治療方針も一人で決め、最後に本を1冊書き上げ、年末には「空手還郷」という言葉を残してこの世を去っていった。
たまに実家に帰って父との残りの時間を惜しみながらも、自分の家庭は完全に崩壊したのでアパートを探して別居し、夕飯は近所の沖縄料理屋に通うようになった。後で聞いたことだが、カウンターで一人で泡盛を飲んでいた私の背中からは、近寄りがたい黒いものが漂い出していたらしい。
そのお店には毎週通うようになり、最初はマスターとだけ話していたが、カウンターでふらりと同席した人と話をするようになり、更に友人と呼べる人たちもできた。2013年に閉店するまで、マスターや常連さんたちには心の支えになってもらったと思う。閉店後も連絡を取り合い、その仲間たちと朝まで飲み歩いたり、ゲーム会に誘ってもらったり、いろんな付き合いが続いた。
また、2013年には登山を再開した。学生時代が終わり、子育てと仕事が忙しくやめてしまっていたのだが、登山靴を磨き直して2泊3日で木曽駒ヶ岳、空木岳を縦走した。一人ではあったが、小屋で意気投合した人たちとビールを酌み交わし、楽しい山行となった。2013年中には雲取山にも登り、2014年には雪のなか丹沢塔ノ岳と鍋割山、奥多摩の三頭山に登り、鳳凰三山を縦走した。単独行だけでなく、仲間を探すようになり、相模湖のほとりの嵐山、奥多摩の川苔山、奥秩父の金峰山、赤城山、日光男体山といろんなところに登った。2015年も鍋割山、天城山、棒ノ折と登った。
2013年には FEBS Letter に論文が掲載されている。これはトラブルでラボを去った後輩の仕事を引き継いでデータを追加し、論文にしたもの。「こんな研究は論文にしても仕方ない、インパクトファクター 1.6 ぐらいのマイナー雑誌になら投稿してもよい」、と突き放されつつ、価値がある仕事だからとボスを繰り返し説得して出した論文。これは後から学会などでも「なんでもっと良い雑誌に出さなかったの」と聞かれることになり、自分の考えの方が正しかったことを示すことができた。というより、自分の視野がもっと広ければ、もっと良い雑誌に投稿できたはずなので、そこは後悔している。それはともかく、その論文の続きを進めるべく入念に作戦を練って研究所内セミナーで発表し、ラボの外で自分の研究をサポートしてくれる共同研究者を見つけて進めた研究は、ラボの方針と違うと毎週のミーティングで責められたが、耐え忍んでなんとか 2016年の頭には Nature Communications に掲載することができた。
その過程で、ボスとの考えの違いが明白となり、もうこの環境では仕事はできないと、転職活動をはじめた。アカデミックなポジションはもちろんずっと探してきたけれど、公募されるポストはどんどん減ってくるし、自分の少ない業績に加えて年齢を考えると厳しいことは分かっていたので、本格的に民間企業への転職を探した。アカデミックなポジションにはついに面接に呼ばれることすらなかったが、民間企業はいくつか面接に呼ばれ、その中から運良く札幌の小さなベンチャー企業に開発職として採用された。
これまでやってきた基礎研究を捨て、1から新しいことをやる覚悟で北海道に渡り、入社後は頭を下げて自分より若い人たちからいろんなことを学んだ。しかし、驚くほど運が良かったのは、私が得意としていたにもかかわらず入社時には聞かれもしなかったある技術が、会社の状況が大きく変わったときに必要とされたことだった。また、アカデミアで苦労しながら後輩のポスドクやテクニシャンたちのサポートをしてきたことも大いに役に立ち、管理職として一部門を任せてもらえることになった。世の中ではアカデミアのスキルは民間では通用しない、とよく言われていたのだけど、少なくとも自分の経験の範囲では決してそんなことはなかった。
今は開発やマネジメントの仕事の傍ら、レビュー論文を頼まれたり、共同研究の論文書きのお手伝いなど、一応ではあるが研究も続けられている。
また、相手も見つけて2016年には2度めの結婚もした。
思い返すと、大げさに言えばどん底から這い上がる、もしくは自分の若い頃の失敗による負債を返済する10年だった。それは苦難の道でもあったけど、2011年当時には想像すらできなかった面白い10年だったとも言える。次の10年がどうなるか全くわからないが、そろそろいい歳なので、後進を育てることにも注力しつつ、そろそろ老後ということも考えていくことになるのだろうな、と思っている。