うんこな電子メール(2)

Hello ****@

My nickname in darknet is sheppard42.
I'll begin by saying that I hacked this mailbox (please look on 'from' in your header) more than six months ago,
through it I infected your operating system with a virus (trojan) created by me and have been monitoring you for a long time.

Even if you changed the password after that - it does not matter, my virus intercepted all the caching data on your computer
and automatically saved access for me.

I have access to all your accounts, social networks, email, browsing history.
Accordingly, I have the data of all your contacts, files from your computer, photos and videos.

I was most struck by the intimate content sites that you occasionally visit.
You have a very wild imagination, I tell you!

During your pastime and entertainment there, I took screenshot through the camera of your device, synchronizing with what you are watching.
Oh my god! You are so funny and excited!

I think that you do not want all your contacts to get these files, right?
If you are of the same opinion, then I think that $500 is quite a fair price to destroy the dirt I created.

Send the above amount on my bitcoin wallet: 1MN7A7QqQaAVoxV4zdjdrnEHXmjhzcQ4Bq
As soon as the above amount is received, I guarantee that the data will be deleted, I do not need it.

Otherwise, these files and history of visiting sites will get all your contacts from your device.
Also, I'll send to everyone your contact access to your email and access logs, I have carefully saved it!

Since reading this letter you have 48 hours!
After your reading this message, I'll receive an automatic notification that you have seen the letter.

I hope I taught you a good lesson.
Do not be so nonchalant, please visit only to proven resources, and don't enter your passwords anywhere!
Good luck!

最近はビットコインを使って支払わせようとするわけだな。前のは $550 だったが今回は $500 で少し安くなっている(笑)。それから前のは「あなたがエロサイトを見ている姿をウェブカムで撮影したから、金を払わないと家族や友人に送りつけますよ」(笑)みたいな文面だったが、こんどのは「『トロイの木馬』で PC をハックした」とあって、PC のデータを「人質」に取っているということなのだろうな。ここでもウェブカムでお前の映像を撮影したとか、お前が密かに訪れているサイト(エロサイトをほのめかしているのだろう)にはびっくりした(笑笑)とかもあるが。アホなやつだ。

しかし、ビットコインのアカウント(?)が晒してあるのだが、これで足がつかないようになっているのだろうな。自分にはよくわからないけれど。

前見たビットコインのサイトでは、メールを真に受けて支払っている人が少なからず存在した。大した額ではないとはいえ、これだけのことで労せずして実際に数十万円でも手に入るなら楽な商売である。これからもこういうことをする奴は絶えないだろう。足がつきにくいとなればなおさらである。

「コマ大数学科」を Ruby で

izu-mix.comプログラミングで解けそうな問題だけ解いています。
 

#001 フィボナッチ数列

問題:
15段の階段を上るとき、1段上るか、2段上るか、2通りの方法を組み合わせて登ると、何通りの登り方があるか。

コード。

def fib(n)
  return 1 if n == 1
  return 2 if n == 2
  fib(n - 1) + fib(n - 2)
end

puts fib(15)    #=>987

 

#042 ミッシングナンバー

問題:
ホテルの部屋の扉に4と9を使わないで1号室から番号をつけていくと500番目の部屋は何号室か?

コード。

def room(goal)
  room_number = 1
  n = 0
  loop do
    n += 1 if room_number.to_s.scan(/[49]/).empty?
    return room_number if n == goal
    room_number += 1
  end
end

puts room(500)    #=>875

 

#117 バスケットボール

問題:
バスケットボールの得点は、フリースローが1点、スリーポイントラインの内側からだと2点、外側からだと3点入ります。ちょうど得点が10点になるまでの得点経過は何通りあるでしょうか?

コード。

def point(n)
  case n
  when 1 then 1
  when 2 then 2
  when 3 then 4
  else
    point(n - 1) + point(n - 2) + point(n - 3)
  end
end

puts point(10)    #=>274

考え方は「#001 フィボナッチ数列」の場合と同じ。
 

#029 カタラン数

