けいじゅ@日本ラショナルソフトウェアです.

In [ruby-list :07035 ] the message: "[ruby-list:7035] Re: modulo ", on
Mar/10 05:27(JST) toyofuku / juice.or.jp writes:

>  豊福@パパイヤです。

>  mathn.rb で / を再定義している路線をねらったん
>ですが。

うーん. あれは, / だけですからね... ほとんどの場合, rubyも問題なく動作
するでしょう(^^'''

>> # いや, 無理があるというのと違うかな, 目的が剰余系なrubyを作りたいとい
>> # うなら別ですが...
>
>  mathn.rb で分数電卓の気分で使えたように剰余系
>電卓の気分で使えるようなものができないかというのが
>目的です。

うーん. なるほど....

そうするとかなりうらわざを使う必要が出てきますね...

こんなのはどうでしょう?

--
$mod = 0

def modulo(n)
  $mod = n.abs
end

class Fixnum
  alias no_pl +
  alias no_mn -
  alias no_ml *
  alias no_dv /

  def mo_pl (other); super; end
  def mo_mu (other); super; end
  def mo_ml (other); super; end
  def mo_dv (other); super; end

  alias + mo_pl
  alias - mo_mu
  alias * mo_ml
  alias / mo_dv

  def mo_divmod(other)
    normal do
      x = self / other
      y = self - x * other
      return x, y
    end
  end
  alias divmod mo_divmod
end

class Bignum
  alias no_pl +
  alias no_mn -
  alias no_ml *
  alias no_dv /

  def mo_pl (other); super; end
  def mo_mu (other); super; end
  def mo_ml (other); super; end
  def mo_dv (other); super; end

  alias + mo_pl
  alias - mo_mu
  alias * mo_ml
  alias / mo_dv

  def mo_divmod(other)
    normal do
      x = self / other
      y = self - x * other
      #y = self.mn(x.ml(other))
      return x, y
    end
  end
  alias divmod mo_divmod
end

class Integer
  def normal
    for t in [Fixnum, Bignum]
      eval %Q[
	class #{t}
	  alias + no_pl
	  alias - no_mn
	  alias * no_ml
	  alias / no_dv
	end
      ], TOPLEVEL_BINDING
    end
    begin
      return yield
    ensure
      for t in [Fixnum, Bignum]
	eval %Q[
	  class #{t}
	    alias + mo_pl
	    alias - mo_mn
	    alias * mo_ml
	    alias / mo_dv
	  end
	], TOPLEVEL_BINDING
      end
    end
  end

  def mod
    if ($mod == 0)
      return self
    else
      void, x = self.divmod($mod)
      normal{x = x + $mod} if (x < 0)
      return x
    end
  end

  def inv
    x = self
    y = $mod
    klist = []
    void, a = x.divmod(y)
    normal{a = a + y} if (a < 0)
    b = y
    while b != 0
      k, a = a.divmod(b)
      klist.push(k)
      a, b = b, a
    end
    klist.pop

    normal do
      a, b = 1, 0
      while (k = klist.pop)
	a, b = k * a + b, a
      end
      if (x * b - y * a < 0)
	b = y - b
      end
      return b
    end
  end

  def mo_pl(other)
    normal{self + other}.mod
  end

  def mo_mn(other)
    normal{self - other}.mod
  end

  def mo_ml(other)
    normal{self + other}.mod
  end

  def mo_dv(other)
    normal do
      if ($mod == 0)
	self / other
      else
	(self * other.inv).mod
      end
    end
  end
end

---
# 大体動作しますがバグ付きです(^^;;;
# あと, ruby-1.1b9でないと動作しません.

味噌はイテレータnormalです.

  normal{exp}

の中では通常の計算が可能になります.

# ただし, 中でaliasしているのでマルチスレッド対応ではありません.

でも, これではforの問題は解決していませんので

  modulo(n) {exp}

でそのイテレータの中だけ剰余系にするというほうが良いかも知れません.

そうすると, 

  1. $modの様な大域変数なしにできる
  2. forも無理なく使えると思う

  def fib(n);
    a, b = 1, 1;
    for i in 1..n do; modulo(3) {print "#{b} "; a, b = (a+b)*1, a} end;
    print "\n";
  end;

まあ, プログラムの中にmoduloイテレータが入ってしまいますが...


>> ># 剰余系での演算
>> ># 合成数のケースはちょっと面倒なので後まわし
>> 合成数って何でしたっけ?
>
>  えっ・・・、素数でも1でもない自然数のことです・・・。
>前に書いたプログラムは 4 / 6 mod 10 みたいなやつに
>対応してません。

そうでしたっけ? すっかり忘れたなあ...

__
................................石塚 圭樹@日本ラショナルソフトェア...
----------------------------------->> e-mail: keiju / rational.com <<---