Difference between passing &:method and :method as function arguments in ruby - ruby-on-rails

I'm struggling in understanding when to use the ampersand in passing symbols to functions representing a method. For example, If I wanted to calculate the sum of the range 1..10, I could do the following:
(1..10).inject(:+)
This originally lead me to believe that if you wanted to pass a symbol to define a method to "Magically" be used in the function, you would pass the function name as a symbol. But then I see something like this in rails:
total = Product.find(product_list).sum(&:price)
If I understand correctly, &:price is the same as calling :price.to_proc. I don't understand how the above works.

In actual parameter list of a method call, &object is built-in syntax, which will
convert object to a Proc using object.to_proc
pass the Proc as the block parameter of the method
Symbol#to_proc converts the symbol (eg. :the_symbol) to proc {|obj| obj.send(:the_symbol)}. And you can use this syntax whenever object responds to to_proc method and returns a Proc.
abc = "aha~"
class << abc
def to_proc
proc {|obj| obj.to_i * 2 }
end
end
p ["1", "2", "3"].map(&abc)
#=> [2, 4, 6]
(1..10).inject(:+) shows that inject accepts a symbol as parameter. How the symbol is used is method specific behavior. In inject's special case, it has the same effect as (1..10).inject{|a, b| a.send(:+, b)}. It's just a simple, normal parameter, the effect depends on the implementation of the method that accept the symbol as parameter.
Note that sum in ActiveSupport accepts a block with single parameter, which has the effect "map values in the original sequence to new ones and calculate the sum of them", so
total = Product.find(product_list).sum(&:price)
is equivalent as
total = Product.find(product_list).sum(&proc{|p| p.send(:price)})
# or you may write
total = Product.find(product_list).sum{|p| p.price }
which has the same return value as the following but won't produce intermediate temp array:
total = Product.find(product_list).map{|p| p.price}.sum

Related

Define a variable which evaluates when expression is evaluated, but not substitutes its definition to expression

Let's say, I want to declare an elliptic integral as
K(k):=elliptic_kc (k^2);
k:=<something like tanh()*coth()...>
The problem is that maxima will always substitute elliptic_kc(x^2) in place of K(x), and k's definition in place of k.
I want to prevent it, while still allowing numeric evaluation of K(), k, and simplifying expressions with these symbols.
...
A function, can be declared as "noun" for disabling substitution. But this also disables its evaluation.
Well, I use various strategies. Sometimes one approach works better than another.
(1) Put a single quote ' on function names to nounify that specific function call. At a later time, ev(expr, nouns) verbifies any nouns, so the functions are called. E.g. foo: 'integrate(sin(x), x); yields a noun expression. Then ev(foo, nouns); (which can be abbreviated to foo, nouns; at the console input) to actually calculate it.
(2) Don't define functions, but just let them be undefined symbols. Then substitute a lambda expression when you want to evaluate them. E.g. foo: f(2); then later subst(f = lambda([x], x + 1), foo);.
(3) Don't assign values, but let them be undefined, then substitute values later on. E.g. foo: a + b; then later subst([a = 123, b = y*z], foo);.

Find sum of all elements using ruby and selenium

