In an attempt to answer this question: How can I make the set difference insensitive to case?, I was experimenting with sets and strings, trying to have a case-insensitive set of strings. But for some reason when I reopen String class, none of my custom methods are invoked when I add a string to a set. In the code below I see no output, but I expected at least one of the operators that I overloaded to be invoked. Why is this?
EDIT: If I create a custom class, say, String2, where I define a hash method, etc, these methods do get called when I add my object to a set. Why not String?
require 'set'
class String
alias :compare_orig :<=>
def <=> v
p '<=>'
downcase.compare_orig v.downcase
end
alias :eql_orig :eql?
def eql? v
p 'eql?'
eql_orig v
end
alias :hash_orig :hash
def hash
p 'hash'
downcase.hash_orig
end
end
Set.new << 'a'
Looking at the source code for Set, it uses a simple hash for storage:
def add(o)
#hash[o] = true
self
end
So it looks like what you need to do instead of opening String is open Set. I haven't tested this, but it should give you the right idea:
class MySet < Set
def add(o)
if o.is_a?(String)
#hash[o.downcase] = true
else
#hash[o] = true
end
self
end
end
Edit
As noted in the comments, this can be implemented in a much simpler way:
class MySet < Set
def add(o)
super(o.is_a?(String) ? o.downcase : o)
end
end
Related
In continuation of another question I also have the following question.
I have a class which has a very central instance variable called var. It is so central to the class that when I print an object of the class, it should just print the instance variable.
When I compare that same object with a string which matches the instance variable, I would like it to return true, but that doesn't always work in the implementation below:
class MyClass
attr_accessor :var
def initialize(v)
#var = v
end
def to_s
#var.to_s
end
def inspect
#var.inspect
end
def ==(other)
self.var == other
end
# ... methods to manipulate #var
end
str = "test"
obj = MyClass.new("test")
puts obj # prints the var string - great this is what i want
p obj # prints the var string - great this is what i want
puts obj==str # returns true - great this is what i want
puts str==obj # returns false - oops! Fails since i didn't overload the == operator in String class. What to do? I don't think i want to "monkey patch" the String class.
I understand that it's not my overwritten == operator which is used. It's the one in the String class. What do you suggest I do to get around this? Am I going down the wrong path?
I read through the question What's the difference between equal?, eql?, ===, and ==? without getting an answer to my question.
I understand that it's not my overwritten == operator which is used. It's the one in the String class. What do you suggest I do to get around this? Am I going down the wrong path?
Before trying anything else, have a look at the docs for String#==:
If object is not an instance of String but responds to to_str, then the two strings are compared using object.==.
In other words: you have to implement to_str and return the object's string value:
class MyClass
# ...
def to_str
#var.to_s
end
end
You can also return to_s or use alias to_str to_s.
With any of the above, you'd get:
"test" == MyClass.new("test")
#=> true
Note that to_str should only be implemented by objects that are string-like.
What is a good naming convention for clearing an instance variable via a method. The actual value is stored in an array and the methods filter the array to find the value.
Here are methods I already have:
class Foo
def initialize
# Array of objects
#data = []
end
# Get the variable or default value
def variable
#data.select { |o| o.attribute == 'Some Value' }.first || 'Default Value'
end
# Does the variable exist
def variable?
!!#data.select { |o| o.attribute == 'Some Value' }.first
end
# Does this make sense?
def clear_variable!
# Delete the variable
end
end
Or should it be something like delete_variable!?
If you're creating something like that, it's best to emulate the conventional method names used within the most similar structure in Ruby. In this case, it's Hash:
class Foo
def initialize
#data = [ ]
#default = 'Default Value'
end
def [](k)
found = #data.find { |o| o.attribute == k }
found ? found.value : #default
end
def has_key(k)?
!!#data.find { |o| o.attribute == k }
end
# Does this make sense?
def delete(k)
#data.reject! { |o| o.attribute == k }
end
end
Generally a method with ! in it either raises exceptions if something goes wrong, it makes a permanent modification to the state of something (e.g. in-place modification methods), or both. It's not taken to mean "reset" or "clear".
I don't see variable! as making sense in a deletion context. The ruby idiom for a method with an exclamation mark is that the method does something destructive to something else, yes, but it makes little sense in the context of a single variable.
e.g. hash.merge!, record.save! make sense, but mymodel.field! doesn't.
I'd suggest a name like remove_field or unset_field or, if you're clearing multiples, clear!.
I have a class in which the data is stored as a set and I want to be able to compare objects of that class such that the letter case of the elements is of no matter. For example if the set contains elements that are strings there should be no difference of "a" and "A".
To do this I have tried to define the eql? method of the set members to be insensitive to case but this has no effect on the method - (alias difference) in Set. So, how should I go about to make - insensitive to case?
The following code illustrates the problem:
require 'set'
class SomeSet
include Enumerable
def initialize; #elements = Set.new; end
def add(o)
#elements.add(o)
self
end
def each(&block) # To enable +Enumerable+
#elements.each(&block)
end
def difference(compared_list)
#elements - compared_list
end
end
class Element
attr_reader :element
def initialize(element); #element = element; end
# This seems to have no effect on +difference+
def eql?(other_element)
element.casecmp(other_element.element) == 0
end
end
set1 = SomeSet.new
set2 = SomeSet.new
set1.add("a")
set2.add("A")
# The following turns out false but I want it to turn out true as case
# should not matter.
puts set1.difference(set2).empty?
Ok, firstly, you're just storing strings from SomeSet#add, you need to store an instance of Element, like so:
def add(o)
#elements.add(Element.new(o))
self
end
And you need to implement a hash method in your Element class.
You can convert Element##element to lowercase, and pass on its hash.
def hash
element.downcase.hash
end
Full code and demo: http://codepad.org/PffThml2
Edit: For my O(n) insertion comment, above:
Insertions are O(1). From what I can see, eql? is only used with the hash of 2 elements is same. As we're doing hash on the downcased version of the element, it will be fairly well distributed, and eql? shouldn't be called much (if it is called at all).
From the docs:
The equality of each couple of elements is determined according to Object#eql? and Object#hash, since Set uses Hash as storage.
Perhaps you need to implement Object#hash as well.
require 'set'
class String2
attr_reader :value
def initialize v
#value = v
end
def eql? v
value.casecmp(v.value) == 0
end
def hash
value.downcase.hash
end
end
set1 = Set.new
set2 = Set.new
set1.add(String2.new "a")
set2.add(String2.new "A")
puts set1.difference(set2).empty?
For instance,
class Point
attr_accessor :x, :y, :pointer_to_something_huge
end
I only want to serialize x and y and leave everything else as nil.
In Ruby 1.9, to_yaml_properties is deprecated; if you're using Ruby 1.9, a more future proof method would be to use encode_with:
class Point
def encode_with coder
coder['x'] = #x
coder['y'] = #y
end
end
In this case that’s all you need, as the default is to set the corresponding instance variable of the new object to the appropriate value when loading from Yaml, but in more comple cases you could use init_with:
def init_with coder
#x = coder['x']
#y = coder['y']
end
After an inordinate amount of searching I stumbled on this:
class Point
def to_yaml_properties
["#x", "#y"]
end
end
This method is used to select the properties that YAML serializes. There is a more powerful approach that involves custom emitters (in Psych) but I don't know what it is.
This solution only works in Ruby 1.8; in Ruby 1.9, to_yaml has switched to using Psych, for which Matt's answer using encode_with is the appropriate solution.
If you want all fields but a few, you could do this
def encode_with(coder)
vars = instance_variables.map{|x| x.to_s}
vars = vars - ['#unwanted_field1', '#unwanted_field2']
vars.each do |var|
var_val = eval(var)
coder[var.gsub('#', '')] = var_val
end
end
This stops you from manually having to manage the list. Tested on Ruby 1.9
If you have a plenty of instance variables, you could use a short version like this one
def encode_with( coder )
%w[ x y a b c d e f g ].each { |v| coder[ v ] = instance_variable_get "##{v}" }
end
You should use #encode_with because #to_yaml_properties is deprecated:
def encode_with(coder)
# remove #unwanted and #other_unwanted variable from the dump
(instance_variables - [:#unwanted, :#other_unwanted]).each do |var|
var = var.to_s # convert symbol to string
coder[var.gsub('#', '')] = eval(var) # set key and value in coder hash
end
end
or you might prefer this if eval is too dangerous and you only need to filter out one instance var. All other vars need to have an accessor:
attr_accessor :keep_this, :unwanted
def encode_with(coder)
# reject #unwanted var, all others need to have an accessor
instance_variables.reject{|x|x==:#unwanted}.map(&:to_s).each do |var|
coder[var[1..-1]] = send(var[1..-1])
end
end
I'd recommend adding a custom to_yaml method in your class that constructs the specific yaml format you want.
I know that to_json accepts parameters to tell it what attributes to serialize, but I can't find the same for to_yaml.
Here's the actual source for to_yaml:
# File activerecord/lib/active_record/base.rb, line 653
def to_yaml(opts = {}) #:nodoc:
if YAML.const_defined?(:ENGINE) && !YAML::ENGINE.syck?
super
else
coder = {}
encode_with(coder)
YAML.quick_emit(self, opts) do |out|
out.map(taguri, to_yaml_style) do |map|
coder.each { |k, v| map.add(k, v) }
end
end
end
end
So it looks like there may be an opportunity to set opts so that it includes specific key/value pairs in the yaml.
I have an object Results that contains an array of result objects along with some cached statistics about the objects in the array. I'd like the Results object to be able to behave like an array. My first cut at this was to add methods like this
def <<(val)
#result_array << val
end
This feels very c-like and I know Ruby has better way.
I'd also like to be able to do this
Results.each do |result|
result.do_stuff
end
but am not sure what the each method is really doing under the hood.
Currently I simply return the underlying array via a method and call each on it which doesn't seem like the most-elegant solution.
Any help would be appreciated.
For the general case of implementing array-like methods, yes, you have to implement them yourself. Vava's answer shows one example of this. In the case you gave, though, what you really want to do is delegate the task of handling each (and maybe some other methods) to the contained array, and that can be automated.
require 'forwardable'
class Results
include Enumerable
extend Forwardable
def_delegators :#result_array, :each, :<<
end
This class will get all of Array's Enumerable behavior as well as the Array << operator and it will all go through the inner array.
Note, that when you switch your code from Array inheritance to this trick, your << methods would start to return not the object intself, like real Array's << did -- this can cost you declaring another variable everytime you use <<.
each just goes through array and call given block with each element, that is simple. Since inside the class you are using array as well, you can just redirect your each method to one from array, that is fast and easy to read/maintain.
class Result
include Enumerable
def initialize
#results_array = []
end
def <<(val)
#results_array << val
end
def each(&block)
#results_array.each(&block)
end
end
r = Result.new
r << 1
r << 2
r.each { |v|
p v
}
#print:
# 1
# 2
Note that I have mixed in Enumerable. That will give you a bunch of array methods like all?, map, etc. for free.
BTW with Ruby you can forget about inheritance. You don't need interface inheritance because duck-typing doesn't really care about actual type, and you don't need code inheritance because mixins are just better for that sort of things.
Your << method is perfectly fine and very Ruby like.
To make a class act like an array, without actually inheriting directly from Array, you can mix-in the Enumerable module and add a few methods.
Here's an example (including Chuck's excellent suggestion to use Forwardable):
# You have to require forwardable to use it
require "forwardable"
class MyArray
include Enumerable
extend Forwardable
def initialize
#values = []
end
# Map some of the common array methods to our internal array
def_delegators :#values, :<<, :[], :[]=, :last
# I want a custom method "add" available for adding values to our internal array
def_delegator :#values, :<<, :add
# You don't need to specify the block variable, yield knows to use a block if passed one
def each
# "each" is the base method called by all the iterators so you only have to define it
#values.each do |value|
# change or manipulate the values in your value array inside this block
yield value
end
end
end
m = MyArray.new
m << "fudge"
m << "icecream"
m.add("cake")
# Notice I didn't create an each_with_index method but since
# I included Enumerable it knows how and uses the proper data.
m.each_with_index{|value, index| puts "m[#{index}] = #{value}"}
puts "What about some nice cabbage?"
m[0] = "cabbage"
puts "m[0] = #{m[0]}"
puts "No! I meant in addition to fudge"
m[0] = "fudge"
m << "cabbage"
puts "m.first = #{m.first}"
puts "m.last = #{m.last}"
Which outputs:
m[0] = fudge
m[1] = icecream
m[2] = cake
What about some nice cabbage?
m[0] = cabbage
No! I meant in addition to fudge
m.first = fudge
m.last = cabbage
This feels very c-like and I know Ruby
has better way.
If you want an object to 'feel' like an array, than overriding << is a good idea and very 'Ruby'-ish.
but am not sure what the each method
is really doing under the hood.
The each method for Array just loops through all the elements (using a for loop, I think). If you want to add your own each method (which is also very 'Ruby'-ish), you could do something like this:
def each
0.upto(#result_array.length - 1) do |x|
yield #result_array[x]
end
end
If you create a class Results that inherit from Array, you will inherit all the functionality.
You can then supplement the methods that need change by redefining them, and you can call super for the old functionality.
For example:
class Results < Array
# Additional functionality
def best
find {|result| result.is_really_good? }
end
# Array functionality that needs change
def compact
delete(ininteresting_result)
super
end
end
Alternatively, you can use the builtin library forwardable. This is particularly useful if you can't inherit from Array because you need to inherit from another class:
require 'forwardable'
class Results
extend Forwardable
def_delegator :#result_array, :<<, :each, :concat # etc...
def best
#result_array.find {|result| result.is_really_good? }
end
# Array functionality that needs change
def compact
#result_array.delete(ininteresting_result)
#result_array.compact
self
end
end
In both of these forms, you can use it as you want:
r = Results.new
r << some_result
r.each do |result|
# ...
end
r.compact
puts "Best result: #{r.best}"
Not sure I'm adding anything new, but decided to show a very short code that I wish I could have found in the answers to quickly show available options. Here it is without the enumerator that #shelvacu talks about.
class Test
def initialize
#data = [1,2,3,4,5,6,7,8,9,0,11,12,12,13,14,15,16,172,28,38]
end
# approach 1
def each_y
#data.each{ |x| yield(x) }
end
#approach 2
def each_b(&block)
#data.each(&block)
end
end
Lets check performance:
require 'benchmark'
test = Test.new
n=1000*1000*100
Benchmark.bm do |b|
b.report { 1000000.times{ test.each_y{|x| #foo=x} } }
b.report { 1000000.times{ test.each_b{|x| #foo=x} } }
end
Here's the result:
user system total real
1.660000 0.000000 1.660000 ( 1.669462)
1.830000 0.000000 1.830000 ( 1.831754)
This means yield is marginally faster than &block what we already know btw.
UPDATE: This is IMO the best way to create an each method which also takes care of returning an enumerator
class Test
def each
if block_given?
#data.each{|x| yield(x)}
else
return #data.each
end
end
end
If you really do want to make your own #each method, and assuming you don't want to forward, you should return an Enumerator if no block is given
class MyArrayLikeClass
include Enumerable
def each(&block)
return enum_for(__method__) if block.nil?
#arr.each do |ob|
block.call(ob)
end
end
end
This will return an Enumerable object if no block is given, allowing Enumerable method chaining