Ruby ! Methods or Procs - ruby

I'm wanting to modify a variable in place, replicating the methodname! syntax as opposed to reassigning a new modified value to the same var. Can I do this with a proc? I'm still learning procs and see them as quite useful is used properly.
a = "Santol bag 85.88 www.example.com/products/16785
Shaddock kg 2.94 www.example.com/products/4109
Palm Fig 5kg 94.34 www.example.com/products/23072
Litchee lb 95.85 www.example.com/products/2557"
a = a.split("\n")
linebreak = Proc.new { |text| text.split("\n") }
linebreak![a]
that first reassignment seems cumbersome. The proc version I would like to see if I can perform it inline. Is this possible?

This is surely possible, you just need to modify the string inplace
linebreak = ->(text) { text.replace text.split("\n").join(",") }
a = "foo\nbar"
linebreak[a]
#⇒ "foo,bar"
a
#⇒ "foo,bar"
What is not possible, is to change the class in place, that’s why split won’t work (called on a string, it returns an array.)

methodname! is just a convention - usually there are two flavours of the same method - one without bang and one with bang. If you want to have a proc that mutates its params, you need to implement it using mutating methods.
And in this case it's not possible, because you're trying to transform a string into an array. You have to reassign the variable:
linebreak = Proc.new { |text| text.split("\n") }
a = linebreak.call(a)

Related

Why can you change the value of a local variable in Ruby in a function using another method but not with an assignment operator?

