すごいH本読書会始めました
すごいH本
知らない人が見たら間違いなくタイトルがヤバい。
ヤバいと感じた人は今すぐ「すごいH本」で検索をかけましょう。 ヤバくないことが確認できるかと思います。 それどころか素晴らしいとすら感じるはずです。
すごいH本の正解はこちら。Haskellの素敵本です。
- 作者: Miran Lipovača,田中英行,村主崇行
- 出版社/メーカー: オーム社
- 発売日: 2012/05/23
- メディア: 単行本(ソフトカバー)
- 購入: 25人 クリック: 580回
- この商品を含むブログ (73件) を見る
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
素敵なリプライをいただきました。
@necojackarc inject(0) {... の部分はreduce(:+)でいけるので、試してみてください☻
— yasaichi (@_yasaichi) 2015, 2月 14
やってみた。
((1..999).select { |x| x % 3 == 0 || x % 5 == 0 }).reduce(:+) => 233168
すごい……!とりあえず、ググる旅に出よう。
まずはreduce
について。調べるとなんとあっさり、reduce
はinject
の別名らしいということが判明。とりあえずinject
に置き換えてみた。
(1..999).select { |x| x % 3 == 0 || x % 5 == 0 }).inject(:+) => 233168
確かにどっちでもいける!
さて、問題は:+
の部分。
またSymbol#to_proc
あたりの不思議な記法かと思いましたが、
リファレンス読んだら普通に書いてありました。
ちゃんと読むんだ俺よ……。
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メソッドは、シンボルと同名のメソッドを使う手続きオブジェクト(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
素敵な先輩からもリプライをいただきました。
@necojackarc ActiveSupport 使えば Enumerable#sum でもいけるよ🙆
— ときめきクッキンッ! (@na_o_ys) 2015, 2月 14
さて、試しましょう。
require 'active_support' require 'active_support/core_ext' ((1..999).select { |x| x % 3 == 0 || x % 5 == 0 }).sum => 233168
ほんとだできた!こっちの方が名前が直感的でわかりやすいですね。
ActiveSupportって名前だけ知っててよく知らなかったのでこちらも調べてみました。
Railsを構成する部品の中で、もっとも汎用的なライブラリがActiveSupportです。ActiveSupportは、いわば「素のRubyにちょっとした便利な機能を付け加える」役目を果たしています。
ほむほむ。
余談ですが、筆者の経験では、blnak?はあまりにも基本的かつ便利なため、Railsの初心者はこれをRubyの機能だと信じていたり、ところかまわず乱用する傾向があります。