In my web page there are 5 values given in the text field(like $10, $20, $30, $40 and $50) and I am trying to sum the values using ruby and selenium WebDriver.
Here is my code:
def get_sum_of_all_elements()
#logger.info("Searching element #{value1, value2, value3, value4, value5}");
allelements = #driver.find_elements(:id = "lbl_val_")
#logger.info("Total Elements Found with locator #{locator} are : #{allelements.size}");
if allelements.start_with?("$")
allelements = "((allelements))".tr('$', '') #removing '$' sign from values
iSum =0
allelements.each do|i|
iSum += i
end
end
end
I am expecting to see output as 150. Do I need to store values in an array?
Any help would be appreciated.
There a couple of things you should modify in your code to make it work:
Fix how arguments are passed to find_elements; it should be id: "lbl_val_".
find_elements returns an array of WebDriver::Element objects, so you must check the value for each object.
The string "Searching element #{value1, value2, value3, value4, value5}" is not valid since you are trying to interpolate the value of 5 variables chained with a comma. You either need to interplate only the variable (keeping commas as strings) or use square brackets ([]) to interpolate an array.
Now your code should look something like this1:
def get_sum_of_all_elements
#logger.info("Searching element #{[value1, value2, value3, value4, value5]}")
allelements = #driver.find_elements(id: "lbl_val_")
#logger.info("Total Elements Found with locator #{locator} are : #{allelements.size}");
if allelements.all? { |elem| elem.value.start_with?("$") }
elements = allelements.map { |elem| elem.value.tr('$', '').to_i }
elements.reduce(:+)
end
end
A few things to note:
Parenthesis (()) were removed in method definition, ruby doesn't need them when no arguments are passed.
There is no longer need to assign the final value to a variable (e.g iSum) since ruby will return the result of last evaluated code.
If any value doesn't start with "$", it will return false. You could change this by adding a default value after if block.
Semicolons (;) were removed, you don't need them in ruby (unless you want to chain multiple statements in a singe line).
One more thing, the variables value1, value2, value3, value4, value5 and locator doesn't seem to be set anywhere in your method; you must set them within your method (or pass them as arguments) or you will get an error.
1 This considers the same logic that you seemed to be looking for in your code, that is, sum all values only if all of them start with "$".
It's hard to say exactly what you are trying to do, but this might help. I assume you have an array of string values with dollar signs:
>> allelements = ["$10", "$20", "$30", "$40", "$50"]
=> ["$10", "$20", "$30", "$40", "$50"]
We can make a new array stripping out all non-numeric characters and transforming the string values to integers:
>> integers = allelements.map { |e| e.gsub(/[^\d]/, '').to_i }
=> [10, 20, 30, 40, 50]
Now use inject to sum the values:
>> integers.inject(:+)
=> 150

Ruby method returns hash values in binary

