How to write block in IRB? - ruby

For example, I want to implement this code in IRB, but it has single-line input so I can get how to write block there.
a = [3, 2, 1]
a[3] = a[2] - 1
a.each do |elt|
print elt+1
end

(Oh you mean IRB)
If you enter something that will be on multiple lines, ruby will wait until the final end is completed before running the code:
irb(main):001:0> def dostuff
irb(main):002:1> puts "things"
irb(main):003:1> end
=> :dostuff
irb(main):004:0> dostuff
things
=> nil
irb(main):005:0>
As you can see the number at the prompt changes depending on how deep the block-level is.

Related

How do I iterate over a file?

I have a file that looks like this:
[noahc:~/projects/wordsquares] master(+87/-50)* ± cat wordlist/test_word_list.txt
card
apple
joe
bird
card
dart
area
rear
birdbird
after
boat
swim
north
abbe
byes
beep
If I open up an irb session, I can do:
2.0.0p247 :001 > file = File.open('./wordlist/test_word_list.txt', 'r')
=> #<File:./wordlist/test_word_list.txt>
2.0.0p247 :002 > file.readlines
=> ["card\n", "apple\n", "joe\n", "bird\n", "card\n", "dart\n", "area\n", "rear\n", "birdbird\n", "after\n", "boat\n", "swim\n", "north\n", "abbe\n", "byes\n", "beep \n", "\n"]
But, now I have a class called WordSquareGenerator:
class WordSquareGenerator
require 'pry'
require 'pry-nav'
require './lib/word_list_builder.rb'
def initialize(n, file_location)
#size_of_square = n
#file = load_file(file_location)
#word_stem_hash = WordListBuilder.new(n, #file).word_stem_hash
#word_list = nil
end
def word_square_word_list
binding.pry
#file.each do |w|
binding.pry
#word_list ? break : solve_for_word_list([word])
end
binding.pry
end
def is_list_valid?(list)
(0..#size_of_square - 1).each do |n|
(0..#size_of_square - 2).each do |m|
return false if list[n][m] != list [m][n]
end
end
#generated_list = list unless #generated_list
end
def solve_for_word_list(word_array)
if word_array.length == 4
#word_list = word_array
elsif #word_list
else
next_words = #word_stem_hash[word_array.map{|w| w[word_array.length]}.join]
next_words.each do |word|
solve_for_word_list(word_array + [word])
end
end
end
private
def load_file(file_location)
File.open(file_location, 'r')
end
end
When I run the word_square_word_list method and hit the first binding.pry, I can do:
2.0.0 (#<WordSquareGenerator:0x007ff3f91c16b0>):0 > #file.readlines
=> []
and I get an empty array for readlines. How can it be that I'm getting two different results doing the same thing, except one is inside that class and the other isn't?
At WordListBuilder you are probably reading the file already and when the action gets back to WordSquareGenerator the file is already at it's end and there's nothing else to read. First, don't do what you're doing now, that is opening the file since this leaks the file handle (you're not closing it anywhere) and someone else reading the handle is causing your code to fail.
Here's how you could do it:
class WordSquareGenerator
require 'pry'
require 'pry-nav'
require './lib/word_list_builder.rb'
def initialize(n, file_location)
#size_of_square = n
#file_location = file_location
#word_stem_hash = WordListBuilder.new(n, file_location).word_stem_hash
#word_list = nil
end
def word_square_word_list
binding.pry
IO.foreach(#file_location) do |word|
binding.pry
#word_list ? break : solve_for_word_list([word])
end
binding.pry
end
def is_list_valid?(list)
(0..#size_of_square - 1).each do |n|
(0..#size_of_square - 2).each do |m|
return false if list[n][m] != list [m][n]
end
end
#generated_list = list unless #generated_list
end
def solve_for_word_list(word_array)
if word_array.length == 4
#word_list = word_array
elsif #word_list
else
next_words = #word_stem_hash[word_array.map{|w| w[word_array.length]}.join]
next_words.each do |word|
solve_for_word_list(word_array + [word])
end
end
end
end
And you would also need to update WordListBuilder to do the same. This also has the advantage of closing the file handle automatically for you so you don't have to care about closing it yourself.
Try running this before you read the lines:
#file.seek 0
You have probably already read the lines, and you have to seek back to the start of the file before you read them again.
Sample IRB session:
irb(main):001:0> f = File.new 'test.txt' # already existing file
=> #<File:test.txt>
irb(main):002:0> f.readlines
=> ["this", "is", "a", "test"]
irb(main):003:0> f.readlines
=> []
irb(main):004:0> f.seek 0
=> 0
irb(main):005:0> f.readlines
=> ["this", "is", "a", "test"]
You could even make it a method:
def readlines
#file.seek 0
#file.readlines
end
Also, make sure to close your file (#file.close)! You should only leave it open for as little time as possible. And you definitely should not leave it open for the whole program. If you don't want to worry about that, just store the lines in a variable instead of keeping the file:
#lines = File.open(file_location) {|f|
f.readlines
}
# you could also use the shortcut form
# #lines = File.open(file_location, &:readlines)

Ruby: methods as array elements - how do they work?

This probably isn't something you should try at home, but for some reason or another I tried to create an array of methods in Ruby.
I started by defining two methods.
irb(main):001:0> def test1
irb(main):002:1> puts "test!"
irb(main):003:1> end
=> nil
irb(main):004:0> def test2
irb(main):005:1> puts "test2!"
irb(main):006:1> end
=> nil
The weird thing happens when you try to put it into an actual array. It seems to run both methods.
irb(main):007:0> array = [test1, test2]
test!
test2!
=> [nil, nil]
And afterwards, the array is empty.
irb(main):008:0> puts array
=> nil
Can someone explain to me why it runs the methods? Other than that the whole excercise is seriously in need of an exorcist?
What you're storing in your array is the result of calling your methods, not the methods themselves.
def test1
puts "foo!"
end
def test2
puts "bar!"
end
You can store references to the actual methods like this:
> arr = [method(:test1), method(:test2)]
# => [#<Method: Object#test1>, #<Method: Object#test2>]
Later, you can call the referenced methods like this:
> arr.each {|m| m.call }
foo!
bar!
#alestanis explained the reason well. If you were trying to store the methods, then you can do what Lars Haugseth says or you could do the folllowing:
test1 = Proc.new { puts "test!" }
test2 = Proc.new { puts "test2!" }
a = [test1, test2]
This may make your code much more readable.
Here is an irb run.
1.9.3p194 :009 > test1 = Proc.new { puts "test!" }
=> #<Proc:0x00000002798a90#(irb):9>
1.9.3p194 :010 > test2 = Proc.new { puts "test2!" }
=> #<Proc:0x00000002792988#(irb):10>
1.9.3p194 :011 > a = [test1, test2]
=> [#<Proc:0x00000002798a90#(irb):9>, #<Proc:0x00000002792988#(irb):10>]
Your array never contains anything else than two nil values. I tricks you by putting the strings when evaluating. But the return value of each function still is nil.
Your code runs the two methods because you're actually calling the methods when you say "test1" and "test2" - parentheses are optional for ruby method calls.
Since both of your methods just contain a "puts", which returns nil, your resulting array is just an array of two nils.
If you had a square method and wanted to create an array with the square values of 2 and 4, you would write
array = [square(2), square(4)]
Here you are doing exactly the same thing, except that your test methods don't return anything and that's why your final array seems empty (actually, it contains [nil, nil]).
Here's my two-pennies worth. Building on the solutions already posted, this is an example of a working example. What might be handy for some here is that it includes method arguments and the use of self (which refers to the instance of the PromotionalRules class when it is instantiated) and the array of symbols, which is neat - I got that from the Ruby docs on the #send method here. Hope this helps someone!
class PromotionalRules
PROMOTIONS = [:lavender_heart_promotion, :ten_percent_discount]
def apply_promotions total, basket
#total = total
if PROMOTIONS.count > 0
PROMOTIONS.each { |promotion| #total = self.send promotion, #total, basket }
end
#total.round(2)
end
def lavender_heart_promotion total, basket
if two_or_more_lavender_hearts? basket
basket.map { |item| total -= 0.75 if item == 001 }
end
total
end
def two_or_more_lavender_hearts? basket
n = 0
basket.each do |item|
n += 1 if item == 001
end
n >= 2
end
def ten_percent_discount total, *arg
if total > 60.00
total = total - total/10
end
total
end
end
Thanks to everyone for their help. I love the open-source nature of coding - threads just get better and better as people iterate over each other's solutions!

irb does not print anything

I'm a newbie at ruby.
While I am using irb, something happens.(Nothings are printed)
Does anyone have any advise about this?
I cannot know even what search keyword would be OK for this situation.
(maybe an environment-specific problem? How do you think?)
irb(main):010:0> a = [3,2,1]
=> [3, 2, 1]
irb(main):011:0> a.each
=> #<Enumerable::Enumerator:0x7f413a20d668>
irb(main):012:0> a.each{|x| print x}
321=> [3, 2, 1]
irb(main):013:0> a.each do |x| print x end
321=> [3, 2, 1]
irb(main):014:0> 1.to 9
NoMethodError: undefined method `to' for 1:Fixnum
from (irb):14
from :0
irb(main):015:0> 1.to(9) do |x| print x done
irb(main):016:1> 1.to(9) { |x| print x }
irb(main):017:1> 1.to(9)
irb(main):018:1> 1.upto(9)
irb(main):019:1> 1.upto(9) do |x| print x done
irb(main):020:2> 1.upto(9) { |x| print x }
irb(main):021:2> print "x"
irb(main):022:2> abc
irb(main):023:2> a
irb(main):024:2> b
What happened is that after the error, you typed done instead of end.
Nothing was executed until the block was parsed, but the end never came, so irb just kept reading stuff...
In the future just type ^C or ^D until you get back to the top level or the shell, and then start over.
IRB is waiting for something to close (in this case the do block on line 15 needs an end).
You can notice this by watching the number after the line number (:0, :1, :2...): while it's positive, IRB wants you to close something.
You can press Ctrl+C to abort the current command and start a new one.

How do I make my code remember the current position and next time show the next element?

In Python there is iter() used like this:
>>> a=[1,2,4]
>>> b=iter(a)
>>> b.next()
1
>>> b.next()
2
>>> b.next()
4
>>> b.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
Does Ruby have the same feature?
I tried this but it seems there is an issue:
ruby-1.9.2-p0 > a=[1,2,3]
=> [1, 2, 3]
ruby-1.9.2-p0 > def b()
ruby-1.9.2-p0 ?> for i in a
ruby-1.9.2-p0 ?> yield i
ruby-1.9.2-p0 ?> end
ruby-1.9.2-p0 ?> end
=> nil
ruby-1.9.2-p0 > b
NameError: undefined local variable or method `a' for #<Object:0xb7878950>
Why didn't Ruby find the a variable?
Ruby has iterators also.
The basic use is:
>> iter = [0,1,2,3].each #=> #<Enumerator: [0, 1, 2, 3]:each>
>> iter.next #=> 0
>> iter.next #=> 1
>> iter.next #=> 2
>> iter.next #=> 3
>> iter.next
StopIteration: iteration reached an end
from (irb):6:in `next'
from (irb):6
from /Users/greg/.rvm/rubies/ruby-1.9.2-p0/bin/irb:16:in `<main>'
>>
You can use that in a method:
def iter(ary)
ary.each do |i|
yield i
end
end
iter([1,2,3]) { |i| puts i}
# >> 1
# >> 2
# >> 3
Your Ruby code is failing because a is not in scope, in other words, Ruby doesn't see a inside the b method. The typical way it would be defined is as I show it above. So, your code is close.
Also, note that we seldom write a for/loop in Ruby. There are reasons such as for loops leaving a local variable behind after running, and potential for running off the end of an array if the loop isn't defined correctly, such as if you are creating an index to access individual elements of an array. Instead we use the .each iterator to return each element in turn, making it impossible to go off the end, and not leaving a local variable behind.
Working with the code which you provided, and assuming that you want the values to be printed out:
a = [1, 2, 3]
def b(a)
a.each { |i| puts i }
end
b(a)
(There are much better ways, as Mark Thomas pointed out)
[1,2,4].each { |i| puts i }

Are there something like Python generators in Ruby?

I am new to Ruby, is there a way to yield values from Ruby functions? If yes, how? If not, what are my options to write lazy code?
Ruby's yield keyword is something very different from the Python keyword with the same name, so don't be confused by it. Ruby's yield keyword is syntactic sugar for calling a block associated with a method.
The closest equivalent is Ruby's Enumerator class. For example, the equivalent of the Python:
def eternal_sequence():
i = 0
while True:
yield i
i += 1
is this:
def eternal_sequence
Enumerator.new do |enum|
i = 0
while true
enum.yield i # <- Notice that this is the yield method of the enumerator, not the yield keyword
i +=1
end
end
end
You can also create Enumerators for existing enumeration methods with enum_for. For example, ('a'..'z').enum_for(:each_with_index) gives you an enumerator of the lowercase letters along with their place in the alphabet. You get this for free with the standard Enumerable methods like each_with_index in 1.9, so you can just write ('a'..'z').each_with_index to get the enumerator.
I've seen Fibers used in that way, look at an example from this article:
fib = Fiber.new do
x, y = 0, 1
loop do
Fiber.yield y
x,y = y,x+y
end
end
20.times { puts fib.resume }
If you are looking to lazily generate values, #Chuck's answer is the correct one.
If you are looking to lazily iterate over a collection, Ruby 2.0 introduced the new .lazy enumerator.
range = 1..Float::INFINITY
puts range.map { |x| x+1 }.first(10) # infinite loop
puts range.lazy.map { |x| x+1 }.first(10) # [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
Ruby supports generators out of the box using Enumerable::Generator:
require 'generator'
# Generator from an Enumerable object
g = Generator.new(['A', 'B', 'C', 'Z'])
while g.next?
puts g.next
end
# Generator from a block
g = Generator.new { |g|
for i in 'A'..'C'
g.yield i
end
g.yield 'Z'
}
# The same result as above
while g.next?
puts g.next
end
https://ruby-doc.org/stdlib-1.8.7/libdoc/generator/rdoc/Generator.html
Class Enumerator and its method next behave similar
https://docs.ruby-lang.org/en/3.1/Enumerator.html#method-i-next
range = 1..Float::INFINITY
enumerator = range.each
puts enumerator.class # => Enumerator
puts enumerator.next # => 1
puts enumerator.next # => 2
puts enumerator.next # => 3

Resources