@ledsun blog

無味の味は佳境に入らざればすなわち知れず

イベントハンドラーAの処理があるときはイベントハンドラーBの処理を止めたいです

ブラウザのJavaScriptの話です。 イベントハンドラーAの処理があるときはイベントハンドラーBの処理を止めたいです。

イベントハンドラーAは新しいNodeを作って選択します。 イベントハンドラーBは選択を解除します。

期待する動作は、新しいNodeができて選択されてほしいです。今はイベントハンドラーBが発火して、新しいNodeの選択が解除されます。

最初、stopPropagationを使おうと思いましたが、どちらのイベントハンドラーもリッスンしているElementは同じのため、伝播タイミングが同じなので、ダメでした。 とりあえず、イベントハンドラーAでイベントオブジェクトにフラグをたてて、イベントハンドラーBでフラグチェックすれば、期待通りに動作します。 もう少しかっこいい実装方法はないものでしょうか?

  1. イベントオブジェクトではない、何かで状態を持つ
  2. イベントハンドラーAのリッスンするElementを、子Elementにする(で、stopPropagationを使う)
  3. イベントハンドラーBをイベントハンドラーAより先に発火させる

結局2にしました。

rbenvでのバージョン指定が効かなくなりました

~ rbenv version
2.5.5 (set by /Users/shigerunakajima/pubannotation/.ruby-version)
~ ruby --version
ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-darwin18]

ruby のバージョンが上がっている。パスが上書きされたのでしょうか?

~ echo $PATH
/usr/local/opt/libxml2/bin /usr/local/opt/ruby/bin /Users/shigerunakajima/.yarn/bin /Users/shigerunakajima/.rbenv/shims /usr/local/bin /usr/local/bin /usr/bin /bin /usr/local/sbin /usr/sbin /sbin /opt/X11/bin /usr/local/share/dotnet ~/.dotnet/tools /Library/Frameworks/Mono.framework/Versions/Current/Commands /Applications/Wireshark.app/Contents/MacOS

/usr/local/opt/ruby/bin/ruby/Users/shigerunakajima/.rbenv/shims より前にいるのがよくないのでしょうか? /usr/local/opt/ruby/bin/ruby を消してみましょう。

あれ?historyset -U fish_user_paths (string match -v /usr/local/opt/ruby/bin $fish_user_paths) が残っているなあ・・・前にも同じことをしたのかな? 実行してみます。

~ set -U fish_user_paths (string match -v /usr/local/opt/ruby/bin $fish_user_paths)
set: Universal variable 'fish_user_paths' is shadowed by the global variable of the same name.

なぜか怒られます。グローバル変数の fish_user_paths がある?

~/.config/fish/config.fishset -g fish_user_paths "/usr/local/opt/ruby/bin" $fish_user_paths って書いてありました! 消したら

~ rbenv local 2.5.5
~ ruby --version
ruby 2.5.5p157 (2019-03-15 revision 67260) [x86_64-darwin18]

わーい

ただしHomebrewで入れているRubyは使えなくなりました。PATHから /usr/local/opt/ruby/bin 消したんだから、そりゃそうか・・・ set -U fish_user_paths $fish_user_paths /usr/local/opt/ruby/bin したら

~ echo $PATH
/Users/shigerunakajima/.yarn/bin /usr/local/opt/ruby/bin /Users/shigerunakajima/.rbenv/shims /usr/local/bin /usr/local/bin /usr/bin /bin /usr/local/sbin /usr/sbin /sbin /opt/X11/bin /usr/local/share/dotnet ~/.dotnet/tools /Library/Frameworks/Mono.framework/Versions/Current/Commands /Applications/Wireshark.app/Contents/MacOS

rbenvの前に来ちゃった・・・

再起動したら理想的な並び順に変わりました。

~ echo $PATH
/Users/shigerunakajima/.rbenv/shims /usr/local/bin /Users/shigerunakajima/.yarn/bin /usr/local/opt/ruby/bin /usr/local/bin /usr/bin /bin /usr/local/sbin /usr/sbin /sbin /opt/X11/bin /usr/local/share/dotnet ~/.dotnet/tools /Library/Frameworks/Mono.framework/Versions/Current/Commands /Applications/Wireshark.app/Contents/MacOS

なんじゃそれ?

gem install libxml-ruby -v '3.1.0' に失敗します