問題:
10人が円卓に座って1人ずつ握手をするとき、全員の手が交差しないように握手する仕方は全部で何通りあるか?

コード。

def count(n)
  case
  when n.odd?  then 0
  when n.zero? then 1
  else
    result = 0
    2.step(n, 2) {|i| result += count(i - 2) * count(n - i)}
    result
  end
end

puts count(10)    #=>42

これだと n = 30 くらいが限界なので、メモ化するとよい。メモ化すると例えば n = 200 くらいでも一瞬。ちなみに count(200) = 896519947090131496687170070074100632420837521538745909320 である。
 

#020 ラマヌジャン

問題:
 10軒以上、50軒以内の家が並んでおり、1から順に番号がつけてある。
 左右の端から順番に番号を足していったとき(すなわち、1から順に足していくのと、大きい方の数字から順に足していくとき)、合計がちょうど同じになる家があったとき、その家の番号を答えよ。

コード。

def find(n)
  (1..n).each do |i|
    return [n, i] if (1..i).inject(:+) == (i..n).inject(:+)
  end
  nil
end

answer = (10..50).map {|i| find(i)}.compact
p answer    #=>[[49, 35]]

つまり、49軒あったときの35番目の家。
 

#012 1000

問題:
 1~1000の数字が振られている1000個の電球がある。すべてOFFの状態から始めて、1回目の操作で1の倍数の電球のスイッチのON/OFFを切り替え、2回目の操作では2の倍数の電球のON/OFFを切り替える。
 このように、n回目の操作でnの倍数の電球のON/OFFを切り替える操作を、1000回目までおこなったとき、最後にONの状態の電球の数は何個か。

コード。

N = 1000
table = Array.new(N + 1, false)
(1..N).each {|n| n.step(N, n) {|i| table[i] = !table[i]} }
puts table.count(true)    #=>31

素直すぎるくらい素直なコードですね。
 

#196 和と積

問題:
足し合わせても掛け合わせても同じ数になる5つの正の整数と、その数を答えなさい。

5つの数を a, b, c, d, e とおくとき、a ≦ b ≦ c ≦ d ≦ e としてよいのでそうすると、明らかに 1 ≦ e ≦ 5 である(a + b + c + d + e ≦ 5e より 5 ≦ abcd)。よって超素直にコードを書くと

N = 5
(1..N).each do |e|
  (1..e).each do |d|
    (1..d).each do |c|
      (1..c).each do |b|
        (1..b).each do |a|
          sum = a + b + c + d + e
          p [[a, b, c, d, e], sum] if sum == a * b * c * d * e
        end
      end
    end
  end
end

結果は

[[1, 1, 2, 2, 2], 8]
[[1, 1, 1, 3, 3], 9]
[[1, 1, 1, 2, 5], 10]

となる。さすがにコードが素朴すぎるという向きもあるかも知れないので、ちょっと工夫して

N = 5
def loop(last, args)
  if args.size == N
    sum = args.inject(&:+)
    p [args, sum] if sum == args.inject(&:*)
  else
    (1..last).each {|i| loop(i, [i] + args)}
  end
end

loop(N, [])

とすると高級に見えるかも知れない。確かにこちらだといくつの整数でもできるという利点がある。例えば N = 13 とするとこんな結果

[[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3], 18]
[[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 5], 20]
[[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 7], 21]
[[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 13], 26]

になる。ただしこれだとこの N = 13 くらいが限度なので、さらにこうしてみる。

N = 100
def loop(last, args)
  product = args.inject(&:*) || 0
  return if product > N ** 2    #枝刈り
  if args.size == N
    p [args, product] if product == args.inject(&:+)
  else
    (1..last).each {|i| loop(i, [i] + args)}
  end
end

loop(N, [])

途中で「枝刈り」するのである。これだったら N = 100 でも計算できる。

でもこれ、N個だけ数があるとき、最大のそれが N 以下ってちょっと微妙かな。証明できるか?
 

#038 モンモール問題

問題:
6人でプレゼント交換するとき、6人とも自分のプレゼントをもらわない組み合わせは全部で何通りあるか求めよ。

