読者です 読者をやめる 読者になる 読者になる

Line 1: Error: Invalid Blog('by Esehara' )

または私は如何にして心配するのを止めてバグを愛するようになったか

>> Zanmemo

あと何かあれは 「esehara あっと じーめーる」 か @esehara まで

本当のプログラミング初心者にElixirを教えたことで得た学び

今日の風景

f:id:nisemono_san:20161106011717j:plain

知っている人から知らないものをさしいれでいただきました。

f:id:nisemono_san:20161106011727j:plain

ところで、みなさんは完全ですか?

はじめに

知人が唐突に「これからElixirやっていくぞ」という気持ちになったため、Elixirのもくもくと勉強する会を秋葉原で定期的にやることになった。今回は第一回目であったため、以前に会をやっていたルームシェアでやることにした。

その中で、ウェブ媒体のライターをしつつ、のちのちはプログラマになりたいという若人がいたために、Elixir最速基礎文法をあらかじめ目を通しておいて、そのあとの課題としてProject Eulerの最初の10問程度をできるだけ自分で問いてみるということを提案してりたりした。他の人は、直接HTMLなどが表示できるほうがいいのではないか、という提案があったりしたのだが、個人的にはまず「分岐」「反復」といった考え方になれたほうがいいし、最初の10問程度はそれで済む程度の問題なので、それを頑張ってもらうようにした。

で、教える側というのは、往々にして教えられるというか、学びがあったので、そのあたりを共有しておこうかなあというのが今回のエントリの目的となる。多分、こういった「関数型」に近い言語に接するときの戸惑いというのは、わりと似ているように感じたので、そのあたりを共有していきたいと思う。

再帰の考え方

やはり、慣れなかったのは、再帰によって反復を行なうという部分だった。例えば、1から10の数字を表示するという場合、次のように書ける:

defmodule Count do
  def up(n) do
    _up(1, n)
  end

  defp _up(counter, m) do
    IO.puts(n)
    if counter != m do
      _up(counter + 1, m)
    end
  end
end

Count.up(100)

Elixirに関して、基本的にはwhileといったようなループ構文は用意されていないようなので、自分で自分の関数を呼びだすといった再帰的方法に慣れないといけない。今回の場合であるならば、1から100まで表示するためにはカウンターをただ1足していく方法でいいのだけれども、例えばProject Eulerの最初の問題でどうも躓いてしまうのである:

10未満の自然数のうち, 3 もしくは 5 の倍数になっているものは 3, 5, 6, 9 の4つがあり, これらの合計は 23 になる.同じようにして, 1000 未満の 3 か 5 の倍数になっている数字の合計を求めよ.

このとき、慣れているプログラマであるならば、まずカウントする従来の関数と、数字の合計を保持するための何らかの方法が必要となる、というように問題を読みとける。問題は、この数字の合計を何処に保持しておくのか、ということになる。

再帰的な方法として、一番簡単なのは、再帰するさいに、カウンダーとは別に「数字の合計」という暫定的な値を引数として渡してあげる、ということであるんだけれども、これを思いつくのがなかなか難しい。最初のカウンターを引数として渡してあげる、という発想から、引数というのは同時に次の実行する関数に渡してあげるさいに利用できるという考えに至るのが難しい。たぶん、これは再帰を勉強するにあたってひっかかる、まず最初の難関だと思う。

素朴に再帰を使って上の問題を解くと次のようになる:

defmodule Problem1 do
  def solve do
    _solve(1, 0)
  end

  defp _solve(1000, result) do; result end
  defp _solve(n,   result) do
    if rem(n, 3) == 0 || rem(n, 5) == 0 do
      _solve(n + 1, result + n)
    else
      _solve(n + 1, result)
    end
  end
end

IO.puts Problem1.solve

とはいえ、この再帰の方法は、直観的ではないようにも思える。ここまで書ければ上等なのだけれども、自分だったら、メモリ効率の問題とかを度外視した場合には次のように書くだろうと思う。

defmodule Problem1 do
  def solve do
    1..999
    |> Enum.filter(&(rem(&1, 3) == 0 || rem(&1, 5) == 0))
    |> Enum.sum
    |> IO.puts
  end
end

Problem1.solve

要するに、1から999のEnumerator(でいいのか?)を作り、そこから3と5の倍数のみの要素リストに加工してあげて、それを合計するという方法を取る。関数型の世界だと再帰は強すぎるので、もし再帰が必要になったさいには、その再帰mapfilterなどの高階関数で表現できないものなのかどうなのか、というを検討するのが良い(という話をどこかで聞いた)。

