坂野 正明%かっての北白川住人です。

新井さん、どうも丁寧な解説をありがとうございました。
知らなかったこと、気づかなかったことがたくさん出てきて、
大変勉強になりました。

斉藤さん、フォローを、特に make_clones() をありがとうございました。
binding を使うと、eval でうまく実現できちゃうんですねぇ。感心しました。
以下、少しだけ工夫を加えてみました。
	def make_clones( bind, names )
	  #names.split( "[, ]+" ).each do | name |
	  names.gsub(/[*&]/,'').split( "[, ]+" ).each do | name |
	   #eval( "#{name} = #{name}.clone", bind )
	    eval( "#{name} = #{name}.clone rescue nil", bind )
	  end
	end

原さん、ポインタをありがとうございます。そのスレッドを早速拝見しました。
# 議論が爆発してますね (@_@)

	http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/21267
に示されているように、「網(network)」のクラス図の構築には、現在の
仕様(それこそオブジェクト指向というべきなんですか?)が必須なんですね。
# もっとも、複雑過ぎて私の理解を超えるので、自分でそういうクラスを
# 作ることはないと思いますけど (^^;

あと、(アーカイブの)議論の中で気づいた点を二点。

 .clone は、オブジェクト自体をコピーするけれど、オブジェクトが指し示す
他のオブジェクトまで再帰的にコピーするわけではないんですね(!)。
# .clone って使った経験がほとんどなかったので気づきませんでした。

irb(main):035:0> x="ABC"
"ABC"
irb(main):036:0> x.id
67542462
irb(main):037:0> e=[x]
["ABC"]
irb(main):038:0> f=e.clone
["ABC"]
irb(main):039:0> [e.id,f.id,e[0].id,f[0].id]
[67536372, 67532862, 67542462, 67542462]
	=> x, e[0], f[0] は、"equal"

ということは、特に、多重配列とかになると、 .clone のありがたみは
ほとんどなさそうですね。

もう一点は、土岐仁謙さんの御投稿の
	http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/21302
name = name.dup
name.sub! (....)
は、なるほど、と思いました。これだと、色々考えなくていいですね。
# あくまで破壊的メソッドを使う場合の(私にとっての)一般論です。sub の
# 場合に限れば、name = name.sub(....) で十分ですし、また、あえて破壊的
# メソッドを使う必要がある場合は実はそう多くない、とも思いますが。


いずれにせよ、初級者(当然私も含みます ^^;)には、lint みたいなもので、
破壊的メソッドを使っているかどうかをちゃんと外部からチェックできれば
ありがたい、と感じます。(初級者には)ちょっと見つけにくいバグになる
でしょうから。

というわけで、jihg さん、御投稿をありがとうございます。
簡単に試してしてみたところ、いい感じですね。String, Array,
Hash について簡単なチェックができるので、実用的に使えそうです。

いくつか不完全(?)のところがあるように思ったので、以下のように
いくつか書き換えてみましたが、いかがでしょう?


○=制約
  def abc (a)
    b=a
    b.xxx!
は検出できませんね(多重代入まで考えるともっと複雑!)。

○def x (a,*b,&c) にも対応させました。

他、いくつか。


皆様への御礼を兼ねて、先ほどの Summary の改定版を準備中です。
できれば明日(もしくは週明け…)投稿します。

--------------
さかのまさあき


# jihg さんのスクリプトに対する
#-------------- patch ------------
*** lintdef.rb	Thu Feb 14 19:15:08 2002
--- lintdef.new.rb	Thu Feb 14 19:15:56 2002
***************
*** 8,11 ****
--- 8,13 ----
      line.scan(/(?:^|\s+|;)def\s+.+?\((.*?)\)/i) do |s|
        sRE = (s[0].split(/\s*,\s*/)).each do |ss|
+       	next if /^\s*\&/ =~ ss
+       	ss.sub!(/^\s*\*/,'')
          ss.gsub!(/(.+?)\s*(?:$|=.*)/){ $1 }
        end.join("|")
***************
*** 15,25 ****
              \.
              (?:
!                (?:\w+!|replace)
!               |(?:push|unshift|pop|shift|concat|clear|fill)
!               |(?:store|delete(?:_if)?|update)
              )
-             \b
            )
!           |(?:\s*(?:<<|\+=))
            |(?:\[.+?\]\s*=\s*)
          )
--- 17,29 ----
              \.
              (?:
!               (?:
!                  (?:replace)
!                 |(?:push|unshift|pop|shift|concat|clear|fill)
!                 |(?:store|delete(?:_if|_at)?|update)
!               )
!               \b|\w+!
              )
            )
!           |(?:\s*(?:<<))
            |(?:\[.+?\]\s*=\s*)
          )
***************
*** 39,43 ****
      row += 1
    end
!   print "(#{count})count."
  end
  
--- 43,47 ----
      row += 1
    end
!   puts "(#{count})count."
  end
  
#-------------- 修正されたスクリプト ------------
flnm = ARGV.shift
open(flnm ,"r") do |flh|
  row = 1
  count = 0
  re = nil
  reEnd = nil
  while line = flh.gets
    line.scan(/(?:^|\s+|;)def\s+.+?\((.*?)\)/i) do |s|
      sRE = (s[0].split(/\s*,\s*/)).each do |ss|
      	next if /^\s*\&/ =~ ss
      	ss.sub!(/^\s*\*/,'')
        ss.gsub!(/(.+?)\s*(?:$|=.*)/){ $1 }
      end.join("|")
      re = /[^\w@](#{sRE})
        (?:
          (?:
            \.
            (?:
              (?:
                 (?:replace)
                |(?:push|unshift|pop|shift|concat|clear|fill)
                |(?:store|delete(?:_if|_at)?|update)
              )
              \b|\w+!
            )
          )
          |(?:\s*(?:<<))
          |(?:\[.+?\]\s*=\s*)
        )
      /x
      sREEnd = line.scan(/\s*/)[0]
      reEnd = /^#{sREEnd}end\b"/
    end
    unless re.nil?
      line.scan(re) do |sArg|
        print "warning : #{flnm}(#{row})---" +
          " 引数 #{sArg} に対し破壊的な操作をしています。" +
          "'#{line.scan(/(.*?)(?:\r\n|\r|\n)/)}'\n"
        count += 1
      end
      re = nil if reEnd =~ line
    end
    row += 1
  end
  puts "(#{count})count."
end

#Version 2002.2.13 -> 14?
#-------------- ここまで ------------