Building native extensions. This could take a while...
ERROR:  Error installing libxml-ruby:
    ERROR: Failed to build gem native extension.
    current directory: /Users/shigerunakajima/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/libxml-ruby-3.1.0/ext/libxml
/Users/shigerunakajima/.rbenv/versions/2.5.5/bin/ruby -r ./siteconf20200414-17043-wpqllw.rb extconf.rb
/Users/shigerunakajima/.rbenv/versions/2.5.5/bin/ruby: warning: shebang line ending with \r may cause problems
checking for libxml/xmlversion.h in /opt/include/libxml2,/opt/local/include/libxml2,/usr/local/include/libxml2,/usr/include/libxml2... no
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.  You may
need configuration options.
Provided configuration options:
    --with-opt-dir
    --without-opt-dir
    --with-opt-include
    --without-opt-include=${opt-dir}/include
    --with-opt-lib
    --without-opt-lib=${opt-dir}/lib
    --with-make-prog
    --without-make-prog
    --srcdir=.
    --curdir
    --ruby=/Users/shigerunakajima/.rbenv/versions/2.5.5/bin/$(RUBY_BASE_NAME)
    --with-xml2-config
    --without-xml2-config
    --with-xml2-dir
    --without-xml2-dir
    --with-xml2-include
    --without-xml2-include=${xml2-dir}/include
    --with-xml2-lib
    --without-xml2-lib=${xml2-dir}/lib
 extconf failure: need libxml2.
    Install the library or try one of the following options to extconf.rb:
      --with-xml2-config=/path/to/xml2-config
      --with-xml2-dir=/path/to/libxml2
      --with-xml2-lib=/path/to/libxml2/lib
      --with-xml2-include=/path/to/libxml2/include
To see why this extension failed to compile, please check the mkmf.log which can be found here:
  /Users/shigerunakajima/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/extensions/x86_64-darwin-18/2.5.0/libxml-ruby-3.1.0/mkmf.log
extconf failed, exit code 1
Gem files will remain installed in /Users/shigerunakajima/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/libxml-ruby-3.1.0 for inspection.
Results logged to /Users/shigerunakajima/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/extensions/x86_64-darwin-18/2.5.0/libxml-ruby-3.1.0/gem_make.out

libxml2が見つからないみたいです。

~ brew install libxml2
Warning: libxml2 2.9.10_1 is already installed and up-to-date
To reinstall 2.9.10_1, run `brew reinstall libxml2`

Homebrew的にはインストール済みです。

checking for libxml/xmlversion.h in /opt/include/libxml2,/opt/local/include/libxml2,/usr/local/include/libxml2,/usr/include/libxml2... no

とエラーメッセージが出ています。ヘッダファイル自体はあります。

~ ls /usr/local/opt/libxml2/include/libxml2/libxml/xmlversion.h
/usr/local/opt/libxml2/include/libxml2/libxml/xmlversion.h

ヘッダファイルを探すパスに /usr/local/opt/libxml2/include/libxml2 が入っていないのが問題のようです。

f:id:ledsun:20200508061818p:plain

~ bundle config build.libxml-ruby --with-xml2-config=/usr/local/opt/libxml2/bin/xml2-config
~ bundle config
Settings are listed in order of priority. The top value will be used.
build.libxml-ruby
Set for the current user (/Users/shigerunakajima/.bundle/config): "--with-xml2-config=/usr/local/opt/libxml2/bin/xml2-config"

gem installは失敗します。だってbundle configだもん。 でもbundle installは成功するので、大丈夫でした!

切符を通したときだけ通れる改札口Ractor

前回、Ractor.selectでtakeとrecvを両方待てることがわかりました。 これを使って切符を通したときだけ通れる改札口を作ります。

完成形

キーボード入力を待つRactor = Ractor.new { loop { Ractor.yield gets.chomp } }

改札口Ractor = Ractor.new(キーボード入力を待つRactor) do |キーボード入力|
  ゲート = :閉

  loop do
    # キーボード入力と自身へのメッセージを両方待つ
    メッセージの種類, 受信したメッセージ = Ractor.select キーボード入力, self

    if メッセージの種類 == :recv
      ゲート = :開
    else
      Ractor.yield 受信したメッセージ if ゲート == :開
      ゲート = :閉
    end
  end
end


