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

「等値」と「等価」は Ruby では?

Ruby

これを見てどうも JavaC++ の話のように思ったのだが、最初の「『等値』と『等価』の違いを説明してください」というのがよくわからなかった。僕は Ruby しか知らない素人初心者プログラマなのだが、Ruby だとどういうことなのだろう。

ただ、Ruby でも「==」と「equal?」はちがうよね。

irb(main):001:0> a = "hoge"
=> "hoge"
irb(main):002:0> b = "hoge"
=> "hoge"
irb(main):003:0> a == b
=> true
irb(main):004:0> a.equal?(b)
=> false
irb(main):005:0> a.object_id
=> 47028457345780
irb(main):006:0> b.object_id
=> 47028458255160
irb(main):007:0> a = b = "hoge"
=> "hoge"
irb(main):008:0> a.equal?(b)
=> true

上を見ればわかると思うけれども、「==」はオブジェクトが同じ内容なら true、「equal?」は同じオブジェクトID でなければ true にならない。上の「等値」と「等価」というのは、これと関係があるのかな。

Ruby で関数型プログラミングもどき

Ruby

上のリンク先で JavaScript を使って、オブジェクト指向プログラミングと関数型プログラミングの対比をやってあったので、Ruby に移植してみました。

 

課題:
唐揚げ弁当がいくつかあるとします。それぞれ唐揚げが複数入っています。
この中からx個の唐揚げをつまみ食いするプログラムを作りましょう。
つまみ食いはバレないようにするために、
その時点で最も唐揚げ数が多いお弁当から取るという仕様にします。

http://qiita.com/stkdev/items/5c021d4e5d54d56b927c

 

オブジェクト指向で。こんな感じですかね。

class Bento
  def initialize(dish, num)
    @dish = dish
    @num = num
  end
  attr_reader :num
  
  def eat
    @num -= 1
  end
  
  def show
    puts @dish + @num.to_s + ""
  end
end

order = [Bento.new("唐揚げ", 10), 
         Bento.new("唐揚げ",  8),
         Bento.new("唐揚げ",  6)]
         
5.times do
  max_bento = order.inject(order.first) {|r, bento| r = (bento.num > r.num) ? bento : r}
  max_bento.eat
  puts "-------"
  order.each {|x| x.show}
end

結果。

-------
唐揚げ9個
唐揚げ8個
唐揚げ6個
-------
唐揚げ8個
唐揚げ8個
唐揚げ6個
-------
唐揚げ7個
唐揚げ8個
唐揚げ6個
-------
唐揚げ7個
唐揚げ7個
唐揚げ6個
-------
唐揚げ6個
唐揚げ7個
唐揚げ6個

 
 
次は関数型プログラミングっぽく。元の JavaScript コードは上と同じ答えを返さないので、多少変更してあります。

order = [{dish: "唐揚げ", num: 10},
         {dish: "唐揚げ", num:  8},
         {dish: "唐揚げ", num:  6}]
         
show = ->(d) {puts d[:dish] + d[:num].to_s + ""}

show_all = ->(data) {data.each {|x| show[x]}}

select_eating_bento = ->(data) {
  data.inject(data.first) {|r, bento| r = (bento[:num] > r[:num]) ? bento : r}
}

eating_karaage = ->(data) {
  data.map do |bento|
    if bento.equal?(select_eating_bento[data])
      {dish: bento[:dish], num: (bento[:num] - 1)}
    else
      bento
    end
  end
}

eating = ->(num, data) {
  data = eating_karaage[data]
  puts "-------"
  show_all[data]
  eating[num - 1, data] if num > 1
}

eating[5, order]

「副作用」がないようにというのが目標ですね。
 
どちらがわかりやすいでしょうか。

元記事は色いろコメントで怒られていますね…。

「10分でコーディング」やってみた(Ruby)

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"]

 
 

さらにもうひとつ(10分)

問題なし。簡単。

class CCipher
  def decode(ciphertest, shift)
    st = ""
    ciphertest.each_byte do |byte|
      nb = byte - shift
      nb += 26 if nb < 65
      st += nb.chr
    end
    st
  end
end

p CCipher.new.decode("VQREQFGT", 2)    #=>"TOPCODER"

 
リンク先のブログ主は Java でコーディングしておられるが、Rubyist からは Java は書くことが多すぎるように見える。

Python の for else って地味に便利と Rubyist が思う

Ruby Python

ので、遊びで 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

6
5
3
6
Nyao, hit!

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 で

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桁か
  a < 10 ** r and a >= 10 ** (r - 1)
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 'utils'

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 クラスでアトリビュートっぽく

Ruby

PythonJavaScript では

>>> 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

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 を使ってもできます(参照)。
 

メタプログラミングRuby 第2版

メタプログラミングRuby 第2版