雀巽の日記帳

雀巽が綴る日常の記録

すごいH本読書会始めました

すごいH本

知らない人が見たら間違いなくタイトルがヤバい。

ヤバいと感じた人は今すぐ「すごいH本」で検索をかけましょう。 ヤバくないことが確認できるかと思います。 それどころか素晴らしいとすら感じるはずです。

すごいH本の正解はこちら。Haskellの素敵本です。

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!

Haskell勉強会開催!

渋谷の某所で、Haskell勉強会(すごいH本読書会)を始めました。 本日は2回目で、2章まで読み進めた後、Project Eulerを1問だけ解きました。

正直、今日は超まったりしてました。

第1回の内容については、Haskell勉強会メモ #1に後輩がまとめてくれています。感謝!

Project Euler

プロジェクト「オイラー」と読みます。あのオイラーです。

Project Euler is a series of challenging mathematical/computer programming problems that will require more than just mathematical insights to solve.

超簡単に言うと、プログラムで解く数学の問題集です。

問題はこのページに載っています。 英語かよ!ふざけんなよ!という人は、ここを見ると良いかもしれません。なんと和訳してくれています。

Problems 1

Problems 1 Multiples of 3 and 5を解いてみました。

If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23. Find the sum of all the multiples of 3 or 5 below 1000.

和訳はこちら。

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

同じようにして, 1000 未満の 3 か 5 の倍数になっている数字の合計を求めよ.

すごいH本の1章にある、内包表記を用いて簡単に解くことができました。

sum [ x | x <- [1..999], x `mod` 3 == 0 || x `mod` 5 == 0 ]
=> 233168

なんとたったこれだけ!気持ち良い! Rubyで書くとどうなるかなと思って、まずは素直に書いてみました。

x = 0
(1..999).each do |n|
  x += n if (n % 3 == 0) || (n % 5 ==0)
end
x
=> 233168

あんまり美しくない……ちょっとRuby力低すぎました。
というわけで、改善してみました。

((1..999).select { |x| x % 3 == 0 || x % 5 == 0 }).inject(0) { |sum, x| sum + x }
=> 233168

ほい、1行!でも、Haskellの内包表記の方が可読性は高い気がします。 少しゴチャゴチャしているように感じます。 もちろん私のRuby力が低すぎるだけの可能性もあります。

それと、selectの動きはわかりやすいですが、injectは慣れてないとわかりにくいですね。 ちなみに、いま初めて知ったメソッドなので、もちろん慣れていません! injectの解説については、ruby の inject をわかりやすく説明してみるがわかりやすそうでした。 慣れていたら全く問題ないとは思います。

立派なHaskeller目指して頑張ります。

追記1

素敵なリプライをいただきました。

やってみた。

((1..999).select { |x| x % 3 == 0 || x % 5 == 0 }).reduce(:+)
=> 233168

すごい……!とりあえず、ググる旅に出よう。 まずはreduceについて。調べるとなんとあっさり、reduceinjectの別名らしいということが判明。とりあえずinjectに置き換えてみた。

(1..999).select { |x| x % 3 == 0 || x % 5 == 0 }).inject(:+)
=> 233168

確かにどっちでもいける!

さて、問題は:+の部分。 またSymbol#to_procあたりの不思議な記法かと思いましたが、 リファレンス読んだら普通に書いてありました。 ちゃんと読むんだ俺よ……。

module Enumerable inject

reduce(sym) -> object

ブロックの代わりに使われるメソッド名を表す Symbol オブジェクトを指定します。 実行結果に対して sym という名前のメソッドが呼ばれます。

例: p [1, 2, 3, 4, 5].inject(:+) #=> 15

でもおそらく動作的にSymbol#to_procあたりが鍵なんだろうなと思って調べてみたら、やっぱりそうっぽかったです。RubyのSymbol#to_procを考えた人になってみるという記事が非常にわかりやすかったです。

Symbol#to_procの動作をあまり理解してなかったので、そこも調べておきました。

to_proc (Symbol)

to_procメソッドは、シンボルと同名のメソッドを使う手続きオブジェクト(Proc)を作成して返します。手続きオブジェクトは、Proc.new {|obj, args| obj.send(sym, args) }と同じようなものになります。

つまり、こんな感じになるようです。

prc = Proc.new {|obj, *args| obj.send(sym, *args) }
prc.call(obj, *args)
=> obj.sym(*args)

具体的に:+で試してみると、以下の通り動きました。

prc = Proc.new { |obj, *args| obj.send(:+, *args) } # <=> :+.to_proc
=> #<Proc:0x007fa50a0fb0c0>
prc.call(4, 5)
=> 9

奥が深い。

追記2

素敵な先輩からもリプライをいただきました。

さて、試しましょう。

require 'active_support'
require 'active_support/core_ext'

((1..999).select { |x| x % 3 == 0 || x % 5 == 0 }).sum
=> 233168

ほんとだできた!こっちの方が名前が直感的でわかりやすいですね。

ActiveSupportって名前だけ知っててよく知らなかったのでこちらも調べてみました。

Railsの全体像を知ろう (2/2)

Railsを構成する部品の中で、もっとも汎用的なライブラリがActiveSupportです。ActiveSupportは、いわば「素のRubyにちょっとした便利な機能を付け加える」役目を果たしています。

ほむほむ。

余談ですが、筆者の経験では、blnak?はあまりにも基本的かつ便利なため、Railsの初心者はこれをRubyの機能だと信じていたり、ところかまわず乱用する傾向があります。

確かにあまりにRailsで自然に使ってると、Rubyの機能だと勘違いしそう。 気をつけよ!