また、Elixirというか、関数の引数においてパターンマッチを採用している言語(Haskellとか?)でもそうなんだけど、このときに関数が実行される順序がわからない、という混乱があったようで、これ自体に関しては条件があっている関数が見つかったときに、その関数が実行される、という説明くらいしかできなかったけれども合ってるのだろうか。

あともう一つとして、ちょっと難しいなあと思ったのは、例えば次の問題:

フィボナッチ数列の項は前の2つの項の和である. 最初の2項を 1, 2 とすれば, 最初の10項は以下の通りである. 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ... 数列の項の値が400万以下の, 偶数値の項の総和を求めよ.

この問題のときに、C言語のサンプルなんかを参考にしていたみたいだけれども、それが余計混乱する要因になっていたようで、C言語はわからないけれど、例えばPythonで泥臭く書くならば、次のように書くことができる:

def fib_sum():
    f0 = 1
    f1 = 1
    result = 0

    while f1 < 4000000:
        if f1 % 2 == 0:
            result += f1
        f2 = f0 + f1
        f0 = f1
        f1 = f2

    print(result)


if __name__ == "__main__":
    fib_sum()

このように泥臭く書くことはできるのだが、しかしElixirでは先程もいったように、そもそもwhile自体をどのように再帰的なコードに書き直すのか、というところがある。フィボナッチ数列自体は、数列にした場合に、「横にずれていく」数列であるということに気がつけば、前の二項の後ろ側を一つずらし、新しく二項を足したものを新しく後ろに追加していくようなことを繰りかえすことを考えるとわかりやすい。

defmodule Problem2 do
  def solve do
    _fib_sum(1, 1, 0)
  end

  defp _fib_sum(f0, f1, result) do
    cond do
      f1 >= 4000000   -> result
      rem(f1, 2) == 0 -> _fib_sum(f1, f0 + f1, result + f1)
      true            -> _fib_sum(f1, f0 + f1, result)
    end
  end
end

IO.puts Problem2.solve

しかし、個人的な審美眼からすれば、このコードはそもそも直感的ではないのではないか、という不安を感じざるを得ない。というのは、フィボナッチ数列から指定された範囲の中で偶数の数字を取りだして、それを足しあわせればいいのではないか、ということで、自分の解答は以下の通りになる:

defmodule Problem2 do
  def solve do
    Problem2.gen_fib(1, 1)
    |> Enum.filter(&rem(&1, 2) == 0)
    |> Enum.sum
    |> IO.puts
  end

  def gen_fib(prev, next) do
    if prev + next <= 4000000 do
      [prev + next | gen_fib(next, prev + next) ]
    else
      [prev + next]
    end
  end
end

Problem2.solve

こうすると、指定させた範囲のフィボナッチ数列から、偶数の数だけを取りだしてきて、それを合計するということが表現しやすくなった。ただし、この方法は、フィボナッチ数列がある程度のリストまで増えないということがわかっているが故の方法でもある。

まとめ

たぶん、大企業の人々は、新卒の本当に「プログラミングを経験したことがありません」という人に対してこういった初歩的な分岐や反復を教えるということは多々やってきていると思うのだけれども、そういう機会が全くない人間にとっては、こういう風に教えることは殆どないので、教えることで学ぶことができたというのは大きい。

また、やはりいきなりの初心者にとって、Elixirはハードルが高いのはあたりまえで、そういうのにぶちこんでしまったこっちの責任もあるのだろうけれども、しかしここの考え方自体は他のプログラムを組むさいにも流用できると信じて、応用していって欲しいなあという気がした。教えた方は別件でPythonチュートリアルもやっているようなので、それを比較しながら、「こういう風に違うのか」ということを学んでいってくれればなあ、という気持ちになったりした。

プログラミングElixir

プログラミングElixir

日報: PAY Conferrence #01 に行ってきた。

今日の風景

f:id:nisemono_san:20161027081643j:plain

端的に言えば断捨離です。

本文

laisoさんが「そういえば、似非原さん、仕事探しているんでしたら、うちの会社にも遊びに来るといいんじゃないんですか、その機会を作るので、よかったらよろしくです」という話だった。

基本的にこういうのは前もってそういう会社のイベントに申しこんで、距離を狭めていったほうがいいのではいないか、という自分基準の理由により、PAY Conference #01に若干遅れて参加した。ハラペコだったので、途中で漂ってくるピザの芳しい匂いと戦っていた。貧しければ鈍するということだろう。

