Using Ruby to solve a quiz - ruby

So I found this quiz on a website that I was excited to solve with my newly acquired Ruby skills (CodeAcademy, not quite finished yet).
What I want to do is make an array with 100 entries, all set to "open". Then, I planned to create a method containing a for loop that iterates through every nth entry of the array and changes it to either "open" or "closed", based on what it was before. In the for loop, n should be increased from 1 to 100.
What I have so far is this:
change_state = Proc.new { |element| element == "open" ? element = "closed" : element = "open" }
def janitor(array,n)
for i in 1..n
array.each { |element| if array.index(element) % i == 0 then element.change_state end }
end
end
lockers = [*1..100]
lockers = lockers.map{ |element| element = "closed" }
result = janitor(lockers,100)
When trying to execute I receive an error saying:
undefined method `change_state' for "closed":String (NoMethodError)
Anybody an idea what is wrong here? I kinda think I'm calling the "change_state" proc incorrectly on the current array element.
If you know the quiz, no spoilers please!

As you have implemented change_state, it is not a method of any class, and definitely not one attached to any of the individual elements of the array, despite you using the same variable name element. So you cannot call it as element.change_state.
Instead, it is a variable pointing to a Proc object.
To call the code in a Proc object, you would use the call method, and syntax like proc_obj.call( params ) - in your case change_state.call( element )
If you just drop in that change, your error message will change to:
NameError: undefined local variable or method `change_state' for main:Object
That's because the change_state variable is not in scope inside the method, in order to be called. There are lots of ways to make it available. One option would be to pass it in as a parameter, so your definition for janitor becomes
def janitor(array,n,state_proc)
(use the variable name state_proc inside your routine instead of change_state - I am suggesting you change the name to avoid confusing yourself)
You could then call it like this:
result = janitor(lockers,100,change_state)
Although your example does not really need this structure, this is one way in which Ruby code can provide a generic "outer" function - working through the elements of an array, say - and have the user of that code provide a small internal custom part of it. A more common way to achieve the same result as your example is to use a Ruby block and the yield method, but Procs also have their uses, because you can treat them like data as well as code - so you can pass them around, put them into hashes or arrays to decide which one to call etc.
There may be other issues to address in your code, but this is the cause of the error message in the question.

Related

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.

ruby method with for loop, unexpected return

I have a model MyModel with a method to return a specific record (see logic below).
def self.find_future_rec #note2
rec = find(rand(MyModel.count)+1) #note1
while rec.nil? | (rec.expdate<Date.today)
rec = find(rand(MyModel.count)+1)
end
return rec
end
Every record of MyModel class has a variable expdate of Date class. (I know this is a horrible way to find a record, this is more for my own edification and also some test code.)
This method will iterate through several undesirable records before finding an appropriate record, but the record returned is always the one found at note 1, the first record queried. Is there some lazy assignment thing going on here?
I had to add self at note2 to be able to call the method in a static context. Is this the correct interpretation?
The find method never returns nil when you give it a single ID to find: if it can't find the record you're asking for, it raises an ActiveRecord::RecordNotFound exception. So, rec is always non-nil and rec.nil? is always false. That means that your loop is really like this:
while rec.expdate < Date.today
If your loop is always returning the rec from #note1 then you're never entering the while loop at all and you're always getting a desirable MyModel on the first try.
Other points to consider:
Sometimes things get deleted so Model.count + 1 is not necessarily the maximum ID.
find raises an exception to indicate failure so you need to rescue ActiveRecord::RecordNotFound rather than check for nil.
You are using | which is a boolean OR. Try using or or ||.
Adding self to make a method a class method ("static" is a decent approximation) is indeed one of the correct ways to approach this.

calling inject only passing it a symbol

Could someone explain this with an example of how it is useful.
Specifically I am having an issue with how you accumulate something (an array would be nice) when you change scope into a method..
e.g.
def modify(value)
...code ....
end
an_array.inject(:modify)
How can I get an accumulator (above), or something that is passing a message along. The value returned is the last value to come out of modify. But modify only gets each value in the array. It doesn't get the message. (below) passing message to the .... code ...
an_array.inject(0) { |message,element| .... code .... }
Using inject with only a symbol:
[*1..5].inject(:+) #=> 15
The above translates to:
[*1..5].inject { |sum, num| sum + num }
This is what the docs have to say on the subject (emphasis added):
Combines all elements of enum by applying a binary operation,
specified by a block or a symbol that names a method or operator
I'm happy to update my answer once you clarify the second part of your question a bit (e.g. what do you mean by "changing scope" in this context?).

Is there a hook for when anonymous classes are assigned to a constant?

I've been practicing some Ruby meta-programming recently, and was wondering about assigning anonymous classes to constants.
In Ruby, it is possible to create an anonymous class as follows:
anonymous_class = Class.new # => #<Class:0x007f9c5afb21d0>
New instances of this class can be created:
an_instance = anonymous_class.new # => #<#<Class:0x007f9c5afb21d0>:0x007f9c5afb0330>
Now, when the anonymous class is assigned to a constant, the class now has a proper name:
Foo = anonymous_class # => Foo
And the previously created instance is now also an instance of that class:
an_instance # => #<Foo:0x007f9c5afb0330>
My question: Is there a hook method for the moment when an anonymous class is assigned to a constant?
There are many hooks methods in Ruby, but I couldn't find this one.
Let's take a look at how constant assignment works internally. The code that follows is extracted from a source tarball of ruby-1.9.3-p0. First we look at the definition of the VM instruction setconstant (which is used to assign constants):
# /insns.def, line 239
DEFINE_INSN
setconstant
(ID id)
(VALUE val, VALUE cbase)
()
{
vm_check_if_namespace(cbase);
rb_const_set(cbase, id, val);
INC_VM_STATE_VERSION();
}
No chance to place a hook in vm_check_if_namespace or INC_VM_STATE_VERSION here. So we look at rb_const_set (variable.c:1886), the function that is called everytime a constant is assigned:
# /variable.c, line 1886
void
rb_const_set(VALUE klass, ID id, VALUE val)
{
rb_const_entry_t *ce;
VALUE visibility = CONST_PUBLIC;
# ...
check_before_mod_set(klass, id, val, "constant");
if (!RCLASS_CONST_TBL(klass)) {
RCLASS_CONST_TBL(klass) = st_init_numtable();
}
else {
# [snip], won't be called on first assignment
}
rb_vm_change_state();
ce = ALLOC(rb_const_entry_t);
ce->flag = (rb_const_flag_t)visibility;
ce->value = val;
st_insert(RCLASS_CONST_TBL(klass), (st_data_t)id, (st_data_t)ce);
}
I removed all the code that was not even called the first time a constant was assigned inside a module. I then looked into all the functions called by this one and didn't find a single point where we could place a hook from Ruby code. This means the hard truth is, unless I missed something, that there is no way to hook a constant assignment (at least in MRI).
Update
To clarify: The anonymous class does not magically get a new name as soon as it is assigned (as noted correctly in Andrew's answer). Rather, the constant name along with the object ID of the class is stored in Ruby's internal constant lookup table. If, after that, the name of the class is requested, it can now be resolved to a proper name (and not just Class:0xXXXXXXXX...).
So the best you can do to react to this assignment is to check the name of the class in a loop of a background worker thread until it is non-nil (which is a huge waste of resources, IMHO).
Anonymous classes don't actually get their name when they're assigned to a constant. They actually get it when they're next asked what their name is.
I'll try to find a reference for this. Edit: Can't find one, sorry.

Resources