I wrote a method that takes six names then generates an array of seven random numbers using four 6-sided dice. The lowest value of the four 6-sided dice is dropped, then the remainder is summed to create the value. The value is then added to an array.
Once seven numbers have been generated, the array is then ordered from highest to lowest and the lowest value is dropped. Then the array of names and the array of values are zipped together to create a hash.
This method ensures that the first name in the array of names receives the highest value, and the last name receives the lowest.
This is the result of calling the method:
{:strength=>1, :dexterity=>1, :constitution=>0, :intelligence=>0, :wisdom=>0, :charisma=>1}
As you can see, all the values I receive are either "1" or "0". I have no idea how this is happening.
Here is the code:
module PriorityStatGenerator
def self.roll_stats(first_stat, second_stat, third_stat, fourth_stat, fifth_stat, sixth_stat)
stats_priority = [first_stat, second_stat, third_stat, fourth_stat, fifth_stat, sixth_stat].map(&:to_sym)
roll_array = self.roll
return Hash[stats_priority.zip(roll_array)]
end
private
def self.roll
roll_array = []
7.times {
roll_array << Array.new(4).map{ 1 + rand(6) }.sort.drop(1).sum
}
roll_array.reverse.delete_at(6)
end
end
This is how I'm calling the method while I'm testing:
render plain: PriorityStatGenerator.roll_stats(params[:prioritize][:first_stat], params[:prioritize][:second_stat], params[:prioritize][:third_stat], params[:prioritize][:fourth_stat], params[:prioritize][:fifth_stat], params[:prioritize][:sixth_stat])
I added require 'priority_stat_generator' where I'm calling the method, so it is properly calling it.
Can someone help me make it return proper values between 1 and 18?
Here's a refactoring to simplify things and use an actually random number generator, as rand is notoriously terrible:
require 'securerandom'
module PriorityStatGenerator
def self.roll_stats(*stats)
Hash[
stats.map(&:to_sym).zip(self.roll(stats.length).reverse)
]
end
private
def self.roll(n = 7)
(n + 1).times.map do
4.times.map { 1 + SecureRandom.random_number(6) }.sort.drop(1).inject(:+)
end.sort.last(n)
end
end
This makes use of inject(:+) so it works in plain Ruby, no ActiveSupport required.
The use of *stats makes the roll_stats function way more flexible. Your version has a very rigid number of parameters, which is confusing and often obnoxious to use. Treating the arguments as an array avoids a lot of the binding on the expectation that there's six of them.
As a note it's not clear why you're making N+1 roles and then discarding the last. That's the same as generating N and discarding none. Maybe you meant to sort them and take the N best?
Update: Added sort and reverse to properly map in terms of priority.
You need to learn to use IRB or PRY to test snippets of your code, or better, learn to use a debugger. They give you insight into what your code is doing.
In IRB:
[7,6,5,4,3,2,1].delete_at(6)
1
In other words, delete_at(6) is doing what it's supposed to, but that's not what you want. Instead, perhaps slicing the array will behave more like you expect:
>> [7,6,5,4,3,2,1][0..-2]
[
[0] 7,
[1] 6,
[2] 5,
[3] 4,
[4] 3,
[5] 2
]
Also, in your code, it's not necessary to return a value when that operation is the last logical step in a method. Ruby will return the last value seen:
Hash[stats_priority.zip(roll_array)]
As amadan said, I can't see how you are getting the results you are, but their is a definite bug in your code.
The last line in self.roll is the return value.
roll_array.reverse.delete_at(6)
Which is going to return the value that was deleted. You need to add a new lines to return the roll_array instead of the delete_at value. You are also not sorting your array prior to removing that last item which will give you the wrong values as well.
def self.roll
roll_array = []
7.times {
roll_array << Array.new(4).map{ 1 + rand(6) }.sort.drop(1).sum
}
roll_array.sort.drop(1)
roll_array
end

are there any additional inject shorthand

I recently ran into this issue:
I've always used inject like so (i knew that (0) part is optional and can be omitted)
array = [13,23,13]
#=> [13, 23, 13]
array.inject(0) { |sum,i| sum+i }
#=> 49
By chance i found out that you can use:
array.inject(:+)
#=> 49
array.inject(:-)
#=> -23
array.inject(:*)
#=> 3887
array.inject(:/)
#=> 0
Googling on the issue i found a nice article on inject, but no mentioning there about what i've tried....
Can anyone explain to me or give some info about these inject commands that I've just used?
From the doc on Enumerable#inject:
... If you specify a symbol instead, then each element in the collection will be passed to the named method of memo. In either case, the result becomes the new value for memo. At the end of the iteration, the final value of memo is the return value for the method.
If you do not explicitly specify an initial value for memo, then uses the first element of collection is used as the initial value of memo.
So, if you specify a symbol, it treats it as a method name and invokes this method on every element of the enumerable, replacing memo as stated above. Now, the math operators (+-*/) are just methods, nothing else. These lines produce identical result:
13 + 23 # => 36
13.+(23) # => 36
13.send(:+, 23) # => 36
When you pass a symbol to inject or reduce it uses the third form to dynamically apply that operator to elements:
[1,2,3].inject(:+) # => 6
This shorthand can be used with methods other than operators as well:
[{"a"=>1}, {"b"=>2}].inject(:merge) # => {"a"=>1, "b"=>2}

Some question about "Closure" in Lua