で、今回のPAY.JPの話としては、乱暴にまとめてしまうならば、「決済するときに、その決済に対する信頼を担保するのが問題」というのがあって、それを解決するのが、PAY.JPのミッションなんだろうなという漠然とした像ができた。

当然ながら、いきなりベンチャーが「決済一括についてやります」って言っても、それは以前なら門前払いされたらしいんだけれど、最近はそのあたりはやさしくなってきてるわけで、そういう配慮もあって実現できたりしている。細かい話もあるのだけれど、カード会社との信頼の担保については、大きな交渉の末に成りたっているという、当たりまえなんだけど、やろうとすると難しいことをやっているんだな、というのがひしひしと感じる。

じゃあ逆に消費者側というか、今回の場合、PAY.JPの親がBASEなので、それで考えると、BASEの場合であるならば、明らかな反社会勢力ではない限りは、基本即日OKで店舗を開設できるようにしたい。で、そこの決済をPAY.JPに委託している、とする。そうすると、当然その開設に対する審査というのも大変になってくる。

いまのところさばききっているのかは、今のところ不明なところもあるのだけれども、ここで大変になるのは、二つのシステムがあって、たとえば元々フリマを経営しているM社がいたとして、その会社が「今後はPAY.JPを使いたいんですよね」というと大変になる。

このとき、二つの方法がある。まずその大きな会社が担保になって、「売り手」と「買い手」を審査してもらう方法。ただ、これだと確かに審査の手間は減るのだが、その会社が担保になっている(と理解していいのか)ので、その分自由度は減る。

一番いいのは「売り手」と「買い手」を直接審査する方法で、こちらのほうが明かにいいのではあるのたが、先ほども言ったように、フリマを運営している会社の会員数によっては、それを審査するのは無茶な話なので、後者を取るとするならば、いかにそれらをさばいていくか、という問題になると思う。

とはいえ、決済の裏側なんて普段はあまり意識しないものだから、(「アプリとかで使わないんですか」ってツッコまれたけど、そういえば、そういう売買系のアプリを作ったことがない……)参考になったというというか、「なるほどな」ということを考えていた。

もちろん、これは今後APIとかで簡単にWebアプリケーションで決済ができるということを目指しているので、例えば、「こういうブログを買いてくださいよ」なものだったり、あるいは最近なら「アーティストを応援するプラットフォーム」みたいなのがあるんだけど、そういうのが簡単に作れるようになるわけだから、いわばスタートアップにとってもやさしい仕組みになっていくはずだ、とまあそういう風な話がされていて面白かった。

とはいえ、自分はカードが使えなくなった記念日なので、決済考える会社で信頼を失った直後であるのはかなり厳しい社会というものを考えないといけない。決済は決済、信頼は信頼なのだから……

日報: とある企業に面接に行ってきた(1)

今日の風景

f:id:nisemono_san:20161018180539j:plain

転職しようとする人のモノマネです

本文

シリーズもののように、連番は付けているが、これが続くかどうかはわからないが、感触からすれば続くだろうなと思ったので連番にしようと思った。

一般的に、最近の企業ならば、面接者にコードを書かせるかと思われる。とある企業では、事前に聞いていたテストだったので、ここで種あかしするのは野暮であり、と同時に一部には「とある企業」の「とある」が分かってしまうので、曖昧にぼかすのだけれども、基本的に自分はREPLに甘やかされていて、それで通らないコードを提出してしまった。

なので、今からでもいいから「そのソース書きなおさせろ」と殴りこみをかけたいものだが、良識を分きまえているつもりなので、それは知なかったが、動かないコードが、あとから動かないと気がついて、面接会場から出るというのは、本当に恥かしい経験で、穴があったら入りたいどころか、穴二つ分作りたい気分になったりした。

まあそういうことなので、面接の内容を若干変更して、こういうものだった、というのを解説すると次のようになる。

まず、「esehara」という文字列がある。この文字列を「eeaa」といったように、一文字ずつ飛ばし文字にするにはどうしたらいいか、という課題だったと、ここでは仮定しよう。

最初に、自分が「多分これで動く」というコードをエディタで提出した結果である。

def one_jump(s)
  r = []
  s.each_with_index do |c, i|
    r.shift(c) if i % 2 == 0
  end
  return r.to_s
end

まず最初に、shiftは先頭から要素を取りだしてくるメソッドであるので、これは本来間違いでpushを使うべきだった。あと、to_sも、これも単純に配列を文字列にするだけだから、これも減点。こちらはjoinを使うべき。これは勘違いのご愛嬌だったとしても(普通のRubyistなら間違わない点だから、この時点で失格だろう)、もっとまずいエラーがある。