I'm trying to understand the concept of Ruby as a pass-by-reference-value language. Using an example I found on this site...
def uppercase(value)
value.upcase!
end
name = 'William'
uppercase(name)
puts name
we get the output "WILLIAM". So value and name are both pointing at the same object, which is initially holding a value of "William" and then .upcase changes it to "WILLIAM". I would understand this as pass-by-reference.
But then if I change .upcase to =:
def uppercase2(value)
value = "WILLIAM"
end
name = 'William'
uppercase2(name)
puts name
The output becomes "William". Which is pass by value.
Why does an assignment operator make the difference in how a variable is treated in Ruby?
The key here is that you can never change a Ruby object's core self, once it has been created it will always be the same object until garbage collected and destroyed. It is only possible to alter properties of the object.
You can, however, change variables or object references such as instance variables, constants, attr_accessor properties among other things.
So in this case:
def uppercase2(value)
value = "WILLIAM"
end
This reassigns the value local variable. It does nothing to the original object.
If you want to replace that text you need to use methods on the object to effect it, if supported. In this case there is a method:
def uppercase2(value)
value.replace("WILLIAM")
end
These are generally termed in-place modifications as in the object itself is manipulated rather than swapped for another object.
I'm trying to understand the concept of Ruby as a pass-by-reference-value language.
Ruby is pass-by-value. Always. It is never pass-by-reference. The value that is being passed is an immutable unforgeable pointer, but that is very different from pass-by-reference.
C is pass-by-value. And C has pointers. That doesn't mean that if you pass a pointer in C, it magically becomes pass-by-reference. It is still pass-by-value.
C++ has pointers, and it supports both pass-by-value and pass-by-reference. In C++, you can pass pointers by value or by reference and you can pass non-pointers by value or by reference. The two concepts are completely orthogonal.
If we can agree that C is pass-by-value, and we can agree that C has pointers, and we can agree that when we pass a pointer in C, that is still pass-by-value, then we must also agree that Ruby is pass-by-value, because Ruby behaves like a hypothetical version of C, where the only allowed types are "pointer to something", the only way to access a value is dereferencing the pointer, and the only way to pass a value is taking the pointer.
That does not change parameter passing in C in any way, which means it is still pass-by-value, which means if we call that in C pass-by-value, it doesn't make sense to call it anything else in Ruby.
def uppercase(value)
value.upcase!
end
name = 'William'
uppercase(name)
puts name
we get the output "WILLIAM". So value and name are both pointing at the same object, which is initially holding a value of "William" and then .upcase changes it to "WILLIAM". I would understand this as pass-by-reference.
Again, this is not pass-by-reference. Pass-by-reference would mean that you can change the reference in the caller's scope, which Ruby does not allow you to do.
This is nothing but simple mutation. Ruby is not a purely functional language, it does allow you to mutate objects. And when you mutate an object, you can observe its changed state regardless of what name you call it.
My friends call me "Jörg" but my barber calls me "Mr. Mittag". When my barber cuts my hair, it doesn't magically grow back when I meet my friends, it will still be gone even though they don't refer to me by the same name as my barber does.
You have two names for the same object, and you mutate that object. You will observe the new state regardless of which name you use to refer to that object.
That is simply "mutable state", it has nothing to do with pass-by-reference.
But then if I change .upcase to =:
def uppercase2(value)
value = "WILLIAM"
end
name = 'William'
uppercase2(name)
puts name
The output becomes "William". Which is pass by value.
Why does an assignment operator make the difference in how a variable is treated in Ruby?
It doesn't. Both cases are pass-by-value. In the second case, you created a new object and assigned it to the local variable value inside the uppercase2 method. (Technically, it is not a local variable, it is a parameter binding, but it can be rebound inside the method body, precisely because Ruby is pass-by-value. If it were pass-by-reference, then this would have also reassigned the name local variable to the newly created object.)
Sometimes, this specific case of pass-by-value, where the value being passed is an immutable pointer to a potentially mutable object is called call-by-object-sharing, call-by-sharing, or call-by-object. But that is not something different from pass-by-value. It is still pass-by-value. It is a special case of pass-by-value, where the value cannot be "any value" but is always "an immutable unforgeable pointer".
Sometimes, you will hear the description "Ruby is pass-by-value where the value that is being passed is a reference" or "Ruby is pass-by-reference-value" or "Ruby is pass-by-value-reference" or "Ruby is pass-by-object-reference". I don't really like these terms, because they sound very close to "pass-by-reference", but actually the term "reference" in "pass-by-reference" and the term "reference" in "pass-by-object-reference" mean two different things.
In "pass-by-reference", the term "reference" is a technical term that can be thought of as a generalization of the concept of "variable", "storage location", etc. Wheres in "pass-by-value-reference", we are talking about "object references", which are more like pointers but cannot be manufactured or changed.
I also don't like the terms that I used above "pass-by-value where the value being passed is an immutable unforgeable pointer", because the term "pointer" has certain connotations, especially for people coming from C. In C, you can do pointer arithmetic, and you can cast a number to a pointer, i.e. you can "conjure up a pointer out of thin air". You can do none of that in Ruby. That's why I add the adjectives "immutable" (no arithmetic) and "unforgeable" (you cannot create a pointer, only be handed one by the system), but people overlook or ignore them or underestimate their importance.
In some languages, these immutable unforgeable pointers to objects are called "object references" (which is again dangerous because it invites confusion with "pass-by-reference") or OOPS (Object-Oriented PointerS) which has the unfortunate connotations of the mostly unrestricted free-for-all "C" pointers. (Go, for example, has much more restrictive pointers, but when you simply say the word "pointer", nobody thinks of Go.)
Note that none of this is really specific to Ruby. Python, ECMAScript, Java, and many others behave the same way. C# behaves the same way by default, but also supports pass-by-reference as an explicit opt-in. (You must explicitly request pass-by-reference both at the method definition and at the method call.) Scala behaves the same way by default, but optionally supports call-by-name.
C# is actually a very good way of demonstrating the distinctions, because C# supports both pass-by-value and pass-by-reference, both value types and reference types, and obviously, you can write types both as mutable and immutable types, so you actually get all 8 possible different combinations and you can study how they behave.
I came up with this simple test code, which you can easily translate into other languages as well:
def is_ruby_pass_by_value?(foo)
foo.replace('More precisely, it is call-by-object-sharing!')
foo = 'No, Ruby is pass-by-reference.'
end
bar = 'Yes, of course, Ruby *is* pass-by-value!'
is_ruby_pass_by_value?(bar)
p bar
# 'More precisely, it is call-by-object-sharing!'
Here is the slightly more involved example in C#:
struct MutableCell { public string value; }
static void ArgumentPassing(string[] foo, MutableCell bar, ref string baz, ref MutableCell qux)
{
foo[0] = "More precisely, for reference types it is call-by-object-sharing, which is a special case of pass-by-value.";
foo = new string[] { "C# is not pass-by-reference." };
bar.value = "For value types, it is *not* call-by-sharing.";
bar = new MutableCell { value = "And also not pass-by-reference." };
baz = "It also supports pass-by-reference if explicitly requested.";
qux = new MutableCell { value = "Pass-by-reference is supported for value types as well." };
}
var quux = new string[] { "Yes, of course, C# *is* pass-by-value!" };
var corge = new MutableCell { value = "For value types it is pure pass-by-value." };
var grault = "This string will vanish because of pass-by-reference.";
var garply = new MutableCell { value = "This string will vanish because of pass-by-reference." };
ArgumentPassing(quux, corge, ref grault, ref garply);
Console.WriteLine(quux[0]);
// More precisely, for reference types it is call-by-object-sharing, which is a special case of pass-by-value.
Console.WriteLine(corge.value);
// For value types it is pure pass-by-value.
Console.WriteLine(grault);
// It also supports pass-by-reference if explicitly requested.
Console.WriteLine(garply.value);
// Pass-by-reference is supported for value types as well.
Let's start breaking this down in something more understandable.
You can compare a variable like name (or value) with a signpost.
Let's say I point it to the a beige house, which will be the value of the variable.
With the above in mind let's take a look at the first example:
# don't worry about this line for now
House = Struct.new(:color)
def color_red(value)
value.color = :red
end
house = House.new(:beige)
color_red(house)
puts house
# prints: #<struct House color=:red>
So what happens here? When we pass house as parameter to color_red Ruby will copy our signpost and assign it to value. Now both signposts point to the same house. We then follow the directions of signpost value and walk to the house and paint it red.
For this reason the color of house will end up being red.
Now let's have a look at the other example:
def color_red(value)
value = House.new(:red)
end
house = House.new(:beige)
color_red(house)
puts house
# prints: #<struct House color=:beige>
Here we start of the same, we copy our signpost house and assign the copy to value. However instead of walking up to the house and painting it, we are going to modify the signpost and point it to a red house somewhere further down the street. And because our signpost value is a copy of house, pointing it in a new direction will not effect house.
Your code is doing the same thing. When you call value.upcase! you are saying to she string. Hey you, upcase all your characters! (Similar to painting the house.)
When you reasign value (value = "WILLIAM") you are essentially just modifying the signpost and pointing it to a new direction. However the signpost is passed as a copy so it does not effect the original.
def uppercase(value)
value.upcase!
end
name = 'William'
uppercase(name)
puts name #WILLIAM
In this case, you are mutating the original object. name points to William
and so does value. When you pass in the argument, Ruby will assign
the parameter variable value to the same object that name is pointing to.
def uppercase2(value)
value = "WILLIAM"
end
name = 'William'
uppercase2(name)
puts name
In this case, you are reassigning value. That is, you are changing which
object value is pointing to. It was pointing to the same string object that
name was pointing to. But, now, you are asking value to reference a
different object.
So, in summary, upcase! mutates the object while = will re-assign.
You can think of 3 circles, value is in one, name in another and William
in the third.
value and name both point to the string object, William.
In the first situation, you mutate the string object that both value and name
are pointing to.
In the second situation, you are creating a 4th circle, which has WILLIAM in it. Then you are erasing the line from value to William and creating a line
from value to WILLIAM.
You get the idea if I do this:
def uppercase2(value)
value = "WILLIAM"
puts value
end
name = 'William'
uppercase2(name) # => “WILLIAM”
puts name # William