loop do
  改札口Ractor.send 'に切符を入れる'
  入場者 = 改札口Ractor.take

  p "#{入場者} が入場しました"
  sleep 1
end

実行すると、たくさんキー入力をしても、1秒に一回だけ受け付けます。

感想

Ractor.selectの戻り値の第一引数、自身へのイベントだと:recv帰ってくるのですね。selfが帰ってくるのかと思っていました。

なんだろう、この・・・ES5以前のJavaScript感。懐かしい。クラスを作らないで...

  1. Ractorでスコープを切る
  2. ブロック内のローカル変数で状態を持つ
  3. Ractor.newで定義と同時に、外部への参照を受け取る(実行する)

モジュールパターンやんけ!

var module1 = function(initState){
  var state = initState

  return {
    getState: function(){
      return state
    },
    setState: function(newVal){
      state = newVal
    }  
  }
}('hoge')

みたいな、関数スコープしかなかった時代にプライベートな状態を持つオブジェクトを作るためにやっていた工夫と似ている点を感じました。

Ractor.recvの代わりにRactor.selectする

RactorはsendされたメッセージをRactor.recvで受け取ります。 Ractor.selectでも受け取れます。 例えば、次の例です。

r = Ractor.new do
  # message = Ractor.recv と同じ
  _, message = Ractor.select self
  p "Hello #{message}!"
end

r.send 'World'
r.take

実行するとsendされた'World'を結合して、Hello World!を出力します。

Ractor.selectでsendされるメッセージを待てると何がうれしいのでしょうか?

別のRactorをtakeしているときに、自身にsendされるメッセージと別のRactorからのtakeを同時に待てます。 例えば、次の例です。

r1 = Ractor.new do
  Ractor.yield 'r1がyieldしたメッセージ'
  'r1がreturnしたメッセージ'
end

r2 = Ractor.new(r1) do |r1|
  loop do
    # takeとrecvを同時に待つ
    _, message = Ractor.select self, r1
    p message
  end
end

r2.send 'r2にsendしたメッセージ1'
r2.send 'r2にsendしたメッセージ2'

r2.take

実行すると次のように表示されます。

~ ruby select_take_and_recv.rb
"r2にsendしたメッセージ1"
"r2にsendしたメッセージ2"
"r1がyieldしたメッセージ"
"r1がreturnしたメッセージ"

歌うRactor

Ractorで歌ってみましょう。

歌う部分はRactorは関係ありません。出落ちです。 Ractor内で、spawnしたプロセスをメインスレッドでkillできるか確認します。

非同期なspawn関数をRactor内で実行する必要があるのかは知りません。

完成形

LYRICS =<<FIN
ちょうちょう ちょうちょう 菜の葉にとまれ
菜の葉にあいたら 桜にとまれ
桜の花の 花から花へ
とまれよ遊べ 遊べよとまれ
FIN
.gsub("\n", ' ')
.freeze

pid = Ractor.new { spawn "say #{LYRICS}" }.take

sleep 10

Process.kill 9, pid

pidは数値なので、Ractorと共有可能です。

共有可能なオブジェクト

文字列は共有可能なオブジェクトではありません。 Ractorから参照するとNameErrorが起きます。 freezeして共有可能なオブジェクトにします。

Ractor間のメッセージ送受信 Pull型とPush型

やることは前回と同じです。Ractorの構成を組み替えてみました。

Pull型とPush型

Ractorのメッセージ送受信にはPull型とPush型があります。 前回、メインスレッド(?)でRactorインスタンスをselectしてイベントを待ちました。Pull型です。

f:id:ledsun:20200422213039p:plain
Pull型

今回、描画用のRactorインスタンスrendererを用意して、他のRactorインスタンスからイベントをsendします。Push型です。

f:id:ledsun:20200422213126p:plain
Push型

完成形

require 'io/console'

# 結果を描画するRactor。inputとclockの存在を知りません。
renderer = Ractor.new do
  val = 0
  loop do
    # メッセージを待ちます
    msg = Ractor.recv

    # メッセージがユーザー入力だったら値をリセットします。
    val = 0 if msg == :reset

    # カウントアップします。
    val += 1
    # 行をクリア
    print "\e[2K"
    # 行頭へ移動
    print "\e[0G"
    # 出力
    print val
  end
end