よく文字列といえば「列」なので、恰も配列のように扱ってしまう傾向にある。例えば"Foobar"[2]であるならば、oという文字列を取ってくる。しかし、Rubyの場合、文字列は厳密には配列(もうちょっと言えばArrayではない)ので、each_with_indexは使えないのである。

これに気がついた瞬間、似非原は激怒した。必ず、かの無知蒙昧なるコードを除かなければならぬと決意した。メロスには転職がわからぬ。メロスは、ただの無職である。ブログを書き、Rubyと遊んで暮して来た。けれどもコードに対しては、人一倍に鈍感であった、というわけである

とはいえ、親友のために走りだしたとしても別にコードは書きなおさせてくれないものであるから、無く無くこの凡ミスを受けいれるしかない(どれだけ採用に響くかは知らないが、すくなくとも「これが残っていると恥ずかしい」というのは誰しもあると思う)。

長々とコメントが続いてしまったけれども、ではどうすればいいのか。これはRubyでは良く付かわれるテクニックで、スニペットみたいなものだけれども、split('')を使えば良い。

def one_jump(s)
  r = []
  s.split('').each_with_index do |c, i|
    r.push(c) if i % 2 == 0
  end
  return r.join('')
end

というわけで、本来はこういうコードが書ければ良かったのだけれども、いざ目の前でコードを書いてみろと言われると、こういう恥ずかしいコードになってしまうわけで、恥ずかしいということはお祈りということにも繋る。まだ、それが明確になっているわけではないから、そのように自分でふてくされるのは意味がないことだが、既に自分の中では「やってしまった」感が強いのは否めない。従って、似非原先生の次回作にご期待下さい、ということになる。(続く)

ちなみに

どうしてもeachを使いたい場合には、each_charというメソッドが用意されていることを教えてもらいました。

日報: 治安の悪い「オッ」界隈について

「オッ」界隈とは何か

「オッ」界隈とは、簡単に言ってしまうならば、Twitter上で、何らかの発言や記事に対して「オッ」とだけ返す界隈が存在していることを指している。例えば、「お腹すいたな」という発言をすると、「オッ」とだけリプライが返ってくる、そういう界隈である。

本来ならば、そういう発言に関しては、お気に入りに入れてほんわかと見守るのが、インターネット上のマナーとなるわけだが、「いいね」とすることが、どちらかと言えば消極的な返事に対し、「オッ」と返すことは積極性を反映し、興味関心が非常に強いということを相手に伝え、繋がりが薄いと言われている現代社会に強い絆を作りだすと同時に「お前を見ている」というビックブラザー効果が期待できるということらしい。

f:id:nisemono_san:20161023122941j:plain

上が、そのようなビックブラザー性を象徴する「オッ」界隈の画像である。

要するに「オッ」と言われたら「オッ」と返ってくるようなインターネット、これが「オッ」界隈である。

「オッ」界隈への参加

「オッ」界隈への参加は非常に簡単である。「オッ」界隈では、なんらかの投稿に対して「オッ」と返事することのみを持ってして、「オッ」界隈だと考えている。従って、「いいね!」の代わりに「オッ」と発言すれば、それは即「オッ」界隈への参加となる。

しかし、「オッ」界隈に対して、「オッ」と返事することは、少くとも「oxtsu」と打ちこまなくてはいけないわけで、これは非常に面倒であるという側面は否めない。従って、これを簡略化するために何らかのアプリが作れれば便利であって、とすると最近ならばElectronで作ればいいな、ということで作ったりしていた。

f:id:nisemono_san:20161023123129j:plain

技術的には特に難しいことをしていないのだけれども、あえて挙げるとするならば、Qiitaにて「ElectronでTwitterアプリを作ろう!」みたいなサンプルを見てみると、現時点ではipcMainとipcRenderが分かれているのだけど、当時のコードでは分かれていないということがあったりして非常に面倒だった記憶がある。あと、あえてipcで通信しているのは、remoteの挙動がやや不明瞭であったのがある。本来ならremoteで作ったほうがスッキリするのかもしれない。これはElectronの知見が必要だが、そこまで知見を集める必要があるのかというと、それもやや不明である(上みたいな、書き捨てGUIアプリを作れるくらいには知見があったほうがよさそう)。今だと他にクロスプラットフォームで作れる奴はいろいろあるので、そちらで作ったほうがいいという気持ちもなくはない。

「オッ」界隈の成熟

というわけで、文字列による「オッ」などのやりとりはこういった自動化により、爆発的に進化していったのだが、しかし「オッ」界隈は、当然文字列だけでは満足はせず、画像による「オッ」の表現誕生した