コード。

N = 6
co = 0
[*0...N].permutation do |presents|
  catch(:jump) do
    presents.each_index {|i| throw(:jump) if presents[i] == i}
    co += 1
  end
end
puts co    #=>265

プログラミングで解くと特にむずかしくもないが、紙と鉛筆で解くのはかなりむずかしい。上のリンク先も参照されたい。



ここからは
「コマ大数学科」に挑む
このサイトから問題を拝借いたします(ありがとうございます!)。でもこれ「Yahoo!ジオシティーズ」のホームページなのですが、サービス終了で消えちゃうのでしょうか。もったいないなあ。。。
 

2007年10月3日放送 第3問

問題:
直径ABの円とAの地点に点Pがある。直径(線分)ABが円に沿って等速で一回転する間に点PもAからBへ等速で移動する。このときの点Pの軌跡を書きなさい。

http://www.geocities.jp/tfujisaki2006/sugaku/komadai0710.html

お絵かきですね。これは自作の Gem 'oekaki' を使ってアニメーションしてみましょう。
20181018171548
黄色い点が点A、緑の点が点P です。

コード。

require 'oekaki'

class Vector
  def scrn() [150 + self[0], 150 - self[1]] end
end

R = 130         #円の半径
Steps = 200.0   #全ステップ数
agl_step = 2 * PI / Steps
rad_step = R * 2 / Steps

pa = ->(n) {Vector[cos(agl_step * n), sin(agl_step * n)] * R}
pb = ->(n) {Vector[cos(PI + agl_step * n), sin(PI + agl_step * n)] * R}
pp = ->(n) {
  a, b = pa.(n), pb.(n)
  a + (b - a) / (2 * R) * rad_step * n
}

Oekaki.app do
  white  = color(0xffff, 0xffff, 0xffff)
  yellow = color(0xffff, 0xffff, 0)
  green  = color(0, 0x8000, 0)
  n = 0
  
  draw do
    clear(color(0xffff, 0xe709, 0xd65c))
    circle(false, 150, 150, R, white)
  end
  
  id = timer(50) do
    circle(true, *pa.(n).scrn, 3, yellow)
    circle(true, *pp.(n).scrn, 3,  green)
    n += 1
    timer_stop(id) if n > Steps
    true
  end
end

関数 pa.(n), pb.(n), pp.(n) はそれぞれ n ステップ目の点A, B, P の位置を返します。
 
Gem 'cairo' を使って GIFアニメにしてみました。こちらの方がきれいかな。
20181018215706
画像生成のためのコードはこちら
 

順列組み合わせ(2007/11/6)

問題:
1枚500円のチケットを買うために、500円しか持っていない人6人と1000円札しか持っていない人6人、合わせて12人が並んでいます。しかし、いまチケット売り場にはつり銭がありません。

つり銭に不足が無いようにチケットを販売できる12人の並び方は全部で何通りあるでしょうか?
ただし、各6人の並ぶ順序は考えなくてもよい。

http://www.geocities.jp/tfujisaki2006/sugaku/komadai0711.html

コード。

N = 12
co = 0

def check(visitor500)
  stock = 0
  N.times do |i|
    if visitor500.include?(i)
      stock += 1
    else
      stock -= 1
      return false if stock < 0
    end
  end
  true
end

[*0...N].combination(6).each do |vst|
  co += 1 if check(vst)
end
puts co    #=>132

考え方としては、まず 12人の中から 500円しかもっていない 6人の位置を選んで vst に入れます。そして、check(vst) でその並び方でチケットが全員に売れるかチェックしています。メソッド内の変数 stock は売り場にある 500円玉の数です。
この方法はいわゆる brute-force(総当り)ですね。リンク先ではもっと上手いやり方で考えています。
 

サイコロ(2007/12/19)


問題:
図のような三角形が並んだ線の上にサイコロの出た目に従って動くことにする。三回サイコロを振ったとき中央の赤い丸に戻る確率を求めよ。
図はリンク先から拝借しました(ありがとうございます!)。さて、コード。

