I know this code doesn't look good at all , but i just want to explain my requirement. I want to know is there any good or alternative approach to it.
Actually, i want to create a new stack and whenever one stack has reached its capacity. I want to keep track of number of stacks created like #stack_1, #stack_2 ...by incrementing #number += 1 like #stack_#number. And for every stack, i want to maintain a #current_position pointer which is specific to every stack like #stack_2 has #current_position_2. So i want to create dynamic instance variables.
Example:
def initialize
#number = 1
#stack+"#{#number}" = Array.new(10)
#current_position_"#{#number}" = 0
end
Output should be something like #stack1 = Array.new(10).
Lets say if i increment value of #number += 1, it should look like #stack2 = Array.new(10)
Instead of array I suggest you to use Hash Map
#stack = Hash.new
#stack[#number] = <Your Array>
Be Careful if the #number is same your array will be replaced..
For more information about hash maps http://www.ruby-doc.org/core-1.9.3/Hash.html
You can do it like this:
instance_variable_set("#stack#{#number}", Array.new(10, :a))
#stack1
#=> [:a, :a, :a, :a, :a, :a, :a, :a, :a, :a]
instance_variable_set("#stack#{#number+1}", Array.new(10, :b))
#stack2
#=> [:b, :b, :b, :b, :b, :b, :b, :b, :b, :b]
instance_variable_set("#current_position_#{#number}", 0)
#current_position_1
#=> 0
Instead of creating instance variables to track a stack's state from the outside, you could create a Stack class that tracks its state internally. Here's a very simple one:
class StackOverflow < StandardError; end
class Stack
def initialize
#stack = []
end
def position
#stack.size
end
def full?
position == 2 # small size for demonstration purposes
end
def push(obj)
raise StackOverflow if full?
#stack << obj
end
end
stack = Stack.new
stack.push "foo"
stack.full? #=> false
stack.push "bar"
stack.full? #=> true
stack.push "baz" #=> StackOverflow
Having a working stack, you can build something like a StackGroup to handle multiple stacks:
class StackGroup
attr_reader :stacks
def initialize
#stacks = [Stack.new]
end
def push(obj)
#stacks << Stack.new if #stacks.last.full?
stacks.last.push(obj)
end
end
stack_group = StackGroup.new
stack_group.push "foo"
stack_group.push "bar"
stack_group.stacks.size #=> 1
stack_group.push "baz" # no error here
stack_group.stacks.size #=> 2
stack_group.stacks
#=> [#<Stack:0x007f9d8b886b18 #stack=["foo", "bar"]>,
# #<Stack:0x007f9d8b886a50 #stack=["baz"]>]
Related
I have a method with a lot of named arguments, some with default values:
def myClass
def initialize(a:, b:, c:, d:, e:, f:, g: nil, h: nil, i: nil)
...
end
end
The list is a little hard to look at and comprehend. I am looking for way to make this simpler.
Using a hash for args,
myClass.new(**args)
works, but I can't have both symbols with and without a value.
Is there a way to make this simpler?
You could try this
def myClass
def initialize(args)
[:a, :b, :c, :d, :e, :f].each do |a|
raise ArgumentError.new unless args.has_key?(a)
end
...
end
end
args is a hash object.
May be there are cases where a function needs such a large number of parameters, but normally this indicates that a function is doing too many things in one place.
Ok, if you want to do it, I would move it into a special private method:
class MyClass
def initialize(*args)
args = set_defaults(args)
end
private
def set_defaults(args)
# step 1: extract the options hash and check the keys,
# if a key doesn't exist so put it in with the default value
options = args.extract_options!
[g: :state, h: 'a name', i: 5].each do |key, value|
options[key] = value unless options.key?(key)
end
# step 2: check the other array elements
[:a, :b, :c, :d, :e, :f].each do |element|
raise ArgumentError.new unless args.include?(element)
end
# step 3: put them all together again
args << options
end
end
BTW: def className doesn't work. It's class ClassName. In addition please have a look at the beautiful ruby style guide - naming.
It's well known to Rubyist & will call to_proc on a symbol, so
[:a, :b, :c].map(&:to_s)
is equivalent to
[:a, :b, :c].map { |e| e.to_s } # => ["a", "b", "c"]
Say I want to call another method right after to_s, these two implementations will work:
[:a, :b, :c].map { |e| e.to_s.upcase }
[:a, :b, :c].map(&:to_s).map(&:upcase)
My question is, is there a way to chain the & Symbol#to_proc call in one parameter? Something like:
[:a, :b, :c].map(&:to_s:upcase)
Thanks!
If you're only doing:
%i[a b c].map { |e| e.to_s.upcase }
then just use the block and get on with more important things. If you're really doing a chain of Enumerable calls and find the blocks too visually noisy:
%i[a b c].map { |e| e.to_s.upcase }.some_chain_of_enumerable_calls...
then you could toss your logic into a lambda to help clean up the appearance:
to_s_upcase = lambda { |e| e.to_s.upcase }
%i[a b c].map(&to_s_upcase).some_chain_of_enumerable_calls...
or throw it in a method and say:
%i[a b c].map(&method(:to_s_upcase)).some_chain_of_enumerable_calls...
Either way, you're giving your little bit of logic a name (which is pretty much all &:symbol is doing for you) to make the code more readable and easier to understand. In the specific case of to_s.upcase, this is all a bit pointless but these approaches are quite useful when the block gets bigger.
You will need to define some method in advance, but this will have generality. You can do like this:
class Symbol
def * other
->x{x.send(self).send(other)}
end
end
[:a, :b, :c].map(&:to_s * :upcase)
[:a, :b, :c].map(&:to_s * :capitalize)
...
I chose * as a method for functional composition.
And if you think you might use a third symbol, you can define like:
class Proc
def * other
->x{call(x).send(other)}
end
end
So just for fun (and to prove that almost anything is possible in ruby if one puts in a bit of effort) we could define a method on Symbol (we'll call it Symbol#chain) to provide this functionality and a little more
class Symbol
def proc_chain(*args)
args.inject(self.to_proc) do |memo,meth|
meth, *passable_args = [meth].flatten
passable_block = passable_args.pop if passable_args.last.is_a?(Proc)
Proc.new do |obj|
memo.call(obj).__send__(meth,*passable_args,&passable_block)
end
end
end
alias_method :chain, :proc_chain
end
This can then be called like so
[:a, :b, :c].map(&:to_s.chain(:upcase))
#=> ["A","B","C"]
# Or with Arguments & blocks
[1,2,3,4,5].map(&:itself.chain([:to_s,2],:chars,[:map,->(e){ "#{e}!!!!"}]))
#=> => [["1!!!!"], ["1!!!!", "0!!!!"], ["1!!!!", "1!!!!"],
# ["1!!!!","0!!!!", "0!!!!"], ["1!!!!", "0!!!!", "1!!!!"]]
Can even be used as a standalone
p = :to_s.chain([:split,'.'])
p.call(123.45)
#=> ["123","45"]
# Or even
[123.45,76.75].map(&p)
#=> => [["123", "45"], ["76", "75"]]
While we're playing with syntax, how about monkey-patching Array with a to_proc method?
class Array
def to_proc
return :itself.to_proc if empty?
->(obj) { drop(1).to_proc.call(first.to_proc.call(obj)) }
end
end
["foo", "bar", "baz"].map(&[:capitalize, :swapcase, :chars, ->a{ a.join("-") }])
# => ["f-O-O", "b-A-R", "b-A-Z"]
See it on repl.it: https://repl.it/JS4B/1
There is no way to chain using the symbol to proc.
However, you could monkey patch a method to the class you are mapping over that will do both, then call that.
class Symbol
def to_upcase_str
self.to_s.upcase
end
end
[:a, :b, :c].map(&:to_upcase_str)
I am surprised no body mentioned about Proc#<< and Proc#>>:
[:a, :b, :c].map(&(:to_s.to_proc << :upcase.to_proc))
# => ["A", "B", "C"]
[:a, :b, :c].map(&(:upcase.to_proc >> :to_s.to_proc))
# => ["A", "B", "C"]
ref: https://ruby-doc.org/core-2.7.2/Proc.html#method-i-3C-3C
If I have a ruby class called Node:
class Node
attr_accessor :a, :b, :c
attr_reader :key
def initialize(key)
#key = key
end
def [](letter)
if letter == 'a'
return self.a
elsif letter == 'b'
return self.b
elsif letter == 'c'
return self.c
end
end
end
How can I optimize def [](letter) so I won't have repetitive code? More specifically, how can I access an attribute of an object (that is a ruby symbol :a, :b, or :c) by using a corresponding string?
You can use send, which invokes a method dynamically on the caller, in this case self:
class Node
def [](key)
key = key.to_sym
send(key) if respond_to?(key)
end
end
Note that we check that self has the appropriate method before calling it. This avoids getting a NoMethodError and instead results in node_instance[:banana] returning nil, which is appropriate given the interface.
By the way, if this is the majority of the behavior of your Node class, you may simply want to use an OpenStruct:
require 'ostruct'
node_instance = OpenStruct.new(a: 'Apple', b: 'Banana')
node_instance.a #=> 'Apple'
node_instance['b'] #=> 'Banana'
node_instance.c = 'Chocolate'
node_instance[:c] #=> 'Chocolate'
How do I implement the '<<' to have the same behavior when used as a chainable method?
class Test
attr_accessor :internal_array
def initialize
#internal_array = []
end
def <<(item)
#internal_array << :a
#internal_array << item
end
end
t = Test.new
t << 1
t << 2
t << 3
#t.internal_array => [:a, 1, :a, 2, :a, 3]
puts "#{t.internal_array}" # => a1a2a3
t = Test.new
t << 1 << 2 << 3
#t.internal_array => [:a, 1, 2, 3]
puts "#{t.internal_array}" # => a123 , Why not a1a2a3?
I want both cases giving the same result.
Add self as the last line in the << method to return it. As is, you're implicitly returning the array, not the instance.
Explanation of the answer above:
When a method is chained, the next method is applied to the result of the first method.
For exemplo:
class A
def method1
B.new
end
end
class B
def method2
C.new
end
end
class C
def method3
puts "It is working!!!"
end
end
The code below will work
A.new.method1.method2.method3
but the this will not
A.new.method1.method3.method2
because an instance of class B, which is the result of A.new.method1 doesn't implement method3. This is the same of:
(((A.new).method1).method3).method2
The code used in the question above, was a little bit more trick, because both, Test and Array had the method <<. But I want Test#<< to return self, and not the #internal_array that was being returned.
I have a class, which de-constructs incoming string into a nested array cascade.
For example for an input abcde it will produce a [[[[a,b],c],d],e] array.
Just now, if I access to set any top level value of cascade, the []=(index, value) method of my class will be invoked. But I also need to catch the access to the nested array within cascade of arbitrary level.
See example below, where accessing x[0][0] obviously doesn't invoke a class method []=. So, is it possible to catch that access within a class method (or at least in a different way)?
class MyClass
attr_accessor :cascade
def initialize string
build_cascade string.split(//)
end
def build_cascade array
if array.length > 2
array[0] = array[0..1]
array.delete_at(1)
build_cascade array
else
#cascade = array
end
end
def []=(index, value)
puts 'You\'ve just tried to set \''+value.to_s+'\' for \''+index.to_s+'\' of #cascade!'
end
def [](index)
#cascade[index]
end
end
x = MyClass.new('abcdefghigk')
puts x.inspect
x[0] = 5 # => You've just tried to set '5' for '0' of #cascade!
x[0][0] = 10 #= > ~ no output ~
The problem is that you are calling []= on the sub-array contained within your main array.
in other words, you are calling [] on your class, which you implement to return that array element, and then []= on a generic Array, which you have not blocked write access to.
you could implement the structure to have your class create its subarrays by using other instances of MyClass, or you could overwrite the []= method of Array to restrict access.
Its also worth noting that depending on how this would be used, overwriting methods on a class like Array is not usually a great idea so you might want go for something like my first suggestion.
In Ruby you can patch objects, add new methods, redefine old ones freely. So you can just patch all the arrays you create so they tell you when they are being accessed.
class A
def patch_array(arr)
class << arr
alias old_access_method []=
def []= (i, v)
#cascade_object.detect_access(self)
old_access_method(i,v)
end
end
s = self
arr.instance_eval {
#cascade_object = s
}
end
def detect_access(arr)
p 'access detected!'
end
end
a = A.new
arr = [1, 2]
a.patch_array(arr)
arr[1] = 3 # prints 'access detected!'
p arr
new_arr = [1,4]
new_arr[1] = 5 #prints nothing
p new_arr
#!/usr/bin/ruby1.8
require 'forwardable'
class MyClass
extend Forwardable
attr_accessor :cascade
def initialize string
#cascade = decorate(build_cascade string.split(//))
end
private
def build_cascade array
if array.length <= 2
array
else
build_cascade([array[0..1]] + array[2..-1])
end
end
def decorate(array)
return array unless array.is_a?(Array)
class << array
alias old_array_assign []=
def []=(index, value)
puts "#{self}[#{index}] = #{value}"
old_array_assign(index, value)
end
end
array.each do |e|
decorate(e)
end
array
end
def_delegators :#cascade, :[], :[]=
end
x = MyClass.new('abcdefghigk')
p x.cascade
# => [[[[[[[[[["a", "b"], "c"], "d"], "e"], "f"], "g"], "h"], "i"], "g"], "k"]
x[0][1] = 5 # => abcdefghig[1] = 5
x[0][0][1] = 10 # => abcdefghi[1] = 10
x[0][0][0][1] = 100 # => abcdefgh[1] = 100
p x.cascade
# => [[[[[[[[[["a", "b"], "c"], "d"], "e"], "f"], "g"], 100], 10], 5], "k"]