# ユーザー入力を待つRactor。引数でrendererを受け取ります。
input = Ractor.new renderer do |renderer|
  # moveされたSTDIOを使って文字入力を待つ
  io = Ractor.recv
  while "\C-c" != io.getch
    # メッセージを送ります
    renderer << :reset
  end
end

# クロックイベントを発生するRactor。引数でrendererを受け取ります。
Ractor.new renderer do |renderer|
  loop do
    # メッセージを送ります
    renderer << nil
    sleep 0.3
  end
end

# 共有不可能オブジェクトSTDINをRactorにmoveする
input.send STDIN, move: true

# Ractorの終了を待つ?
Ractor.recv

はまったところ

Ractor.recvを書かずに実行したら、何も出力されずにプログラムが終了しました。 Ractorインスタンスが動き出す前に、メインスレッド(?)が終了したようです。

感想

Pull型とPush型では、Ractor間の依存関係が逆転しました。 この規模では、ふーんて感じです。

Ractorを使ったTUIプログラムの一歩

前回のユーザー入力とクロックを両方待つプログラムを少し改造します。

  • Enter以外の入力を受け取る
  • カーソルを移動せずに書き換える

後者はRactorは関係ないです。

Enter以外の入力を受け取る

任意の1文字を受け取るためにSTDIN.getchを使いたいです。 STDINは共有不可能オブジェクトです。 次のようなRactorを書くとcan not access non-sharable objects in constant STDIN by non-main Ractors (NameError)が発生します。

# ユーザー入力を待つRactor
input = Ractor.new do
  while  "\C-c" != STDIN.getch
    Ractor.yield :reset
  end
end

共有不可能オブジェクトをRactorに渡すときはmoveします。

# ユーザー入力を待つRactor
input = Ractor.new do
  io = Ractor.recv
  while  "\C-c" != io.getch
    Ractor.yield :reset
  end
end

input.send STDIN, move: true

カーソルを移動せずに書き換える

エスケープシーケンスを使ってカーソルを操作します。

  # 行をクリア
  print "\e[2K"
  # 行頭へ移動
  print "\e[0G"
  # 出力
  print val

完成形

require 'io/console'

# ユーザー入力を待つRactor
input = Ractor.new do
  # moveされたSTDIOを使って文字入力を待つ
  io = Ractor.recv
  while "\C-c" != io.getch
    Ractor.yield :reset
  end
end

# クロックイベントを発生するRactor
clock = Ractor.new do
  loop do
    Ractor.yield nil
    sleep 0.3
  end
end

# 共有不可能オブジェクトSTDINをRactorにmoveする
input.send STDIN, move: true

# 初期値
val = 0
loop do
  # ユーザー入力とクロックを両方待ちます。
  _, flag = Ractor.select input, clock

  # イベントがユーザー入力だったら値をリセットします。
  val = 0 if flag == :reset

  # カウントアップします。
  val += 1
  # 行をクリア
  print "\e[2K"
  # 行頭へ移動
  print "\e[0G"
  # 出力
  print val
end

次のように動きます。一定のペースでカウントアップし、何か入力すると1に戻ります。

f:id:ledsun:20200421225620g:plain
プログラム実行時のスクリーンショット

参考

Ractorを試す 改訂版

macOShttps://github.com/ko1/ruby/tree/ractorソースコードをビルドする方法を改善しました。

インストール先

コンパイルしたRubyをmake installしたら「PCの環境が壊れるかも」とビビっていたところ、 id:hanachin さんに ./configure --prefix="$HOME/.rbenv/versions/ractor" するとrbenvの1実行環境として扱えると教えてもらいました。

OpenSSL

makeのログを見てたら、次のようなエラーも出ていました。

*** Following extensions are not compiled:
openssl:
    Could not be configured. It will not be installed.
    /Users/shigerunakajima/ractor/ext/openssl/extconf.rb:99: OpenSSL library could not be found. You might want to use --with-openssl-dir=<dir> option to specify the prefix where OpenSSL is installed.
    Check ext/openssl/mkmf.log for more details.
*** Fix the problems, then remove these directories and try again if you want.

--with-openssl-dir=/usr/local/opt/openssl@1.1/オプションをつけて、HomebrewでインストールしたOpenSSLのディレクトリを指定します。

完成形

CFLAGS=-Wno-error=shorten-64-to-32 ./configure --prefix="$HOME/.rbenv/versions/ractor" --with-openssl-dir=/usr/local/opt/openssl@1.1/
make
make install