f:id:nisemono_san:20161023131836j:plain

f:id:nisemono_san:20161023123148p:plain

このことにより、「オッ」界隈は動画などのマルチメディアに発展していくことになる。以下が、その参考例である。

自分に関して言えば、Processingを使っている。

今後の「オッ」界隈について

今後、「オッ」が成熟するにつれて、遠くからでも簡易デバイスによって「オッ」できるようになるIoT(Internet oxtsu Thing)であったり、あるいはVR活用による「オッ」など、さまざまな分野に広がっていくことが予想される。それに伴い、各企業にCTO(Chef Technical Oxtsu)の役割が必要になってくると予想される(これが言いたかっただけである)。

最後に

正直、俺自身もこのエントリ何言っているかさっぱりわからない

参考エントリ

日報:「会」をやる

今日の風景

f:id:nisemono_san:20160503094324j:plain

最近の、ごくごく一部のエンジニア界隈では、言葉を短縮する傾向にあり、例えば「気持ち」だとか、あるいは「やっていく」とか、そういった雑な言葉が日々誕生している。その理由を察するに、基本的には曖昧さとか雰囲気とか、あるは日本語の乱れとかそういうことなのだろうと思う。そういう言葉使いが好きな人は小林銅蟲先生の「めしにしましょう」という料理漫画が近日発売されるので、予約購入するといいのではないか、と思う。

めしにしましょう(1): イブニング

めしにしましょう(1): イブニング

で、その文脈を補強した上で、さらに曖昧な形で、知人達がやっている「サウ」という場所で、「JSの会」、通称「会」というのをプライベートでやったりした。「サウ」とは下のような場所である。

f:id:nisemono_san:20151102030359j:plain

「サウ」というのは「サマーウォーズ」の略で、長広い和室に机が並んでいるのが「サマーウォーズ」っぽいところから派生して、それを略した形となる。「サマーウォーズ」になると「サマーウォーズ」になってしまうので、それを略して「サウ」になったんだと思うけれども、そのあたりの歴史的経緯は知らない。ちなみに「サウ」自体は禁煙である。

そういう歴史的な経緯はともかくとして、「JSの会」をやることになった。「JSの会」というのは、自分がひさしぶりにフロントエントをやろうとしたときに、どうせだったら「ECMAScript 2015」の仕様を知りたかったので、じゃあ集まって勉強しましょうということで、改めて勉強したのがこの会になる。成果としては下のスライドに詳しい。

あとは人のスライドをダシにしながら、Vue.jsについてのお話しと、ReactやRuduxの話をしたりしていた。知人はチュートリアルを作っていて、コミットを見ていくとわかる作りになっているということで、今後これを使ってハンズオンする予定もある。ちなみに、「人の知らないスライドをつまみにしながら、好き勝手にマサカリを投げる」という形式は非常に効果的であるので、是非薦めたい。ちなみに、もうVue.jsは2.0がリリースされるのにも関わらず、このスライドを使って解説していた。

Vue.jsに関しては、知人がかなり苦労した部分もあったので、結構厳しめの意見が出てきて、個人的には「Vue.jsというのは、PHPに似ている」という話をした。もちろん生PHPではないけれど、Viewに対して、反復であったり、分岐であったりするような、若干ロジックが漏れだすような仕組みにはなっていて、それはテンプレートエンジンの宿命なのかもしれないけれど、「PHPっぽい」という気持ちにはなる。また、下手に画面フォームをモデルとして結びつけて、そのフラグをさらにその画面の中で利用してフラグとして活用するみたいなことができるので、そこらへんのフォーム部分制御に関して意識しないと、混沌としたものになるのではないか、という話をした。

あと、フレームワークついでに言うと、Vue.jsはPHPでいうところのCodeignitorや、RubyでいうところのSinatraみたいな、わりと薄いフレームワームなので、簡単な画面を作るときは、設計を気にする必要はないけれど、大規模になると、フレームワークの制約が無いせいで、途端に泥団子みたいにどんどんくっつけていくことになり、日にあてて乾かすとヒビがわれて破滅することになるが難しいという話もした。

このあたりについては、テンプレートであったり、コンポーネントの概念が出てくるわけで、もちろんVue.jsが意識していないわけではないのだけれども、最初の学習コストの安さと、どこでそのあたりをちゃんと学習してペイしていくか、という意識を分けるのは重要であるよなあという気がしている。次はRiot.js最強説が出ているので、そこらへんもおさえていきたいところであったりもする。

