RubyでRepeated Substringを解く-CodeEval
バグ入りで80点^^;
入出力情報少なすぎてバグ特定できない..
まあいっか
str.gsub(substr).to_aという技を閃いた
文字列中に繰り返し現れる最長の部分文字列
RubyのEnumerable#mapをもっと便利にしたいよ
次のような名前のリストがあって
langs = ["ruby", "python", "lisp", "haskell"]
名前の先頭を大文字にするとしたら
君ならどうする?
そう普通Enumerable#mapを使って
次のようにするよね
langs = ["ruby", "python", "lisp", "haskell"] langs.map { |lang| lang.capitalize } # => ["Ruby", "Python", "Lisp", "Haskell"]
Enumerable#mapってほんと死ぬほど便利だよ
僕はRubyの魅力の80%は
mapが占めてるんじゃないかって
たまに感じることがあるよ.. :)
でもただ先の例で
大文字にするだけなのにブロックって
ちょっと大げさすぎると思わない?
もちろんそうなんだよ
そう思うRubyistが沢山いたから
mapは次のように書けるようにもなってるんだ
langs = ["ruby", "python", "lisp", "haskell"] langs.map(&:capitalize) # => ["Ruby", "Python", "Lisp", "Haskell"]
シンボルに&を付けてmapに渡すと
暗黙的にSymbol#to_procが呼ばれて
そこで配列の各要素にcapitalizeが適用されるよ
この記法のお陰でmapは一層使い勝手が良くなってるよ
じゃあ今度は先の言語名のリストから
その「言語使いのリスト」に変換してほしいんだけど..
つまり言語名の後に'ist'を付けてほしいんだ*1
そう次のようにするよね
langs = ["ruby", "python", "lisp", "haskell"].map(&:capitalize) langs.map { |lang| lang + 'ist' } # => ["Rubyist", "Pythonist", "Lispist", "Haskellist"]
これって悔しいよね
'ist'を足すだけなのに
またブロックを書かなきゃいけない
同じように次のような場合も
ブロックを書かなきゃいけない
[1, 2, 3].map { |i| i + 10 } # => [11, 12, 13] (1..5).map { |i| i**2 } # => [1, 4, 9, 16, 25] [[1,2,3,4], [5,6,7,8], [9,10,11,12]].map { |arr| arr.last(2) } # => [[3, 4], [7, 8], [11, 12]] ["ruby", "python", "lisp", "haskell"].map { |lang| lang[-2, 2] } # => ["by", "on", "sp", "ll"]
僕はすごく悔しいよ..
そんなわけで..
Enumerable#mappを考えたよ!
module Enumerable def mapp(op=nil, *args, &blk) op ? map { |e| op.intern.to_proc[e, *args]} : map(&blk) end end langs.mapp(:+, 'ist') # => ["Rubyist", "Pythonist", "Lispist", "Haskellist"]
つまりmappでは引数にメソッドと
その引数が取れるんだ
[1, 2, 3].mapp(:+, 10) # => [11, 12, 13] (1..5).mapp(:**, 2) # => [1, 4, 9, 16, 25] [[1,2,3,4], [5,6,7,8], [9,10,11,12]].mapp(:last, 2) # => [[3, 4], [7, 8], [11, 12]] ["ruby", "python", "lisp", "haskell"].mapp(:[], -2, 2) # => ["by", "on", "sp", "ll"]
ブロックを渡せばmapに処理を投げるから
mapの代わりとしても使えるよ
[1, 2, 3].mapp { |i| i + 10 } # => [11, 12, 13] (1..5).mapp { |i| i**2 } # => [1, 4, 9, 16, 25] [[1,2,3,4], [5,6,7,8], [9,10,11,12]].mapp { |arr| arr.last(2) } # => [[3, 4], [7, 8], [11, 12]] ["ruby", "python", "lisp", "haskell"].mapp { |lang| lang[-2, 2] } # => ["by", "on", "sp", "ll"]
誰でも考えそうだから
既出のアイディアだったらゴメンね
って
もっと高度なことをまめさんがしてました^^;
map が面倒なので DelegateMap - まめめも
まあ折角書いたから..
ゴメンナサイm(__)m
関連記事:RubyのSymbol#to_procを考えた人になってみる - hp12c
(追記:2012-2-14)Twitterを通してすごい荒業を知ったよ*2
[1,2,3].map(&1.method(:+)) #=> [2,3,4]
スゴイね!
でもメソッド呼び出しのオブジェクトが
引数と入れ替わっちゃってるから:+, :*くらいしか
用途がなさそうだけどね..
RubyでClosest Pairを解く-CodeEval
Enumerable#chunkでsetを分けて
Array#combinationで
与えられた複数の座標間の最短を求める
RubyでTelephone Wordsを解く-CodeEval
Array#productを使って^^;
与えられた7桁の電話番号に対する
各数字に割り当てられたアルファベットでの全組み合わせ
RubyでFizzBuzz問題に終止符を打つ!
FizzBuzz問題はプログラマーが最初に出会う問題だよ
FizzBuzzの解法はプログラマーの数ほどあると言われているよ
でもいつまでもFizzBuzz問題に関わってたら
本当に解決しなければならない問題を解決できないよ
だから僕がFizzBuzz問題に終止符を打つよ!
つまり決定版というべき
FizzBuzzオブジェクトが一つあれば
もうみんながFizzBuzz問題に
頭を悩ませなくても済むはずなんだ
さあ!
class FB module Core def fb ->base,n{ (n%base).zero? }.curry end end {FB:15, F:3, B:5}.each do |name, base| k = Class.new do extend Core define_singleton_method(:===) do |n| fb[base, n] end end const_set(name, k) end extend Core end def fizzbuzz(n) case n when FB::FB; :FizzBuzz when FB::F; :Fizz when FB::B; :Buzz else n end end (1..100).map { |i| fizzbuzz i } # => [1, 2, :Fizz, 4, :Buzz, :Fizz, 7, 8, :Fizz, :Buzz, 11, :Fizz, 13, 14, :FizzBuzz, 16, 17, :Fizz, 19, :Buzz, :Fizz, 22, 23, :Fizz, :Buzz, 26, :Fizz, 28, 29, :FizzBuzz, 31, 32, :Fizz, 34, :Buzz, :Fizz, 37, 38, :Fizz, :Buzz, 41, :Fizz, 43, 44, :FizzBuzz, 46, 47, :Fizz, 49, :Buzz, :Fizz, 52, 53, :Fizz, :Buzz, 56, :Fizz, 58, 59, :FizzBuzz, 61, 62, :Fizz, 64, :Buzz, :Fizz, 67, 68, :Fizz, :Buzz, 71, :Fizz, 73, 74, :FizzBuzz, 76, 77, :Fizz, 79, :Buzz, :Fizz, 82, 83, :Fizz, :Buzz, 86, :Fizz, 88, 89, :FizzBuzz, 91, 92, :Fizz, 94, :Buzz, :Fizz, 97, 98, :Fizz, :Buzz]
FB::FBクラスはfizzbuzzを
FB::Fクラスはfizzを
FB::Bクラスはbuzzをそれぞれ判定するクラスオブジェクトだよ
各クラスは===クラスメソッドでfizzbuzzの判定をするから
case式における比較判定にそのまま使えるんだ
またFBクラスのfbクラスメソッドは
次のように使えるよ
fizzbuzz = FB.fb[15] fizz = FB.fb[3] fizzbuzz[15] # => true fizzbuzz[16] # => false fizz[6] # => true fizz[7] # => false
また一つFizzBuzzを増やしただけだった..
ゴメンナサイm(__)m
第2弾!知って得する12のRubyのトリビアな記法 ~ 12 Trivia Notations you should know in Ruby
ブログを下記に移転しました。デザイン変更により移転先では記事が一層読みやすくなっていますので、よろしければ移動をお願い致します。
第2弾!知って得する12のRubyのトリビアな記法 melborne.github.com
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
半年くらい前にちょっとトリビアだけど
知っていると意外と便利なRubyの記法を21個紹介したよ
知って得する21のRubyのトリビアな記法 ~ 21 Trivia Notations you should know in Ruby - hp12c
今回はその第2弾だよ!
ちょっと数が少ないけど
知らないものがあったらへーとかほーとか
得したとか言ってもらえるとうれしいよ
1.Enumerator#with_index
任意のリストを標準出力するときに
連番を同時に振るとしたら
普通はEnumerable#each_with_indexを使うよね
names = Module.constants.take(10) names.each_with_index { |name, i| puts "%d: %s" % [i+1, name] } # => [:Object, :Module, :Class, :Kernel, :NilClass, :NIL, :Data, :TrueClass, :TRUE, :FalseClass] # >> 1: Object # >> 2: Module # >> 3: Class # >> 4: Kernel # >> 5: NilClass # >> 6: NIL # >> 7: Data # >> 8: TrueClass # >> 9: TRUE # >> 10: FalseClass
でも'i+1'ってのがイマイチ..って人いる?
そんな人にはEnumerator#with_indexがあるよ
names = Module.constants.take(10) names.each.with_index(1) { |name, i| puts "%d: %s" % [i, name] } # => [:Object, :Module, :Class, :Kernel, :NilClass, :NIL, :Data, :TrueClass, :TRUE, :FalseClass] # >> 1: Object # >> 2: Module # >> 3: Class # >> 4: Kernel # >> 5: NilClass # >> 6: NIL # >> 7: Data # >> 8: TrueClass # >> 9: TRUE # >> 10: FalseClass
with_indexはindexのoffsetを引数に取れるよ
comparableなオブジェクトが
取れたらもっとよかったのにね
2.Integer#times
timesは処理を特定回数だけ
繰り返したいときに使うよね
you_said = 'てぶくろ' 6.times { puts you_said.reverse! } # => 6 # >> ろくぶて # >> てぶくろ # >> ろくぶて # >> てぶくろ # >> ろくぶて # >> てぶくろ
timesはブロックを渡さないとEnumeratorを返すよ
だから
複数のオブジェクトを生成するようなことにも使えるんだよ
20個のRGBカラーサンプルを作ってみるよ
20.times.map { [rand(256), rand(256), rand(256)] } # => [[45, 190, 194], [94, 43, 125], [6, 104, 181], [144, 92, 114], [34, 161, 214], [96, 69, 241], [216, 246, 133], [6, 237, 131], [194, 95, 214], [177, 252, 202], [184, 149, 142], [184, 166, 45], [41, 108, 115], [176, 100, 138], [124, 213, 89], [173, 123, 34], [137, 31, 47], [54, 92, 186], [118, 239, 217], [150, 184, 240]]
3.String#succ / Integer#succ
ExcelのAから始まる横のラベルを
作りたいんだけどどうする?って問題が最近あったよ
RubyにはString#succまたはnext
があるからこれは簡単だよ
col = '@' 60.times.map { col = col.succ } # => ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "AA", "AB", "AC", "AD", "AE", "AF", "AG", "AH", "AI", "AJ", "AK", "AL", "AM", "AN", "AO", "AP", "AQ", "AR", "AS", "AT", "AU", "AV", "AW", "AX", "AY", "AZ", "BA", "BB", "BC", "BD", "BE", "BF", "BG", "BH"]
4.Comparable.between?
値が一定範囲内にあるかどうかで
処理を切り分けたいことってあるよね?
たぶん普通はこうするよ
pos = 48 status = if 0 <= pos && pos <= 50 :you_are_in else :you_are_out end status # => :you_are_in
でCoffeeScriptを見て悔しがるんだよね
でも安心していいよ
Rubyにはbetween?があるんだから
pos = 48 status = if pos.between?(0, 50) :you_are_in else :you_are_out end status # => :you_are_in pos = 'D' grade = if pos.between?('A', 'C') :you_are_good! else :try_again! end grade # => :try_again!
もっとも僕はcase派ですが..
pos = 48 status = case pos when 0..50 :you_are_in else :you_are_out end status # => :you_are_in
5.Array#uniq
配列の全要素が同じかどうかを
調べたいときはどうする?
そんなときはArray#uniqが使えるよ
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1].uniq.size==1 # => true [1, 1, 1, 1, 1, 1, 1, 2, 1, 1].uniq.size==1 # => false %w(street retest setter tester).uniq { |w| w.chars.sort }.size==1 # => true
6.BasicObject#instance_eval
instance_evalはオブジェクトの生成を
DSL風にするときに良く使われているよね
class Person def initialize(&blk) instance_eval(&blk) end def name(name) @name = name end def age(age) @age = age end def job(job) @job = job end def profile [@name, @age, @job] * '-' end end t = Person.new do name 'Charlie' age 13 job :programmer end t.profile # => "Charlie-13-programmer"
でも
このコンテキストを一時的に切り替えるって機能は
DSL以外でも便利に使えるんだ
例えばテストの結果の平均値を求めてみるよ
普通はこう書くよね
scores = [56, 87, 49, 75, 90, 63, 65] scores.inject(:+) / scores.size # => 69
短いコードで変数scoresが3回も..
instance_evalを使うと
scoreを消すことができるんだ
[56, 87, 49, 75, 90, 63, 65].instance_eval { inject(:+) / size } # => 69
さらに標準偏差sdを求めるよ
まず普通に
scores = [56, 87, 49, 75, 90, 63, 65] avg = scores.inject(:+) / scores.size sigmas = scores.map { |n| (avg - n)**2 } sd = Math.sqrt(sigmas.inject(:+) / scores.size) # => 14.247806848775006
instance_evalで
scores = [56, 87, 49, 75, 90, 63, 65] sd = scores.instance_eval do avg = inject(:+) / size sigmas = map { |n| (avg - n)**2 } Math.sqrt(sigmas.inject(:+) / size) end sd # => 14.247806848775006
中間的な変数をブロック内に閉じ込められるし
ブロックで式がまとまって見やすくない?
7.Array#first/last
Array#first/lastは個数の引数を取れるよ
[*1..100].instance_eval { first(5) + last(5) } # => [1, 2, 3, 4, 5, 96, 97, 98, 99, 100]
8.正規表現:名前付き参照
正規表現中で()を使うと部分マッチを捕捉できるよね
でそれに名前を付けたいときは?
langs = "python lisp ruby haskell erlang scala" m = langs.match(/(?<lang>\w+)/) # => #<MatchData "python" lang:"python"> m['lang'] # => "python"
で正規表現リテラルを左辺にした場合は
これをローカル変数として持ち出せるんだよ
langs = "python lisp ruby haskell erlang scala" if /(?<most_fun_lang>r\w+)/ =~ langs printf "you should learn %s!", most_fun_lang end # >> you should learn ruby!
9.正規表現:POSIXブラケット
Ruby1.9では\wは日本語にマッチしなくなったよね
だから1.9で日本語にもマッチさせたいときは
POSIXブラケットでwordを使うといいかもね
need_japanese = "this-日本語*is*_really_/\\変わってる!" need_japanese.scan(/\w+/) # => ["this", "is", "_really_"] need_japanese.scan(/[[:word:]]+/) # => ["this", "日本語", "is", "_really_", "変わってる"]
10.String#match
もう一つ正規表現を^^;
String#matchはMatchDataオブジェクトを返すから
次のように使えるよね
date = "2012february14" m = date.match(/\D+/) mon, day, year = m.to_s.capitalize, m.post_match, m.pre_match "#{mon} #{day}, #{year}" # => "February 14, 2012"
でもmatchはブロックを取れるので
次のようにしてもいいよ
date = "2012february14" mon, day, year = date.match(/\D+/) { |m| [m.to_s.capitalize, m.post_match, m.pre_match] } "#{mon} #{day}, #{year}" # => "February 14, 2012"
11.String#unpack
数字列を決まった長さ基準で区切りたいときはどうする?
正規表現を使うのかな
a_day = '20120214' a_day.match(/(.{4})(.{2})(.{2})/).captures # => ["2012", "02", "14"]
String#unpackを使うともっと簡単かもね*1
a_day = '20120214' a_day.unpack('A4A2A2') # => ["2012", "02", "14"]
12.DATA.rewind
DATAは__END__以降をFileとしたオブジェクトだよ
だからrewindクラスメソッドが使えるんだけど
これは__END__の最初の行に戻るんじゃなくて
ファイルのトップに戻るんだよ
だからこれを使えば
なんちゃってQuineができるんだ
#!/usr/bin/env ruby require "g" def evaluate(str) op = %w(\+ \* \/) digit = /-*\d+/ if m = str.match(/(#{op})\s+(#{digit})\s+(#{digit})/) op, a, b = m.captures inner = a.to_i.send(op, b.to_i) str = m.pre_match + inner.to_s + m.post_match evaluate(str) else str end end g evaluate("+ * 3 4 5") DATA.rewind puts DATA.to_a __END__
このコードを実行すると
evaluateの結果がgrowl出力されると共に
このコード自身が標準出力されるよ
今回はこれで終わりだよ
ほんとは21個溜めてから出したかったんだけど
今朝Peter Cooperさんのビデオを見てたら
同じトリビアが出てたから慌てて出してるんだよ^^;