table = [[0, 1], [1, 0], [1, -1], [0, -1], [-1, 0], [-1, 1]]
co = 0

[*0...6].repeated_permutation(3).each do |pips|
  x, y = 0, 0
  pips.each do |pip|
    x += table[pip][0]
    y += table[pip][1]
  end
  co += 1 if x.zero? and y.zero?
end
puts Rational(co, 6 ** 3)    #=>1/18

変数 table には、出たサイコロの目に対応する移動方向を定義しています。変数 pips に 3回サイコロを投げて出た目が入ります。6 ** 3 というのは、サイコロの出方の総数です。つまり、サイコロの目の 6とおりから 3つを重複を許して選ぶ順列の数です。
 

ハノイの塔」の二重版(2007/12/28)

問題:
A, B, C の三本の棒があって、中央に穴の空いた大きさのちがう円盤を挿すことができます。異なる大きさの 5枚の円盤が、Aの棒に必ず下の円盤の方が大きくなるように挿してあり、まったく同じものが Cの棒にも挿さっています。
さて、ここで 1回につき 1度円盤を動かせるとして、ここでも大きい円盤は必ず小さい円盤の下になるように動かすとき、すべてを Bの棒に移すには最低何回円盤を動かさねばならないでしょうか。なお、(2枚ずつある)同じ大きさの円盤はどちらが上下でもかまいません。

http://www.geocities.jp/tfujisaki2006/sugaku/komadai0712.html

コード。

def hanoi1(n, from1, to, from2)
  if n == 1
    @procedure << "#{from1}#{to}"
    @procedure << "#{from2}#{to}"
  else
    hanoi2(n - 1, from1, to, from2)
    @procedure << "#{from1}#{to}"
    hanoi(n - 1, from2, from1, to)
    @procedure << "#{from2}#{to}"
    hanoi(n - 1, from1, to, from2)
  end
end

def hanoi2(n, from, via, to)
  if n == 1
    @procedure << "#{from}#{to}"
  else
    hanoi1(n - 1, from, via, to)
    @procedure << "#{from}#{to}"
    hanoi(n - 1, via, to, from)
  end
end

def hanoi(n, from, to, via)
  if n == 1
    @procedure << "#{from}#{to}"
    @procedure << "#{from}#{to}"
  else
    hanoi(n - 1, from, via, to)
    hanoi(1, from, to, via)
    hanoi(n - 1, via, to, from)
  end
end

@procedure = []
hanoi1(5, :A, :B, :C)
puts @procedure.size    #=>96

これはかなり複雑です。元記事を参照してみて下さい。

linuxBean のインストール

20181014012027
 
linuxBean は日本で開発されているディストリビューションでしたが、いまは開発が止まっているようです。しかし(開発者とは別の)奇特な方が Ubuntu 16.04 対応版を配布しておられるので、いまでも一応使うことができます。ダウンロードは以下からできます。
http://simosnet.com/livecd/linuxbean/
最新版は linuxbean-16.04-20180131.iso のようなので、これをダウンロードしました。

インストールは他の Debian 系のディストリビューションとほとんど同じです。ただし、「ファイルのコピーが完全に終わるまでは、最後の [続ける] のボタンを押すな」というようなメッセージがでます。どういうことなのかこれだけではよくわかりませんが、タイムゾーンの設定のあとにユーザー名などを登録する画面があって、そこの [続ける] のボタンのことのようです。自分は最初ふつうにこのボタンを押したところ、インストールがクラッシュしました。なので、もう一度やり直してその画面に来たときは、デバイスへのアクセスが完全に終了するまでは [続ける] を押さずにしてやってみたところ、うまくインストールされました。ここはちょっとわかりにくいので注意です。


インストールが終了して再起動したら、まずはネット接続します。これは他の Ubuntu 系の設定と同じで、Wi-Fi のパスワードを入れるだけです。
事前に追加インストールするソフトなどを訊いてくるので、選択して実行します。自分は日本語入力は fcitx-mozc、ブラウザは chromium を追加インストールしました。その他は適当に選択します。終了後はログアウト→ログイン。
日本語入力を fcitx-mozc に設定します。
ああそうだ、もちろん
$ sudo apt update
$ sudo apt upgrade
は忘れずに。