Scala -> Ruby conversion: Confusing output

Ruby Code:
def fnAdd(x)
return ->(y) { x + y }
end
add1 = 1.method(:fnAdd)
puts add1.call(10)
Output: Proc:0x007f52658d3330#main.rb:2 (lambda)
I am having issues getting the desired output in the above code.
I'm basically trying to write the following Scala code (which calls a function that returns another function) in Ruby.
Scala Code:
def fnAdd (x:Int) = {
(y:Int) => x + y
}
var add1 = fnAdd (1)
var add2 = fnAdd (2)
println(add1(10))
println(add2(3))
Output: 11 5
I've made an attempt at converting the code to Ruby but I'm not sure if it is correct. I don't understand the output, which appears to be some kind of proc object.
Could someone please explain what I need to change to get the desired output?
I'm not sure how your first example is even running, as it produces a NameError on my machine. Regardless, #method is intended for accessing methods on specific objects. You've defined a standalone method which is already curried, not one inside of the Fixnum class. So you simply need to call it as a method.
add1 = fnAdd(1)
Also, Ruby has the same behavior as Scala with regard to returning the last expression in a method, so you don't need to use return in this case.
Edit:
Thanks to #JörgWMittag for pointing out a few flaws here. Defining #fnAdd at the top-level makes it a private instance method on Object. Since everything in Ruby is an object, Fixnum inherits from the Object class. Thus, 1.method(:fnAdd) is simply giving you the fnAdd method without actually passing it any arguments. Thus, it still expects to be called twice.
fnAddMethod = 1.method(:fnAdd)
add1 = fnAddMethod.call(1)
puts add1.call(10)
However, this would be extremely unidiomatic, so it's best to stick with the simpler solution.