とにかく、「会」という概念は便利なので、どんどん雑な「会」を開いていって、知見を広げていきたいとおもった次第でした。

f:id:nisemono_san:20161018170329j:plain

ちなみにこういう格好でやっていたので説得力は殆どなかった。(違うスペースなので煙草をすっている)

参考資料

開眼!  JavaScript ―言語仕様から学ぶJavaScriptの本質

開眼! JavaScript ―言語仕様から学ぶJavaScriptの本質

Rubyのグラフライブラリ(gnuplot)で砲台ゲームを実装しよう

今日の風景

f:id:nisemono_san:20161013152351j:plain

寿司の電子工作

はじめに

今は廃刊してしまった、昔懐しい『ベーシックマガジン』という雑誌には、大抵簡単なゲームのコードが載っていた。俺を含めたパソコン少年は、そのコードを見ながらパチパチとコードを打っていたわけだが、そういったサンプルコードリストの一つに、大抵は「砲台ゲーム」というものが存在していた。

仕様としては、ある範囲が的になっており、角度と強さを指定し、投射線を描写する。的の範囲に入っていたら的中となり、的の外になっていたら外れということになる。

大抵、このゲームに関しては、30代から40代にBASICを触っていたおじさんであるならば、何処かで見たことがあると思うのだけれども、舞台がWebになった現代には、このような「砲台ゲーム」を作るといったようなサンプルを見ることは殆どなくなった。

で、前回にRubyからgnuplotを触るエントリを書いたわけだけれど、「あれ、グラフを使えば砲台ゲームが作れるんじゃないか」と思って、実装してみることにした。実行環境はJupyter Notebookを参考にしているので、上のリンクからインストールして欲しい。

投射線を描写する

ここからは殆ど『Pythonからはじめる数学入門』のパクリとなるので、正確な説明が欲しい場合には、実際に買ってみて試して欲しい。

投射線というのは「ある物体を投げたときに描く軌道のこと」と説明することができる。さて、このとき「ものを投げる」ということを考えた場合、「その物体を投げる力」と、「その物体をどの方向に投げるのか」という二つの物差しで考えることができる。

さて、このように考えたとき、我々の世界には「時間」という概念が存在している。つまり、「ある物体を、任意の力と角度で投げた場合の、ある時間の一点」というのが存在している。この詳しい原理について気になる人は、別途物理の教科書を参照して頂くとして、これを算出する式は、コードであるならば次のように書ける。

  def parabora(u, t)
    t = t / 180.0 * Math::PI
    g = 9.8
    t_flight = 2 * u * Math.sin(t) / g
    intervals = (0.0..t_flight).step(0.001)
    return [
      intervals.map {|x| u * Math.cos(t) * x}, 
      intervals.map {|y| u * Math.sin(t) * y - 0.5*g*y*y }
      ] 
  end

一から順番に追っていく。まず、最初の「t」はラジアン変換している部分だ。ラジアンは、2πを360度とするので、角度をまず最初に180度で割る。そうすると、nπのnの部分が出てくるので、それを使う。

次のgは単なる重力なので、特に気にしなくてもよい。

次に気になるのは、t_flightだが、これはボールの軌道をいつ止めるかの式になる。これは、ちょうどy軸が0になる範囲を示している。

あとは、u * Math.cos(t) * xの部分だけれども、これは要するにパワーと角度、そして時間をかけて、cosで求めている。一方、yのほうに関しては、重力補正がかかるので、ちゃんと- 0.5 * g * y * yといったように、時間軸にあわせて重力の値が補正できるようにしておかないと、どこまでも飛んでいってしまう。ちなみに、ここの解説が雑なのは、自分もよく理解していないせいもあるので、実際に知りたい人は物理の本を読むといいと思う。

class GameField
 #...

  def parabora_only(u, t) 
    Gnuplot.open do |gp|
      Gnuplot::Plot.new( gp ) do |plot|
        ball = Gnuplot::DataSet.new(parabora(u, t)) do |ds|
          ds.with = "lines lt rgb\"blue\""
          ds.linewidth = 1
        end
        plot.data << ball
        IRuby.display(plot)
      end
    end
  end

 #...
end

と書くことによって、下のような図を作画することができる。

f:id:nisemono_san:20161013160525p:plain

ゲーム作るぞー

投射線の考え方はわかった。ポイントは的を描写することである。gnuplotにグラフを流しこむことは簡単で、一次元の配列の場合、要素数が xとなる。これを利用し、途中までの外れの部分の配列は「当たり」の部分の配列よりも短くするようにする。これを重ねることによって、ズレが発生し、当たりの部分がはみだして見えるわけですね。要するにズルです。