ホームフォルダを英語化します。
$ env LANGUAGE=C LC_MESSAGES=C xdg-user-dirs-gtk-update


Ruby をインストールする。
Linux Mint に rbenv で Ruby を入れる - Camera Obscura

システムモニターをインストール。
$ sudo apt-get install gnome-system-monitor

Haskell 覚え書き

  • 等しくない 3 /= 5
  • 内包表記
ghci> [x * y | x <- [2,5,10], y <- [8, 10, 11], x * y > 50]
[55,80,100,110]

 

  • asパターン
firstLetter :: String -> String
firstLetter "" = "Empty String."
firstLetter all@(x:xs) = "The first letter of " ++ all ++ " is " ++ [x]

 

  • let 式
ghci> let (a, b, c) = (1, 2, 3); d = 4 in (a + b + c) * d
24

 

ghci> map (\x -> x + 3) [1,6,3,2]
[4,9,6,5]

Ruby の callcc(継続)

Ruby 2.2 から非推奨になってしまった callcc(継続)ですが…。

callcc は goto みたいなものです。

callcc {|cnt| ..}

とあるとき、継続 cnt が call されると、callcc {|cnt| ..} の「後へ」処理が移ります。メソッド call に引数があった場合は、それが callcc {|cnt| ..} の返り値になります。cnt は状態を保存していると同時に、ラベルみたいなものですね。なので、一度 callcc {} が出現しないともちろんそこへジャンプすることはできません。

なので、無限ループは簡単です。

c = nil
callcc {|cnt| c = cnt}
c.call

これで無限ループします。

ループから抜けます。

callcc do |cnt|
  n = 0
  loop do
    puts n
    n += 1
    cnt.call if n > 10
  end
end

 
メソッドからの脱出。

def a(cnt)
  puts "inner 1"
  cnt.call("jump")
  puts "inner 2"
end

puts callcc {|cnt| a(cnt)}

結果。

inner 1
jump

 
メソッド再帰からの脱出もできます。

def a(n, cnt)
  cnt.call(n) if n > 9
  a(n + 1, cnt)
end

puts callcc {|cnt| a(0, cnt)}    #=>10

Python のリスト内包表記と Ruby の map

内包表記は Ruby は map でいけるんじゃね?という話。

pythonの内包表記を少し詳しく
の例を使ってみる。

例えば Python

[i for i in range(10)]

というのは

(0...10).map(&:itself)

とか。まあこれは Array.new(10, &:itself) とか 10.times.to_a とかもっとかんたんに [*0...10] でいいのだけれど。

また、

[i for i in range(10) if i%2==0]

だと select をそのまま使って

(0...10).select {|i| (i % 2).zero?}

ですね。これもしかし

0.step(9, 2).to_a

とした方がよい感じ。step() は Enumerator を返すのでこれができる。一般に、Enumerator を返すメソッドは配列の生成に便利。

Python

[i if i%2==0 else str(i) for i in range(10)]

だと、Ruby では三項演算子を使って

(0...10).map {|i| (i % 2).zero? ? i : i.to_s}

かな。


ここPython

[('fizzbuzz' if x % 15 == 0 else ('fizz' if x % 3 == 0 else ('buzz' if x % 5 == 0 else x))) for x in range(1, 30)]

というのがあったけど、これは Ruby だと

(1...30).map {|i| if (i % 15).zero? then "fizzbuzz" elsif (i % 3).zero? then "fizz" elsif (i % 5).zero? then "buzz" else i end}

ですかねえ。でもこれは Python 文化ならともかく、Ruby ではあり得ない気がする。同じところの例で

[x for inner_list in [[1, 3], [5], [7, 9]] for x in inner_list]

が二重ループの例として挙げられているのは、Ruby だと flat_map を使って敢て二重ループで

[[1, 3], [5], [7, 9]].flat_map {|i| i.map(&:itself)}

となりますね。まあこれは例がよくないので、じつは