まだ、次のようなエラーが表示されます。

installing bundled gems:            /Users/shigerunakajima/.rbenv/versions/ractor/lib/ruby/gems/2.8.0
Traceback (most recent call last):
    6: from ./tool/rbinstall.rb:957:in `<main>'
    5: from ./tool/rbinstall.rb:957:in `each'
    4: from ./tool/rbinstall.rb:960:in `block in <main>'
    3: from ./tool/rbinstall.rb:889:in `block in <main>'
    2: from ./tool/rbinstall.rb:889:in `foreach'
    1: from ./tool/rbinstall.rb:894:in `block (2 levels) in <main>'
./tool/rbinstall.rb:818:in `load_gemspec': [/Users/shigerunakajima/ractor/.bundle/gems/minitest-5.14.0/minitest.gemspec] isn't a Gem::Specification (NilClass instead). (TypeError)
make: *** [do-install-all] Error 1

Rubyコマンドのインストールはできているみたいです。

~ ls ~/.rbenv/versions/ractor/bin/ruby
/Users/shigerunakajima/.rbenv/versions/ractor/bin/ruby*

プログラムの実行だけなら、minitestはなくてもよさそうなので、先に進みます。

動作確認

システムのRubyではRactorを使ったRubyスクリプトの実行に失敗します。

~ ruby --version
ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-darwin18]
~ ruby sum_1_to_10.rb
Traceback (most recent call last):
sum_1_to_10.rb:2:in `<main>': uninitialized constant Ractor (NameError)

ractor版を使うと実行できました。

~ rbenv shell ractor
~ ruby --version
ruby 2.8.0dev (2020-04-08T16:31:17Z ractor 2658fc668d) [x86_64-darwin18]
~ ruby sum_1_to_10.rb
55

Ractorをつかうプログラムの練習 2つのイベントを待つ

Ractorをつかって、ユーザー入力とクロックを両方待つプログラムを書いてみます。 練習中であり、Ractorを使ったイケているソースコードではありません。

次のプログラムを実装します。 1秒毎にカウントアップします。 ユーザーがEnterキーを押したらカウンターをリセットします。

完成形

# ユーザー入力を待つRactor
input = Ractor.new do
  loop do
    gets
    Ractor.yield :reset
  end
end

# クロックイベントを発生するRactor
clock = Ractor.new do
  loop do
    Ractor.yield nil
    sleep 1
  end
end

# 初期値
val = 0
loop do
  # ユーザー入力とクロックを両方待ちます。
  _, flag = Ractor.select input, clock

  # イベントがユーザー入力だったら値をリセットします。
  val = 0 if flag == :reset

  # カウントアップします。
  val += 1
  p val
end

失敗作

処女作

ユーザー入力があったときだけ、カウンターから帰ってくる値を0に戻さそうと思って書きました。

input = Ractor.new do
  loop do
    gets
    Ractor.yield 0
  end
end

counter = Ractor.new do
  loop do
    Ractor.yield Ractor.recv + 1
    sleep 1
  end
end

counter << 0
loop do
  _, v = Ractor.select input, counter
  p v
  counter << v
end

実際はこんな感じにうごきます。

~ docker run --rm -it -v (pwd):/ractor wakaba260/ruby-ractor-dev ruby ractor/input_and_timer.rb
1
2

0
3
1
4

リセットされた値とされていない値が両方出力されます。 Ractorはキューを持っているので、counter << vした値は上書きされることなく保存されています。

debounceを実装

最新の値だけがほしいので、debounceが実装できれば良さそうです。

input = Ractor.new do
  loop do
    gets
    Ractor.yield 0
  end
end

counter = Ractor.new do
  v = 0
  loop do

    v = Ractor.recv
    Ractor.yield v + 1
    sleep 1
  end
end

debounce = Ractor.new do
  prev = Time.now.to_i
  loop do
    v = Ractor.recv

    now = Time.now.to_i
    if now - prev > 0.1
      p v
      prev = now
    end
  end
end

counter << 0
debounce << 0
loop do
  _, v = Ractor.select input, counter
  counter << v
  debounce << v
end

実際には、これも期待通りには動きません。 毎回counter << vしているので、counterのキューに全部の値が貯まります。

どうやらcounterで1秒に1加算しているのがよくなさそうです。 counterを1秒毎にイベントを発火するclockにし、mainで加算しました。

