From: Alex Shulgin Date: 2007-09-18T03:20:04+09:00 Subject: Re: Modifying a hash key On Sep 17, 12:53 pm, "Robert Klemme" wrote: > > > So the real answer to my question (what is the reason for hash to > > store it's keys by reference?) might be: trading rule of a thumb for > > speed and memory? > > No, the reason is that you can only store by reference in Ruby (there > are some internal optimizations for Fixnum and the like but the code > still basically behaves the same). Yes, but hash implementation could make a copy of the key when it inserts new elements. Of course, this will slow down the code in significant part of programs, so there is the trade-off I've talked about. :-) > > state={} > > prefix=[] > > while $stdin.eof? > > Are you serious about the line above? I'd rather have expected "until" there. Sorry, of course not. It was pulled off the top of my head, since no real code was at hand (I've posted that from work, and the Ruby code is something I keep at home (-: ). > I'd do this, note all the freezing in order to make errors with > changing keys obvious. > > state = Hash.new {|h,k| h[k]=[]} > prefix = [].freeze > > ARGF.each do |line| > line.scan /\w+/ do |word| > state[prefix] << word.freeze > (prefix += [word]).freeze > # or: prefix = (prefix.dup << word).freeze > end > end Uh-oh... this freeze stuff seems overly complicated to me. > Btw, there's probably a more efficient way of storing this if you > introduce a specialized class for prefix chaining. Probably like > this: > > Prefix = Struct.new :word, :previous > > > w=... # read a word > > state[prefix] << w > > ... > > prefix << w # <-- Bang! The hash key is changed... :-) > > You can #dup the prefix or use +: > prefix += [w] My real code is as follows: require 'scanf' NPREFIX = 2 $nwords = ARGV[0] ? ARGV[0].to_i() : 1000 # # acquire knowledge # state = {} prefix = [] while not $stdin.eof? do # w, = scanf("%s") words = $stdin.gets().scan(/[^\s]+/) words.each do |w| suf = state[prefix] if not suf suf = state[prefix.clone()] = [] end suf << w if prefix.length >= NPREFIX prefix.shift end prefix << w end end state[prefix] = [] # # generate pseudo-text # prefix = [] count = 0 while count < $nwords do suf = state[prefix] if suf.empty? break end w = suf[rand(suf.length)] print w + " " if prefix.length >= NPREFIX prefix.shift end prefix << w count += 1 end puts May be an eye of experienced programmer could catch some more odd places in my code? See, I'm just a Ruby newbie... Please do not waste more of your time than really necessary on this. :-) Cheers, Alex