Here's my code, I confuse the local variable 'count' in the return function(c1,c2) with memory strack and where does they store in?
function make_counter()
local count = 0
return function()
count = count + 1
return count
end
end
c1 = make_counter()
c2 = make_counter()
print(c1())--print->1
print(c1())--print->2
print(c1())--print->3
print(c2())--print->1
print(c2())--print->2
in the return function(c1,c2) with memory strack and where does they store in?
It's stored in the closure!
c1 is not a closure, it is the function returned by make_counter(). The closure is not explicitly declared anywhere. It is the combination of the function returned by make_counter() and the "free variables" of that function. See closures # Wikipedia, specifically the implementation:
Closures are typically implemented with a special data structure that contains a pointer to the function code, plus a representation of the function's lexical environment (e.g., the set of available variables and their values) at the time when the closure was created.
I'm not quite sure what you're asking exactly, but I'll try to explain how closures work.
When you do this in Lua:
function() <some Lua code> end
You are creating a value. Values are things like the number 1, the string "string", and so forth.
Values are immutable. For example, the number 1 is always the number 1. It can never be the number two. You can add 1 to 2, but that will give you a new number 3. The same goes for strings. The string "string" is a string and will always be that particular string. You can use Lua functions to take away all 'g' characters in the string, but this will create a new string "strin".
Functions are values, just like the number 1 and the string "string". Values can be stored in variables. You can store the number 1 in multiple variables. You can store the string "string" in multiple variables. And the same goes for all other kinds of values, including functions.
Functions are values, and therefore they are immutable. However, functions can contain values; these values are not immutable. It's much like tables.
The {} syntax creates a Lua table, which is a value. This table is different from every other table, even other empty tables. However, you can put different stuff in tables. This doesn't change the unique value of the table, but it does change what is stored within that table. Each time you execute {}, you get a new, unique table. So if you have the following function:
function CreateTable()
return {}
end
The following will be true:
tableA = CreateTable()
tableB = CreateTable()
if(tableA == tableB) then
print("You will never see this")
else
print("Always printed")
end
Even though both tableA and tableB are empty tables (contain the same thing), they are different tables. They may contain the same stuff, but they are different values.
The same goes for functions. Functions in Lua are often called "closures", particularly if the function has contents. Functions are given contents based on how they use variables. If a function references a local variable that is in scope at the location where that function is created (remember: the syntax function() end creates a function every time you call it), then the function will contain a reference to that local variable.
But local variables go out of scope, while the value of the function may live on (in your case, you return it). Therefore, the function's object, the closure, must contain a reference to that local variable that will cause it to continue existing until the closure itself is discarded.
Where do the values get stored? It doesn't matter; only the closure can access them (though there is a way through the C Lua API, or through the Lua Debug API). So unlike tables, where you can get at anything you want, closures can truly hide data.
Lua Closures can also be used to implement prototype-based classes and objects. Closure classes and objects behave slightly differently than normal Lua classes and their method of invocation is somewhat different:
-- closure class definition
StarShip = {}
function StarShip.new(x,y,z)
self = {}
local dx, dy, dz
local curx, cury, curz
local engine_warpnew
cur_x = x; cur_y = y; cur_z = z
function setDest(x,y,z)
dx = x; dy=y; dz=z;
end
function setSpeed(warp)
engine_warpnew = warp
end
function self.warp(x,y,z,speed)
print("warping to ",x,y,x," at warp ",speed)
setDest(x,y,z)
setSpeed(speed)
end
function self.currlocation()
return {x=cur_x, y=cur_y, z=cur_z}
end
return self
end
enterprise = StarShip.new(1,3,9)
enterprise.warp(0,0,0,10)
loc = enterprise.currlocation()
print(loc.x, loc.y, loc.z)
Produces the following output:
warping to 0 0 0 at warp 10
1 3 9
Here we define a prototype object "StarShip" as an empty table.
Then we create a constructor for the StarShip in the "new" method. The first thing it does is create a closure table called self that contains the object's methods. All methods in the closure (those defined as 'function self.') are "closed" or defined for all values accessible by the constructor. This is why it's called a closure. When the constructor is done it returns the closure object "return self".
A lot more information on closure-based objects is available here:
http://lua-users.org/wiki/ObjectOrientationClosureApproach

Resources