[[1, 3], [5], [7, 9]].flat_map(&:itself)

でいいし、さらには [[1, 3], [5], [7, 9]].flatten でおしまいだけれど。


こうして見ると、内包表記はすごく便利なのだけれど、Ruby でもまあまあ簡潔に書けることがわかりますね。可読性もそんなに悪くないと思う。内包表記で Ruby ではうまく書けないとか、あるのかな。



ここでおもしろいもの見つけた。Python

[f(x) for x in lst if f_cond(f(x))]

とすると f(x) が二回呼び出されるのが気に喰わないというもの。解決策は

[f_x for f_x in [f(x) for x in lst] if f_cond(f_x)]

としておられる。なるほど。可読性がいまいちかな。Ruby だと素直に

lst.map {|i| f(i)}.select {|i| f_cond(i)}

だな。

mp4 動画ファイルの gif化 / mp3 → wav

oplern.hatenablog.comここが参考になる。ffmpeg を使う。
 
例えば

$ ffmpeg -i a.mp4 -an -r 30 -s 300x328 -f gif a.gif

-r 30 はフレームレート、-s はリサイズ(いらないかも)など。
 

音声の変換

qiita.commp3 → wav はこんな感じ。

$ ffmpeg -i shot1.mp3 -vn -ac 2 - ar 44100 -acodec pcm_s16le -f wav shot.wav

'libva-drm.so.1' がないというエラー

Linux Mint 19 にして ffmprg を使おうとしたところ

ffmpeg: error while loading shared libraries: libva-drm.so.1: cannot open shared object file: No such file or directory

のエラーが出る。ぐぐってもよくわからない。たぶん Mint のアップグレードのせいではないかと思う。

Ruby/SDL でゲームパッドを使う

20181011133328
ゲームパッドで四角が動き回ります。Aボタンでビームの発射、Bボタンで一時停止、selectボタンで終了。

sdl_sample7.rb

require 'sdl'

Width, Height = 300, 300
SDL.init(SDL::INIT_VIDEO | SDL::INIT_JOYSTICK | SDL::INIT_AUDIO)
screen = SDL::Screen.open(Width, Height, 16, SDL::SWSURFACE)
SDL::WM::set_caption("SDL", "")

SDL::Mixer.open(22050, SDL::Mixer::DEFAULT_FORMAT, 2, 512)
wave = SDL::Mixer::Wave.load("shot.wav")
wave.set_volume(10)

L = 20
joy = SDL::Joystick.open(0)
black  = [0,   0, 0]
rcolor = [0, 255, 0]
x, y = (Width - L) / 2, (Height - L) / 2
dir = [1, 0]
f = true
beam = nil

# 正方形
rect = Fiber.new do
  loop do
    screen.fill_rect(x, y , L, L, rcolor)
    
    x += dir[0]
    y += dir[1]
    x = Width  - L if x < 0
    y = Height - L if y < 0
    x = 0 if x > Width  - L
    y = 0 if y > Height - L
    
    Fiber.yield
  end
end

# ビーム
class Beam
  LB = 40
  Step = 10
  def initialize(rx, ry, dir1, screen)
    @x1 = @x2 = rx
    @y1 = @y2 = ry
    @dir = dir1
    @s = screen
    @l = 0
  end
  attr_writer :color
  
  def draw
    @s.draw_line(@x1, @y1, @x2, @y2, @color)
  end
  
  def next
    @x1 += @dir[0] * Step
    @y1 += @dir[1] * Step
    @l += Step
    if @l > LB
      @x2 += @dir[0] * Step
      @y2 += @dir[1] * Step
      @l = LB
    end 
    
    @x2 < 0 or @x2 > Width or @y2 < 0 or @y2 > Height 
  end
end

generate = lambda do
  Fiber.new do
    beams = [[1, 0], [0, -1], [-1, 0], [0, 1]].map do |dir1|
      Beam.new(x + L / 2, y + L / 2, dir1, screen)
    end
    loop do
      color = [rand(256), rand(256), rand(256)]
      break if beams.map do |b|
        b.color = color
        b.draw
        b.next
      end.all?
      Fiber.yield(false)
    end
    true
  end