# ...

  def initialize
    @first = Random.rand(100...400)
    @last = @first + Random.rand(50..100)
  end

  def show
    Gnuplot.open do |gp|
      Gnuplot::Plot.new( gp ) do |plot|
        plot.title  'Game'
        # gnuplotの場合、配列の要素がxになるので、挟まないといけない
        fix = Gnuplot::DataSet.new([600]) { |ds| ds.with = "lines lt rgb \"#ffffff\"" }
        
        field = Gnuplot::DataSet.new([*0..@first].map {|x| 0 }) do |ds|
          ds.with = "lines lt rgb\"green\""
          ds.linewidth = 10
        end

        hole = Gnuplot::DataSet.new([*0..@last].map {|x| 0 }) do |ds|
          ds.with = "lines lt rgb\"red\""
          ds.linewidth = 5
        end

        plot.data << fix
        plot.data << hole
        plot.data << field
        IRuby.display(plot)
      end
    end    
  end

# ...

諸事情によりDRY、つまり重複コードばっかりなのは御愛嬌として、こうすることによって、下のようなグラフができる:

右上に点を付けているのは、グラフが最大の値に合うようになるので、こうすることによって、よりゲームらしい広がりのある表現を作りたかったためである。

さて、これを描写すると次のようになる

f:id:nisemono_san:20161013160539p:plain

おっ、砲台ゲームっぽいですね。

発射メソッドを作る

というわけで弾を発射します。こんな感じでいいのではないかと。

    Gnuplot.open do |gp|
      Gnuplot::Plot.new( gp ) do |plot|
        # gnuplotの場合、配列の要素がxになるので、挟まないといけない
        fix = Gnuplot::DataSet.new([600]) { |ds| ds.with = "lines lt rgb \"#ffffff\"" }
        
        field = Gnuplot::DataSet.new([*0..@first].map {|x| 0 }) do |ds|
          ds.with = "lines lt rgb\"green\""
          ds.linewidth = 10
        end

        hole = Gnuplot::DataSet.new([*0..@last].map {|x| 0 }) do |ds|
          ds.with = "lines lt rgb\"red\""
          ds.linewidth = 5
        end

        ball = Gnuplot::DataSet.new(parabora(u, t)) do |ds|
          ds.with = "lines lt rgb\"blue\""
          ds.linewidth = 1
        end

        plot.data << fix
        plot.data << hole
        plot.data << field
        plot.data << ball
        t2 = t / 180.0 * Math::PI
        last =  u * Math.cos(t2) * (2 * u * Math.sin(t2) / 9.8)
        if last.abs.between?(@first, @last)
          plot.title  '成功'
        else
          plot.title  '残念'
        end
        
        IRuby.display(plot)
      end
    end
  end

結果として、何処に落ちたかどうかは計算しなければいけなかったので、別途u * Math.cos(t2) * (2 * u * Math.sin(t2) / 9.8)みたいなかたちで、最後のx軸の部分を計算している。ただし、マイナスになることもあるので、absでちゃんと判定できるようにすると吉。

f:id:nisemono_san:20161013160653p:plain

これは残念なパターンです。

f:id:nisemono_san:20161013160701p:plain

やりましたね。

ちなみに、全体のソースはこちらになります。

まとめ

数学だと思ったらいきなり物理の話が出てきて、とまどったし、実際なんでこういう方程式になるのかというのかを説明できない馬鹿なので、今回はポコー(心が凹んだときの音)した。しかし、グラフが書けると、このように砲台ゲームを作ることができる。今だとR言語がグラフを作るのに丁度いいので、こういう砲台ゲームを作ってみると楽しいのではないか、と思ったりした。だれか書いてくれませんかね(お前が書け)

Pythonからはじめる数学入門

Pythonからはじめる数学入門

Jupyter Notebookを使い、Rubyでgnuplotを使ってグラフを書いてみた感想

今日の風景

f:id:nisemono_san:20161010133520j:plain

抽象的に言えば寿司です。

Jupyter Notebookについて

過去のエントリにも紹介したけれども、データサイエンティストにとって、もはや必須道具と呼ばれているツールにJupyter Notebookがある。基本的にはPythonから派生したツールなので、Pythonで良く使われている。基本的には画像をグラフィカルに表示できる、ブラウザ上で使えるREPLだと思ってくれれば良い。

Pythonが派生であるとはいえ、現時点では、そういったグラフィカルなREPLとして、多くの言語をサポートするようになっている。現時点で使える言語はここにまとまっているので参考にして欲しい。