感想

Ractorインスタンスからグローバルな値を変更できないので、Ractor.yieldを使って値を親に返す必要があります。 その代わり、レールに乗りさせすれば、Ractor.selectを使って、イベントの待ち合わせを簡単に書けます。

スレッドで書くと次のようになります。

queue = Queue.new

input = Thread.new do
  loop do
    gets
    queue << :reset
  end
end

clock = Thread.new do
  loop do
    queue << nil
    sleep 1
  end
end

val = 0
loop do
  flag = queue.pop
  val = 0 if flag == :reset
  val += 1
  p val
end

今回の使い方からは「最初からキューを持っているスレッド」というイメージを持ちました。

Ractorを使ってプログラムを書く練習    

注意:Ractorならではとか、計算効率の良さとか考えていません。

1から10までを足す

# 最終結果送り先のRactor
CR = Ractor.current
r = Ractor.new { CR << Ractor.recv }

1.upto(10) do |i|
  # 数を足すRactorを作ってポインタを置き換える
  r = Ractor.new(r, i) do |next_r, i|
    # 担当の整数を足して次のRactorに送る
    next_r << Ractor.recv + i
  end
end

r << 0 # 計算開始。初期値を送る
p Ractor.recv # 結果を出力

<<でRactorインスタンスに値を送ります。 Ractor.recvで値を受け取ります。 並列に動いていません。

10個のRactorを並列に動かす

# 10個のRactorを作る
ractor_list = (1..10).map do |i|
  Ractor.new i do |i|
    i
  end
end

# 10個のRactorの終わりを待つ
until ractor_list.empty?
  # いずれかのRactorの結果を待つ
  r, val = Ractor.select(*ractor_list)
  p val

  # 実行の終わったRactorは、もう待たない
  ractor_list.delete r
end

Ractor.selectでRactorインスタンスの終了を待ちます。 Ractor.newに渡したブロックの戻り値が得られます。

参考

https://github.com/ko1/ruby/blob/ractor/ractor.ja.md

Ractorを試す

Ruby3 さみっと online - connpass を見ていました。 Ractorの発表を見て使ってみたくなったので環境を整えてみます。

ビルドする

https://github.com/ko1/ruby/tree/ractorソースコードをビルドします。

git clone git@github.com:ko1/ruby.git ractor
cd ractor
git checkout ractor
autconf
./configure
make
ractor.c:1105:13: error: implicit conversion loses integer precision: 'VALUE' (aka 'unsigned long') to 'uint32_t' (aka 'unsigned int') [-Werror,-Wshorten-64-to-32]
    r->id = ractor_next_id();

が出ます。

Mac で ruby-1.9.3-p385 がビルドできない問題を素早く解決する - Qiita を参考にして環境変数CFLAGS=-Wno-error=shorten-64-to-32を指定します。

CFLAGS=-Wno-error=shorten-64-to-32 ./configure
make

コンパイルは成功しました。 rubyコマンドを実行するとrubygems.rbが見つからずにエラーになります。

~ ./ruby
Traceback (most recent call last):
    1: from <internal:gem_prelude>:one:in `<internal:gem_prelude>'
<internal:gem_prelude>:one:in `require': cannot load such file -- rubygems.rb (LoadError)

make installも実行する必要があるようです。 PCの環境が壊れるかもと考えると億劫です。

Dockerイメージを使う

id:wakaba260yen さんがDockerイメージを用意していてくれました。 使ってみます。

~ docker run --rm  wakaba260/ruby-ractor-dev ruby -e 'r = Ractor.new(42) { p _1 }; r.take'
42

たとえば ring.rb を実行するには次のように実行します。

~ docker run --rm -v (pwd):/ractor wakaba260/ruby-ractor-dev ruby ractor/ring.rb
:setup_ok
1
:fin

参考資料

http://atdot.net/~ko1/activities/2020_ruby3summit.pdf

ruby/ractor.ja.md at ractor · ko1/ruby · GitHub

自宅作業環境の整備

新型コロナウイルスの影響で在宅勤務しています。 3月3日から、少しずつ作業環境を整備しています。

ノイズキャンセリングヘッドホン

オフィス作業時代に、作業集中用に使っていたものをそのまま使っています。

