「10分でコーディング」やってみた(Ruby)
たぶん10分以内にできたと思う。
問題は、num_player 人のプレーヤーに deck で与えられたカードを切るというもの。ただし、全員に同じ枚数だけ配らないといけない。
class Cards def deal(num_players, deck) ar = Array.new(num_players) {""} (deck.length / num_players).times do |i| num_players.times {|j| ar[j] += deck[i * num_players + j]} end ar end end p Cards.new.deal(4, "123123123") #=>["12", "23", "31", "12"] p Cards.new.deal(6, "01234") #=>["", "", "", "", "", ""]
もう一問やってみた(15分)
問題なし。簡単。
class ReportAccess def who_can_see(user_names, allowed_data, report_data) ar = [] user_names.each_with_index do |uname, i| ad = allowed_data[i].split(" ") catch(:exit) do report_data.each {|d| throw(:exit) unless ad.include?(d)} ar << uname end end ar end end ra = ReportAccess.new p ra.who_can_see(["joe", "nick", "ted"], ["clients products", "products orders", "clients orders"], ["clients", "products"]) #=>["joe"]
Python の for else って地味に便利と Rubyist が思う
ので、遊びで Ruby で実装してみました。each_with_proc(pc, *args) で、ふつうの each のループが終ったあとに proc オブジェクト pc が pc.call(*args) されます。ブロックから break で抜けた場合は proc は呼ばれません。
module Enumerable def each_with_proc(pc, *args) each {|i| yield(i)} pc.call(*args) end end ar = [] 4.times {ar << rand(10)} pc = proc {puts "Nyao, hit!"} ar.each_with_proc(pc) do |i| puts i break if i < 3 end
結果例。
7 8 1 # break で抜けたので proc は呼ばれない
6 5 3 6 Nyao, hit! # proc は呼ばれる
proc を先に指定しないといけないのが使いにくいですかね。
実際は throw 〜 catch するか、lambda と return を使ってこんな風にするか。
ar = [] 4.times {ar << rand(10)} ->{ ar.each do |i| puts i return if i < 3 end puts "Nyao, hit!" }.call
あんまりスッキリしていないですね。わかりにくい。
「孤独の7」を Ruby で
rscの日記さんのところで、「孤独の7」という虫喰い算を知りました。問題は右の画像のとおりです。ここで回答を募集していたようです。
Ruby で解きました。かかった時間はほぼ 0.1秒です(答えの uniqueness のチェックなし。チェックすると 0.7秒程度)。Ruby を使ってだから、まあまあなのではないでしょうか。
lonely7.rb
def main_loop 112.upto(143) {|i| @n = i; yield} end def num_loop(r) 1.upto(9) do |i| yield(@n * i) if check(@n * i, r) end end def check(a, r) #aはr桁か 10 ** (r - 1) <= a and a < 10 ** r end 1000.upto(9999) do |x1| main_loop do num_loop(4) do |i1| next unless check(a1 = x1 - i1, 2) 0.upto(9) {|x2| next unless check(a2 = a1 * 10 + x2 - @n * 7, 3) num_loop(3) do |i2| 0.upto(9) {|x3| next unless check(a3 = a2 * 10 + x3 - i2, 2) 0.upto(9) {|x4| next unless a3 * 10 + x4 < @n 0.upto(9) {|x5| num_loop(4) do |i3| if a3 * 100 + x4 * 10 + x5 == i3 num = x1 * 10000 + x2 * 1000 + x3 * 100 + x4 * 10 + x5 puts "#{num} / #{@n} = #{num / @n}" exit end end } } } end } end end end #=>"12128316 / 124 = 97809"
単純な総当りではないです。特に 112.upto(143)
と割る数を絞っているのは、7を掛けて 3桁になるというのと、1桁の数を掛けて 4桁になり得ることを使っています。また、桁数が指定できるところはすべて桁数のチェックをしています。メソッド num_loop(r)
は Ruby お得意のブロックを使って、(桁数チェックの入った)カスタムの制御構造を作り出しています。
ちなみにマシン・スペックは Core i5-4210U 1.70GHz×2。
追記
rsc さんの Python のコードを Ruby に移植してみました。上手いですねえ。僕のコードよりずっといいですね(見てもらえばわかりますが、僕と発想が真逆です)。実行時間はほぼ 0.7秒です。
require 'kaki/utils/nest_loop' def digit(n) n.zero? ? 0 : n.to_s.length end def get_num(x, n) (x / n) % 10 end [10, 10, 10].nest_loop do |a, b, c| next if a.zero? (100...200).each do |dvs| quo = 10000 * a + 7000 + 100 * b + c dvd = quo * dvs next unless digit(dvs * a) == 4 next unless digit(dvs * 7) == 3 next unless digit(dvs * b) == 3 next unless digit(dvs * c) == 4 tmp = [0, 0, 0, 0, 0] k = [2, 3, 2, 3, 0] rem = dvd / 100000 catch(:exit) do 5.times do |i| tmp[i] = rem * 10 + get_num(dvd, 10 ** (4 - i)) rem = tmp[i] % dvs throw(:exit) unless digit(rem) == k[i] end puts "#{dvd} / #{dvs} = #{quo}" end end end
なお、手製のライブラリのメソッド(nest_loop)を使っています(参照)。
Ruby の OpenStruct クラスでアトリビュートっぽく
Python や JavaScript では
>>> class Country: ... pass ... >>> japan = Country() >>> japan.capital = "Tokyo" >>> japan.capital 'Tokyo'
みたいな書き方ができるのだけれど(いわゆる「アトリビュート」)、Ruby ではこういう書き方はないのかなと思っていました。
けれども標準添付ライブラリの OpenStruct クラスで簡単にできるのですね。
irb(main):001:0> require 'ostruct' => true irb(main):002:0> class Country < OpenStruct irb(main):003:1> end => nil irb(main):004:0> japan = Country.new => #<Country> irb(main):005:0> japan.capital = "Tokyo" => "Tokyo" irb(main):006:0> japan.capital => "Tokyo"
なるほど、これは便利。
Ruby でクラスメソッドを include する
Ruby でモジュールを include してクラスメソッドを作りたいとします。そのとき、
module Utility def self.output(str) puts "output: #{str}" end end class A include Utility end A.output("Hello!") #=>undefined method `output' for A:Class (NoMethodError)
はうまくいきません。
ではどうするかというと、こうします。
module Utility def output(str) puts "output: #{str}" end end class A class << self include Utility end end A.output("Hello!") #=>"output: Hello!"
これはうまくいきます。class << self は特異クラスをオープンします。これはじつは self がクラスA だからで、これがインスタンスならば同様に特異メソッドがオープンされるのです。
module Utility def output(str) puts "output: #{str}" end end obj = Object.new class << obj include Utility end obj.output("Hello!") #=>"output: Hello!"
なお、同じことは Object#extend を使ってもできます(参照)。
- 作者: Paolo Perrotta,角征典
- 出版社/メーカー: オライリージャパン
- 発売日: 2015/10/10
- メディア: 大型本
- この商品を含むブログ (2件) を見る
Ruby のメソッド間で変数を共有する
Ruby のメソッド間では、ふつうは変数は共有されません。
def counter_set x = 0 end def inc x += 1 end def counter_value x end counter_set inc inc counter_value
は最初の inc 呼び出しのところで
undefined method `+' for nil:NilClass (NoMethodError)
のエラーが出ます。これは Ruby の仕様で、却ってメソッドは安全ともいえるわけです。もし変数を共有したければ、メソッドではなく、クロージャである proc を使えば事足ります。
x = 0 inc = proc do x += 1 end inc.call inc.call x #=>2
という具合に。
しかし、これでは「イヤだ」、メソッドを使いたいと仰るわがままな方もおられましょう。これはじつは、Ruby のメタプログラミングを使えば可能です。
def counter_set x = 0 define_method(:inc) do x += 1 end define_method(:counter_value) do x end end counter_set inc inc counter_value #=>2
こんな具合ですね。Module#define_method のメソッド定義がブロック(Ruby のブロックはクロージャです)で行われているために、変数 x が透過して保持されます。
同様のことは、メソッドだけでなくクラスやモジュールでも可能です。
- 作者: Paolo Perrotta,角征典
- 出版社/メーカー: オライリージャパン
- 発売日: 2015/10/10
- メディア: 大型本
- この商品を含むブログ (2件) を見る
なお、上のはなんちゃってメソッドですが、もし冗談にせよ使うなら、こんな感じの方がいいかも知れません。
module Kernel def counter_set x = 0 Kernel.send(:define_method, :inc) do x += 1 end Kernel.send(:define_method, :counter_value) do x end end end
これなら、クラスの中でなど、どこでも使えます。例えばこんな感じ。
counter_set class A inc def initialize inc end end counter_value #=>1 A.new counter_value #=>2
GTK+ で落書き 7(Ruby)
Gem 'oekaki' で落書きです。
oekaki | RubyGems.org | your community gem host
GTK+でお絵かきしてみた(Ruby) - Camera Obscura
引き続き Tool#star を使っています。
require 'oekaki' Oekaki.app do draw do color(0, 0, 0) rectangle(true, 0, 0, 300, 300) end po = Vector[0, 150] θ = PI / 50 a = Matrix[[cos(θ), -sin(θ)], [sin(θ), cos(θ)]] timer(20) do color(0, 0, 0) rectangle(true, 0, 0, 300, 300) color(0, 65535, 0) star(false, 150, 150, 150 + po[0], 150 - po[1]) po = a * po end end