唐突になぜJupyter Notebookのことを話題に出したかというと、Asakusa.rbが丁度やられていて、どうせ近くだから、一時間くらい遊びにいこうかなと思ったのがきっかけだった。ちなみに、途中に秋葉原で、ダンボールに円を描いて物乞いをしているおじいさんがいて、自分も本当にどうしようもなくなったときは参考にしようと思ったりした。

それはともかくとして、誕生日プレゼントとして頂いた『Pythonからはじめる数学入門』という本があるのだけれど、せっかくなので、Rubyでできる環境を構築しようかな、というのが今回のエントリの趣旨となるわけである。

Jupyter Notebookからgnuplotを動かす

とはいえ、まず開発環境が無いとはじまらないので、適当にJupyter Notebook + Rubyを入れてください。このあたりに関しては、検索すればいくらでも出てくると思う。

もし、Jupyter NotebookにうまくRubyの環境が入ったならば、「New」というメニューのところに下のように出てくるはず。

f:id:nisemono_san:20161012045602p:plain

もし、選択を間違えても「Chenge Kernel」が使えるので気にしなくていい。

f:id:nisemono_san:20161012045613p:plain

さて、さっそくRubyでグラフを描こうということになるのだが、gemの候補としては二つある。一つ目は、gnuplotをそのままラップした、そのものズバリのgnuplotというのがある。ただし、メンテナンスを見ればわかるように、2013年の段階で開発は停滞しているのだが、いい感じでグラフが描ければいいので、こちらを利用するのはあり。

あともう一つの候補としては、確かにgnuplotはいいんだけど、モダンなインターフェイスが欲しいよね、ということになると思う。そういう場合はnyaplotを使えばいいと思う。nyaplotはメンテナンスが進んでいるので、利用する側も安心であると思う。

とはいえ、人間はモダンであればいいというわけではなく、「いや、そこはgnuplotを使いたいんだ」という人もいると思われるので、Jupyter notebookからgnuplotを使う方法を書いておく。ちなみに、Rubyでgnuplotを使う方法については、ここを見れば書いてある

直線のグラフを書いてみよう

というわけで、さっそくJupyter notebookでgnuplotを使っていくことになるわけなんだけど、使うだけなら、前のエンドリにあるように:

require 'gnuplot'
Gnuplot.open do |gp|
  Gnuplot::Plot.new( gp ) do |plot|
    plot.title  'test'
    plot.ylabel 'ylabel'
    plot.xlabel 'xlabel'
 
    x = (-100..100).collect {|v| v.to_f}
    y = x
    
    plot.data << Gnuplot::DataSet.new( [x, y] ) do |ds|
      ds.with = "lines"
      ds.notitle
    end
  end
end

と描けば良い。しかし、これには問題があって、このままだと、別ウィンドウに描写されてしまうことになる。

f:id:nisemono_san:20161012045721p:plain

今回の趣旨としては、Jupyter Notebookにグラフを描きたいわけだから、この要件にはあわないことになる。

そこで、おそらくJupyter Notebookにある、何かしらのAPIを使えば画面に表示できる筈である。そのものズバリIRuby.displayである。さっそく、それをコードに追加してみる。

require 'gnuplot'
Gnuplot.open do |gp|
  Gnuplot::Plot.new( gp ) do |plot|
    plot.title  'test'
    plot.ylabel 'ylabel'
    plot.xlabel 'xlabel'
    plot.size "0.5, 0.5"
    plot.origin "0.5, 0.5"
    plot.bmargin "0"
    
    x = (-100..100).collect {|v| v.to_f}
    y = x
    
    plot.data << Gnuplot::DataSet.new( [x, y] ) do |ds|
      ds.with = "lines"
      ds.notitle
    end  
  IRuby.display(plot)
  end
end

f:id:nisemono_san:20161012045733p:plain

やりましたね。

ちなみに、実際のgnuplotを使ったRubyの描き方はここが詳しいので、参考にするといいと思う。

まとめ

基本的には、この手のグラフに関しては、matlabなどや数学系ライブラリが充実してあるPythonを使うほうが望ましい可能性はあるのだけれども、簡単なグラフであったり、どうしてもRubyで書きたい、あるいはgnuplotが慣れているので……という人は、こういう方法があるので、やっていってみてはどうでしょうか。因みに、 個人的にはnyaplotのほうが良さそうな気がしているんだけれど、こればっかりは触ってみないとわからないですね。

Pythonからはじめる数学入門

Pythonからはじめる数学入門