オンラインミーティングしている相手の声が聞き取りにくい時や、自宅内の騒音が大きいときに使っています。 騒音の音が自分には聞こえなくなるのですが、オンラインミーティングの相手には流れているかもしれないのが、気になる点です。

www.sony.jp

モバイルディスプレイ 15,000円

作業に使っているノートPCに外付けディスプレイが欲しかったので買いました。

決め手は値段です。 自宅作業を始めた初期に買ったので、なるべく安いものを選びました。 今考えると、もう少し大きな据置ディスプレイを買ってもよかったかもしれません。

モバイルディスプレイの利点は作業終了後に片付けやすい点です。

同梱のUSBケーブルが2, 3日で壊れたので、追加でUSBケーブルを買いました。

肘置き 4,500円

自宅作業だと右の肩と背中が痛くなるので、対策で買いました。 使ってみたら、机と同じ高さに固定なのは予想より気になります。 1,000円ちょっと追加して、高さが調整できるタイプにすればよかったです。

ないよりはマシですが、これだけで、肩、背中の疲労がなくなることはないです。 予算ができたら、机と椅子を整備しようと思います。

USBマイク 5,000円

PC搭載のマイクだとキーボードのタッチ音を拾いやすいのと、自宅内の雑音も拾ってしまいます。 ヘッドホンにもマイクがついていたのですがBluetooth接続のノイズが乗るので微妙でした。

外付けの単一指向性マイクを買いました。 比較的安くて、アームやポップガードまでついているので、最初の1個目として良さそうと思い選びました。

どれぐらいの効果があるかは、自分が聞く側でないので、わかりません。 録音して試した感じでは、PC搭載のマイクよりはマシだと思います。 ヘッドセットのマイクだと飲み物を飲む音も拾うので、ヘッドセットより外部マイクの方がいいのかなあ?と思っています。

コンデンサマイクよりダイナミックマイクの方が単一指向性が強いらしいので、ダイナミックマイクの方が良いのかもしれません。 よくわかりません。

天板 2,000円

天板の広さ78x29cm、高さ80cm本棚の上で作業していました。 もう少し広い作業スペースがほしかったので、アイリスオーヤマの化粧板を買いました。 本棚の上に載せて天板にしています。

送料込みで2,000円、安い!

item.rakuten.co.jp

新型コロナウイルスの患者数を予測する

東京都 新型コロナウイルス陽性患者発表詳細 - データセット - 東京都オープンデータカタログサイト から患者の情報を取ってきます。 グーグルスプレッドシートをつかってグラフを書きます。

f:id:ledsun:20200329151852p:plain
新型コロナウイルスの患者数推移

24〜28日がほぼ直線になっていることが読み取れます。 赤線の部分です。 この部分だけをグラフにします。

f:id:ledsun:20200329152050p:plain
 3/24-3/28の傾向から予測する患者数

このまま線形に推移すると仮定してトレンドラインを引きます。赤線の部分です。

  • 4/1には500人
  • 4/11には1000人
  • 5月には2000人

と、予測できます。

グラフ作成に使ったスプレッドシートです。 20200329の患者数推移予測 - Google スプレッドシート

都内の感染症指定医療機関で何が起こっているのか(忽那賢志) - 個人 - Yahoo!ニュース

無症状の患者、すでに改善した患者、軽症の患者については隔離を行わずに自宅療養とすべきと考えます。

すべての感染者を隔離し、封じ込めを狙うフェーズはすでに過ぎており、今は中等症〜重症例の医療を必要とする患者を医療機関で診療すべき段階に来ています。

は、本当にそうなのだろうと思います。 つまり、

  1. 感染者を隔離できない
  2. 感染者増加ペースはおさまらない
  3. 今後もいまの外出自粛が続く

のでしょう。気長に頑張りたいと思います。

新型コロナウイルスの患者数増加ペースが指数関数的増加を少し下回る

3月27日が40人増加で、指数関数的増加を少し下回っています。 指数関数的増加をしていたら累計で400人台前半、28日には400人を超えている見込みでした。 少し希望が見えて来ました。 なるべく家族以外との接触を控えて、増加ペースの鈍化に協力していきたいところです。

f:id:ledsun:20200328075259p:plain
東京都の新型コロナウイルス「COVID-19」感染者数推移

都内の最新感染動向 | 東京都 新型コロナウイルス感染症対策サイトより

日テレのグラフはちょっと過激に感じすぎる気がしたので、変えました。