From: Stefano Crocco Date: 2009-12-26T06:14:14+09:00 Subject: Re: Making a counter for each word's occurrences in a string On Friday 25 December 2009, Ben Ben wrote: > |By looking at this method below, I couldn't understand a few things. > |Let me list the method first! > | > |1) def count_frequency(word_list) > |2) counts = Hash.new(0) > |3) for word in word_list > |4) counts[word] += 1 > |5) end > |6) counts > |7) end > | > |p count_frequency(["sparky", "the", "cat", "sat", "on", "the", "mat"]) > | > |---- > |that code above produces {"sparky" => 1, "the" => 2, "cat"=>1, "on"=>1, > |"mat"=>1} > | > |What I don't understand is that why line 6 is there. OK, like 4, it > |seems counts[word] combines to pin point which word and add 1 to the > |counter of that word, right? Because I'm confuse how ruby works as it's > |so compact and nice, and under the hood I have no idea. So line 2 is to > |create an empty hash with 0 item and deposits as counts (object)? Using > |for loop to go through each word in word_list, adding 1 to counts[word], > |but I thought counts[word] produces a position of a word and not a > |counter. OK, you can say I'm completely confused about the whole code > |above. > | > |Help? > | > |Thanks in advance. Line two creates an empty hash which uses 0 as default value. This means that if you ask the value of a key which doesn't exist, it'll return 0. If this default value hadn't been specified (that is, if that line had been counts = Hash.new or, equivalently, counts = {} ), the defalut value would have been nil, instead. Inside the for loop (that is, for each word in the list) the value associated to the word inside the hash is increased by one (here you see the usefulness of setting the default value of the hash to 0: if we hadn't done this, every time we should have checked whether the entry was a number or nil). This has nothing to do with the position of the word. Each value in the hash is simply the number of times the corresponding word has been seen in the word list. For example, supppose the word list is: ['a', 'b', 'a', 'c', 'b', 'a'] Here's how the hash (which is initially empty) becomes in the various iterations: First iteration (word: 'a'): {'a' => 1} Second iteration (word: 'b'): {'a' => 1, 'b' => 1} Third iteration (word: 'a'): {'a' => 2, 'b' => 1} Fourth iteration (word: 'c'): {'a' => 2, 'b' => 1, 'c' => 1} Fifth iteration (word: 'b'): {'a' => 2, 'b' => 2, 'c' => 1} Sixth iteration (word: 'a'): {'a' => 3, 'b' => 2, 'c' => 1} The last line of the method, count, is there because in ruby a method returns the value returned by the last expression (unless the "return" keyword is used). Without that last line, the last expression would be the "for" loop, which returns the object we're iterating on (in this case, the word list). We're not interested in returning the word list, however: we need the word count, which is stored in the variable count. Putting a line containing only the name of the variable at the end of the method, we make sure that the value contained in the variable is returned. If it's clearer to you, you can think the last line as if it were: return count In this case, the return keyword has no effect, since we're already at the end of the method. However, it may make clearer the meaning of the expression. (The return keyword more or less means: don't go on executing the method, stop immediately and return to the calling method the value given to return, or nil if return is called without arguments). I hope this helps Stefano