end


## mainloop
loop do
  while event = SDL::Event.poll
    case event
    when SDL::Event::Quit then exit
    end
  end
  
  screen.fill_rect(0, 0 , Width, Height, black)
  SDL::Joystick.update_all
  
  # 十字キーの処理
  jh, jv = joy.axis(0) / 32768.0, joy.axis(1) / 32768.0
  dir = [jh.round, jv.round] if jh.nonzero? or jv.nonzero?
  
  # Aボタンでビームの発射
  if joy.button(0) and f
    f = false
    beam = generate.call
    rcolor = [rand(256), rand(256), rand(256)]
    SDL::Mixer.play_channel(0, wave, 0)
  end
  f = beam.resume unless f
  
  # 正方形の処理
  rect.resume

  # Bボタンを押しているあいだ静止  
  SDL::Joystick.update_all while joy.button(1)

  # selectボタンで終了
  exit if joy.button(6)
  
  screen.update_rect(0, 0, 0, 0)
  sleep(0.01)
end

なるたけ Fiber を使ってみました。発射音の shot.wav ファイルが必要です(ここからダウンロードできます)。
音源はここから頂きました。ありがとうございます。

 
marginalia.hatenablog.com

Ruby の新しい演算子候補 / 標準添付ライブラリ 'thwait'

*>って Ruby の新しい演算子として使えることない? 右側代入とか。同様に +> とか /> とかも使えそう。
~> とかもいけそうだけれど、-> とまちがえやすそう。

関係ないけれど、早く Guild 使ってみたいな。笹田さん、がんばって下さい。



Thread の話。いくつかの Thread をまとめて待つのには、

threads = []
5.times do
  threads << Thread.new {sleep(rand(0.5..1.0))}
end

threads.each {|t| t.join}

みたいなやり方はうまくない。動かないことはないけれど、これでは待っている Thread は常に 1つしかなくて、すべてを同時に待っているのではない。これは標準添付ライブラリの 'thwait' というのを使うとうまくいく。

require 'thwait'

threads = []
5.times do
  threads << Thread.new {p Thread.current; sleep(rand(0.5..1.0))}
end

ThreadsWait.all_waits(*threads) {|t| p t}

これの結果。

#<Thread:0x00005633007e7b18@a6.rb:5 run>
#<Thread:0x00005633007e78e8@a6.rb:5 run>
#<Thread:0x00005633007e7a00@a6.rb:5 run>
#<Thread:0x00005633007e75f0@a6.rb:5 run>
#<Thread:0x00005633007e74d8@a6.rb:5 run>
#<Thread:0x00005633007e75f0@a6.rb:5 dead>
#<Thread:0x00005633007e7a00@a6.rb:5 dead>
#<Thread:0x00005633007e78e8@a6.rb:5 dead>
#<Thread:0x00005633007e74d8@a6.rb:5 dead>
#<Thread:0x00005633007e7b18@a6.rb:5 dead>

こんな風。これどうやっているのだろうとライブラリのソースを読んでみたら、Thread を待つのにさらに Thread と Thread::Queue を使っていた。なるほどなという感じ。


どうでもいいけれど、ムダに凝ってみた。

require 'thwait'

threads = []
5.times do |i|
  threads << Thread.new(i) {|i| puts "Thread #{i} start."; sleep(rand(0.5..1.0))}
end

h = threads.zip([*0..4]).to_h
ThreadsWait.all_waits(*threads) {|t| puts "Thread #{h[t]} is dead."}

結果。

Thread 1 start.
Thread 0 start.
Thread 3 start.
Thread 4 start.
Thread 2 start.
Thread 4 is dead.
Thread 2 is dead.
Thread 1 is dead.
Thread 3 is dead.
Thread 0 is dead

スレッドの開始って順番が保証されないのだな。

Thread とか Fiber っておもしろそうで使ってみたいけれど、何に使ったらいいだろう。「ブロック崩し」みたいなゲームとか?