I'm trying to figure out how I can create a method in Ruby where I can retrieve values from the method's parameters such as strings/integers.
For example, if this were a function coded in C, it might be done similar to this:
main()
{
int value;
GetAnIntegerValue(value);
printf("The value is %d", value);
}
// The "value" integer variable is passed to it, and updated accordingly because of the use of the ampersand prior to the parameter
GetAnIntegerValue(&int value)
{
value = 5;
}
// The output would be "The value is 5"
I think the term for this is pass by value but I'm not sure. My mind is a little vague on this area and I couldn't find many decent results.
Here's my example Ruby function, the array that the parameters are being assigned to is only local to the class which is the reason for this usage:
def getRandomWordAndHint(&RandomWord, &RandomHint)
randIndex = rand(7)
RandomWord = EnglishLevel1Word[randIndex]
RandomHint = EnglishLevel1Hint[randIndex]
end
Cheers!i
Ruby is pass-by-value. Always. No exceptions. You cannot do pass-by-reference in Ruby.
What you can do, is put the object you want to change into some sort of mutable container:
class MutableCell
attr_accessor :val
def initialize(val)
self.val = val
end
end
def change_the_value(cell)
cell.val = 5
end
value = MutableCell.new(42)
change_the_value(value)
value.val
# => 5
Of course, you can just use an Array instead of writing your own MutableCell class, this is just for demonstration.
However, mutable state is a bad idea in general, and mutating arguments passed to methods is a really bad idea especially. Methods know about their own object (i.e. self) and thus can safely modify it, but for other objects, that's generally a no-go.
Related
I have an array (can be more than just one array) in C. I need to build an interface so that ruby can modify/read the array values. I am building ruby modules in C so they later are used in ruby.
C file:
#include <ruby.h>
VALUE ParentModule;
uint8 variable = 7;
uint8 array[2];
VALUE get_variable(VALUE self)
{
return INT2NUM(variable);
}
VALUE set_variable(VALUE self, VALUE x)
{
variable = NUM2UINT(x);
return Qnil;
}
void Init_extension(void)
{
ParentModule = rb_define_module("ParentModule");
rb_define_method(ParentModule, "variable", get_variable, 0);
rb_define_method(ParentModule, "variable=", set_variable, 1);
}
Ruby file:
class Thing
def initialize
extend ParentModule
end
end
c = Thing.new
c.variable #=> will return the value 7
c.variable= 10 #=> will write 10 to variable in the C section.
c.variable #=> returns 10
So all this works great, but now I need to be able to do the same with an array. What I tried:
C file:
VALUE get_array_0(VALUE self)
{
return INT2NUM(array[0]);
}
VALUE set_array_0(VALUE self, VALUE x)
{
array[0] = NUM2UINT(x);
return Qnil;
}
/* this line is inside Init_extension function */
rb_define_method(ParentModule, "array[0]", get_array_0, 0);
What I am trying to do is name the set/get method to give an impression in ruby that I am "using" an array when is just really an interface to interact with the array that exists in C.
The C file compiles fine but when I try to call the method from Ruby, it complains saying that "array" is not a method
Ruby:
c = Thing.new
c.array[0] #=> NoMethodError (undefined method `array' for #<Thing:0x00000234523>)
What would be the best way to achieve this? (It must work for 2D arrays as well)
NOTE: Please edit my question if you find any information redundant.
What you want is not exactly possible. There is only one way that Ruby interprets this statement:
c.array[0]
That's equivalent to
c.array().[](0)
In other words, two method calls: array with no arguments called on c and then [] with one argument called on the return value of array. If that's the syntax you want, then you'll need to define your classes in a way such that these methods exist: ParentModule will need an array method that returns something responding to []. Since you don't want this to be an actual Array, you'll need to define another object with the [] method (this object can call back to ParentModule to do whatever you want).
You can use [] and []= as the method names to make your object appear array-like in Ruby:
rb_define_method(ParentModule, "[]", get_array, 1);
rb_define_method(ParentModule, "[]=", set_array, 2);
[] takes a single argument, the index of the item you want to look at, and []= takes two arguments, the index and the new value.
You can then implement them something like this:
VALUE get_array(VALUE self, VALUE idx)
{
return INT2NUM(array[NUM2INT(idx)]);
}
VALUE set_array(VALUE self, VALUE idx, VALUE x)
{
array[NUM2INT(idx)] = NUM2UINT(x);
return Qnil;
}
(Obviously this is just a simple example to show the idea, in reality you would want to check the index so it isn’t out of bounds, and also check the values so they make sense for the array type).
These methods will then be directly available on your object:
c = Thing.new
c[0] = 3
p c[0]
I'm trying to make a method with output arguments in ruby.
I read differents posts here and here about the discussion of wether ruby pass its arguments by-value or by-reference and
I undersand that on a strict sens, Ruby always pass-by-value, but the value passed is actually a reference. Reason why there is so much debate on this.
I find out that there are several ways to change the value of the referenced variable.
For instance with the replace method when its an Array, a Hash or a String, or merge! when it's a hash.
I found out that with integer, I can change and pass the value outside my method without any special method use.
My question is about other objects.
For instance I want to retrieve the 'id' attribute of an object, and the object reference itself :
class RestaurantController < ApplicationController
def pizza_to_deliver(pizza_name, id_of_the_order, pizza)
# pizza to eat
pizza = Pizza.where(:name => pizza_name).first
# unknown pizza
return false if pizza.nil?
# first customer order about this pizza
id_of_the_order = Orders.where(:pizza_id => pizza.id).first
true
end
end
my_pizza_name = 'margerita'
My_order_id = nil
my_pizza = nil
my_restaurant = RestaurantController.new
if my_restauant.pizza_to_deliver(my_pizza_name, My_order_id, my_pizza) then
puts "Pizza to deliver : #{my_order_id}"
rex_dog.eat(my_pizza)
end
How to make this works ? (order_id and my_pizza remains with nil)
Ruby has only pass by value, just like Python and Java. Also like Python and Java, objects are not values directly, and are manipulated through references.
It seems you already understand how it works -- assigning to a local variable never has any effect on a caller scope. And to "share" information with the caller scope other than returning, you must use some method on the object to "mutate" the object (if such a method exists; i.e. if the object is mutable) that is pointed to by the passed reference. However, this simply modifies the same object rather than giving a reference to a new object, which you want.
If you are not willing to return the value, you can pass a mutable container (like an array of one element) that the called function can then mutate and put whatever in there and have it be seen in the caller scope.
Another option is to have the function take a block. The function would give the block the new value of pizza, and the block (which is given by the caller) can then decide what to do with it. The caller can pass a block that simply sets the pizza in its own scope.
For the most part, out parameters are a workaround for languages that don't have multiple-value return. In Ruby, I'd just return an Array containing all the output values of the function. Or make the mutable values instance variables in an object and the function a method on that object.
Thanks for both answers.
It seems I came out with an equivalent solution at last : the mutable container.
I created a new class 'OutputParameter' that contains (as attr_accessors) the parameters that I want to output from my method. Then I passed an instance of this class to my method.
class OutputParameters
attr_accessor :order_id, pizza
end
class RestaurantController < ApplicationController
def pizza_to_deliver(pizza_name, output_parameters)
# pizza to eat
pizza = Pizza.where(:name => pizza_name).first
# unknown pizza
return false if pizza.nil?
# first customer order about this pizza
id_of_the_order = Orders.where(:pizza_id => pizza.id).first
# Output values returned
output_parameters.pizza = pizza
output_parameters.order_id = id_of_the_order
true
end
end
my_pizza_name = 'margerita'
my_output = OutputParameters.new
my_restaurant = RestaurantController.new
if my_restaurant.pizza_to_deliver(my_pizza_name, my_output) then
puts "Pizza to deliver : #{my_output.order_id}"
rex_dog.eat(my_output.pizza)
end
The hash or array you suggested seems even a better idea as it is more adaptative : I wouldn't have to declare a class.
I would just use the merge! method
class RestaurantController < ApplicationController
def pizza_to_deliver(pizza_name, output_hash)
# pizza to eat
pizza = Pizza.where(:name => pizza_name).first
# unknown pizza
return false if pizza.nil?
# first customer order about this pizza
id_of_the_order = Orders.where(:pizza_id => pizza.id).first
# Output values returned
output_hash.merge!({:pizza => pizza})
output_hash.merge!({:id_of_the_order => id_of_the_order})
true
end
end
my_pizza_name = 'margerita'
my_output_hash = {}
my_restaurant = RestaurantController.new
if my_restaurant.pizza_to_deliver(my_pizza_name, my_output_hash) then
puts "Pizza to deliver : #{my_output_hash[:id_of_the_order]}"
rex_dog.eat(my_output_hash[:pizza])
end
You could use multiple return values like this:
def maybe_get_something
...
return nil, "sorry" if bad_condition
...
something, nil
end
...
something, err = maybe_get_something
if !err.nil?
handle(err)
return
end
do_something_with(something)
Very similar to what people do when using Go:
f, err := os.Open("filename.ext")
if err != nil {
log.Fatal(err)
}
// do something with the open *File f
I created the following extension
class String
def is_a_number? s # check if string is either an INT or a FLOAT (12, 12.2, 12.23 would return true)
s.to_s.match(/\A[+-]?\d+?(\.\d+)?\Z/) == nil ? false : true
end
end
How can I make it work as a chained method?
is_a_number?("10") # returns true
"10".is_a_number? # returns an error (missing arguments)
Update
Thanks sawa, mikej and Ramon for their answers. As suggested, I changed the class to Object and got rid of the argument (s):
class Object
def is_a_number? # check if string is either an INT or a FLOAT (12, 12.2, 12.23 would return true)
to_s.match(/\A[+-]?\d+?(\.\d+)?\Z/) != nil
end
end
It now works perfectly fine:
23.23.is_a_number? # > true
Thanks guys...
When you write "10".is_a_number?, you already have the object "10" you want to check for, which is the receiver of is_a_number?, so your method doesn't need to take any parameters.
Because match is an instance method on String, you don't need to specify a receiver for it. It will just operate on the same object on which is_a_number? was called. Because you know you already have a String object, the to_s isn't needed either.
Just write it as:
class String
# check if string is either an INT or a FLOAT (12, 12.2, 12.23 would return true)
def is_a_number?
match(/\A[+-]?\d+?(\.\d+)?\Z/) != nil
end
end
Ramon's suggestion that you may want to put your extension on Object rather than on String is a good point if you don't know if the object you're testing is going to be a string.
Also, what you're describing isn't really what is meant by method chaining; it's just calling a method on an object. Method chaining is where the return types of methods are set up so that several methods can be called in sequence e.g in Rails, something like
User.where(:name => 'Mike').limit(3) # find the first 3 Mikes
is an example of method chaining.
It seems like you want to patch Object instead of String (since you are calling to_s):
class Object
def is_a_number?
to_s.match(/\A[+-]?\d+?(\.\d+)?\Z/).nil?
end
end
You could also look at replacing with it with validates numericality: true on your model.
I know a bit about ruby way to handle objects and references. The replace stuff, ect ...
I know it d'ont work on fixnum, cause the var is the fixnum. But i wish to change the value of a fixnum inside a function, and that the value changed in the ouside var.
How can i do this ?
I guess i can use a string like this "1" but that's quite dirty.
Ruby will always pass-by-reference (because everything is an object) but Fixnum lacks any methods that allow you to mutate the value. See "void foo(int &x) -> Ruby? Passing integers by reference?" for more details.
You can either return a value that you then assign to your variable, like so:
a = 5
def do_something(value)
return 1 #this could be more complicated and depend on the value passed in
end
a = do_something(a)
or you could wrap your value in an object such as a Hash and have it updated that way.
a = {:value => 5}
def do_something(dict)
dict[:value] = 1
end
do_something(a) #now a[:value] is 1 outside the function
Hope this helps.
You could pass an array with a single number, like [1], or a hash like {value: 1}. Less ugly than a string, as your number itself remains a number, but less overhead than a new class...
When I was building a game I had the same problem you have. There was a numeric score that represented how many zombies you've killed and I needed to manually keep it in sync between Player (that incremented the score), ScoreBar and ScoreScreen (that displayed the score). The solution I've found was creating a separate class for the score that will wrap the value and mutate it:
class Score
def initialize(value = 0)
#value = value
end
def increment
#value += 1
end
def to_i
#value
end
def to_s
#value.to_s
end
end
I'm new to Ruby and I'm just having a play around with ideas and what I would like to do is remove the #continent data from the country_array I have created. Done a good number of searches and can find quite a bit of info on removing elements in their entirety but can't find how to specifically remove #continent data. Please keep any answers fairly simple as I'm new, however any help much appreciated.
class World
include Enumerable
include Comparable
attr_accessor :continent
def <=> (sorted)
#length = other.continent
end
def initialize(country, continent)
#country = country
#continent = continent
end
end
a = World.new("Spain", "Europe")
b = World.new("India", "Asia")
c = World.new("Argentina", "South America")
d = World.new("Japan", "Asia")
country_array = [a, b, c, d]
puts country_array.inspect
[#<World:0x100169148 #continent="Europe", #country="Spain">,
#<World:0x1001690d0 #continent="Asia", #country="India">,
#<World:0x100169058 #continent="South America", #country="Argentina">,
#<World:0x100168fe0 #continent="Asia", #country="Japan">]
You can use remove_instance_variable. However, since it's a private method, you'll need to reopen your class and add a new method to do this:
class World
def remove_country
remove_instance_variable(:#country)
end
end
Then you can do this:
country_array.each { |item| item.remove_country }
# => [#<World:0x7f5e41e07d00 #country="Spain">,
#<World:0x7f5e41e01450 #country="India">,
#<World:0x7f5e41df5100 #country="Argentina">,
#<World:0x7f5e41dedd10 #country="Japan">]
The following example will set the #continent to nil for the first World object in your array:
country_array[0].continent = nil
irb(main):035:0> country_array[0]
=> #<World:0xb7dd5e84 #continent=nil, #country="Spain">
But it doesn't really remove the continent variable since it's part of your World object.
Have you worked much with object-oriented programming? Is your World example from a book or tutorial somewhere? I would suggest some changes to how your World is structured. A World could have an array of Continent's, and each Continent could have an array of Country's.
Names have meaning and variable names should reflect what they truly are. The country_array variable could be renamed to world_array since it is an array of World objects.
99% of the time I would recommend against removing an instance variable, because it's extra code for no extra benefit.
When you're writing code, generally you're trying to solve a real-world problem. With the instance variable, some questions to ask are:
What real world concept am I trying to model with the various states the variable can be in?
What am I going to do with the values stored in the variable?
If you're just trying to blank out the continent value stored in a World object, you can set #continent to nil as dustmachine says. This will work fine for the 99% of the cases. (Accessing a removed instance variable will just return nil anyway.)
The only possible case (I can think of) when removing the instance variable could be useful is when you're caching a value that may be nil. For example:
class Player
def score(force_reload = false)
if force_reload
# purge cached value
remove_instance_variable(:#score)
end
# Calling 'defined?' on an instance variable will return false if the variable
# has never been set, or has been removed via force_reload.
if not defined? #score
# Set cached value.
# Next time around, we'll just return the #score without recalculating.
#score = get_score_via_expensive_calculation()
end
return #score
end
private
def get_score_via_expensive_calculation
if play_count.zero?
return nil
else
# expensive calculation here
return result
end
end
end
Since nil is a meaningful value for #score, we can't use nil to indicate that the value hasn't been cached yet. So we use the undefined state to tell us whether we need to recalculate the cached value. So there are 3 states for #score:
nil (means user has not played any games)
number (means user played at least once but did not accrue any points)
undefined (means we haven't fetched the calculated score for the Player object yet).
Now it's true that you could use another value that's not a number instead of the undefined state (a symbol like :unset for example), but this is just a contrived example to demonstrate the idea. There are cases when your variable may hold an object of unknown type.