Bang methods in Groovy

Does Groovy have something similar to bang methods on Ruby?
From this blog post:
In Ruby, you can write methods whose names end in ! (exclamation point or “bang”). There’s a lot of confusion surrounding the matter of when, and why, you would want to do so.
The ! in method names that end with ! means, “This method is dangerous”—or, more precisely, this method is the “dangerous” version of an otherwise equivalent method, with the same name minus the !. “Danger” is relative; the ! doesn’t mean anything at all unless the method name it’s in corresponds to a similar but bang-less method name.*
And this site:
You'll find a number of pairs of methods, one with the bang and one without. Those without the bang perform an action and return a freshly minted object, reflecting the results of the action (capitalizing a string, sorting an array, and so on). The bang versions of the same methods perform the action, but they do so in place: Instead of creating a new object, they transform the original object.
This is not a convention in Groovy like it is in Ruby. However you can write methods with names that contain characters like ! with the limitation that it must always be quoted like a string:
// define method with quoted name
def 'dangerous!'() {
// do something dangerous
}
// invoke method with quoted name
'dangerous!'()
No, groovy (currently as of v2.1.4) doesn't have anything like this
To add to your options, another solution that would be more Groovy-like or Java-like would be to include an optional parameter that enabled in-place (a.k.a. dangerous) modification, like so:
def processFoo(Foo item, mutate = false) {
if(!mutate) {
Foo temp = new Foo()
// copy item properties
item = temp
}
item.bar = 'blah blah'
// process item here
return item
}
processFoo(myFoo) // makes a copy
processFoo(myFoo, true) // modifies original
This pattern is used — albeit in the opposite manner — with the sort method on collections. Calling sort(false) on Lists prevents changing the original array. Calling sort() or sort(true) will modify it directly.

Overriding default range output

Right now the code below produces the output below it, but how would I override the default output to a more logical one for my given situation. I understand that I could just append the string "Hz" after the range but I want to incorporate this into a module which can be included to the Range class when needed or for use with refinements.
Code:
("20Hz"..."40Hz").each { |hz| p hz }
Output:
"20Hz"
"20Ia"
"20Ib"
...etc
Wanted output:
"20Hz"
"21Hz"
"22Hz"
...etc
This is absolutely a bad idea, but just for the sake of experimenting:
class String
alias_method :succ_orig, :succ
def succ
self.gsub(/\d+/, &:succ_orig)
end
end
p ("20Hz".."40Hz").to_a
#=> ["20Hz", "21Hz", "22Hz", "23Hz", "24Hz", "25Hz", "26Hz", "27Hz", "28Hz", "29Hz", "30Hz", "31Hz", "32Hz", "33Hz", "34Hz", "35Hz", "36Hz", "37Hz", "38Hz", "39Hz", "40Hz"]
As you can see, it is not the Range class that should be altered, but String#succ method.
But in real project, you better create a class for your Hertz-strings and define its succ method appropriately.
I think its quite simple.
("20"..."40").each { |hz| p hz + 'Hz'}
I would recommend creating your own function or class for this rather that changing the way in which Ruby ranges behave. There is probably a lot of other code that depends on ranges working in a specific way, and changing the range definition would result in that code breaking. You might want to aim for something like this:
HzRange.new("20Hz", "40Hz").each{ |hz| p hz }
The creation of the HzRange class is up to you, but you should probably delegate to the Array or Range object so that you can inherit some default behavior like Enumerable.

Defining Lua methods as initialization

In the Lua language, I am able to define functions in a table with something such as
table = { myfunction = function(x) return x end }
I wondered if I can created methods this way, instead of having to do it like
function table:mymethod() ... end
I am fairly sure it is possible to add methods this way, but I am unsure of the proper name of this technique, and I cannot find it looking for "lua" and "methods" or such.
My intention is to pass a table to a function such as myfunction({data= stuff, name = returnedName, ?method?init() = stuff}).
Unfortunately I have tried several combinations with the colon method declaration but none of them is valid syntax.
So...anyone here happens to know?
Sure: table:method() is just syntactic sugar for table.method(self), but you have to take care of the self argument. If you do
tab={f=function(x)return x end }
then tab:f(x) won't work, as this actually is tab.f(tab,x) and thus will return tab instead of x.
You might take a look on the lua users wiki on object orientation or PiL chapter 16.

Resources