Recursive lambdas in Ruby - ruby

I have the following code which correctly generates all possible trees of size num:
class Tree
attr_accessor :left, :right
def initialize left = nil, right = nil
#left = left
#right = right
end
# Don't ever specify any arguments, it will make me very angry.
# Tilt your head 90 degrees to the side to see the tree when viewing.
def print level = 0
#right.pretty_print(level + 1) if #right
puts (' ' * level) + to_s
#left.pretty_print(level + 1) if #left
end
def self.generate num
trees = []
generate_subtrees(num) { |tree| trees << tree } if num > 0
trees
end
private
def self.generate_subtrees num, &block
if num == 0
yield nil
else
(1..num).each do |root_position|
generate_subtrees(root_position - 1) do |left|
generate_subtrees(num - root_position) do |right|
yield Tree.new nil, left, right
end
end
end
end
end
end
I’m trying to (for the sake of it) “condense” this into one method, utilizing lambda recursion. My current attempt (of several iterations) is below:
def self.generate num
trees = []
gen = ->(num, &block) do
if num == 0
yield nil # L61
else
(1..num).each do |root_position| # L63
gen.call(root_position - 1) do |left| # L64
gen.call(num - root_position) do |right|
block.call { Tree.new nil, left, right }
end
end
end
end
end
gen.call(num) { |tree| trees << tree } # L73
trees
end
This results in the error (referenced lines noted above):
LocalJumpError: no block given (yield)
from tree.rb:61:in `block in generate'
from tree.rb:64:in `call'
from tree.rb:64:in `block (2 levels) in generate'
from tree.rb:63:in `each'
from tree.rb:63:in `block in generate'
from tree.rb:73:in `call'
from tree.rb:73:in `generate'
from (irb):4
from /Users/amarshall/.rbenv/versions/1.9.2-p290/bin/irb:12:in `<main>'
What am I doing wrong? Alternative solutions to this mostly academic problem are also welcome.

The yield keyword does not work from inside a lambda. The alternative is to use &block, in the same way that you are already doing on line 64 and 65:
gen = ->(num, &block) do
if num == 0
block.call(nil)
else
# ...
end
gen.call(num) { |tree| trees << tree }

Related

How to wrap a method that yields in Ruby 1.9

I have a method that prints out a numbered list, yielding to a code block to print a prefix.
arr = %w(a b c)
def print_lines(array)
array.each_with_index do |item, index|
prefix = yield index
puts "#{prefix} #{item}"
end
end
print_lines(arr) do |index|
"(#{index})"
end
This produces the following output:
(0) a
(1) b
(2) c
Now I want to wrap print_lines in another method and call it.
def print_lines_wrapped(array)
puts 'print_lines_wrapped'
print_lines(array)
end
print_lines_wrapped(arr) do |index|
"(#{index})"
end
However, this gives me a LocalJumpError
test_yield.rb:5:in `block in print_lines': no block given (yield) (LocalJumpError)
from test_yield.rb:4:in `each'
from test_yield.rb:4:in `each_with_index'
from test_yield.rb:4:in `print_lines'
from test_yield.rb:16:in `print_lines_wrapped'
from test_yield.rb:19:in `<main>'
Why do I get a LocalJumpError?
How can I implement print_lines_wrapped such that I can call it like this:
print_lines_wrapped(arr) do |index|
"(#{index})"
end
and get the following output:
print_lines_wrapped
(0) a
(1) b
(2) c
?
Your wrapper method also has to accept a block and pass it to the wrapped method. There is no implicit passing of the block:
def print_lines_wrapped(array, &block)
puts 'print_lines_wrapped'
print_lines(array, &block)
end
Example:
def asdf(&block) puts yield(2) end
def qwer(&block)
puts "I am going to call asdf"
asdf &block
end
asdf { |x| x * 3 }
6
=> nil
qwer { |x| x * 5 }
I am going to call asdf
10
=> nil
The & operator converts its operand into a block if possible
qwer &Proc.new { |x| x * 2 }
I am going to call asdf
4

run a ruby project on ruby mine

i am new to ruby programming. i wrote a little program to recursively count blobs in a two dimensional array. it contains two classes, cell and blobs. i am getting the following error and i dont know what it means or how to fix it.
attached is my code and the error.
Cell Class
class Cell
attr_accessor :data,:visited,:row,:col
def initialize(data, row, col)
#data = data
#visited = false
#row = row
#col = col
end
def to_s
self.data.to_s
end
end
Blob class
class Blobs
require ./Cell
#cells = Array.new(10){Array.new(10)}
def separate(percentage)
for i in 0..10
for i in 0..10
random = Random.rand(0,100)
if random < percentage
#cells[i][j] = Cell.new('x',i,j)
else
#cells[i][j] = Cell.new(nil, i, j)
end
end
end
end
def markBlob(currentCell)
if currentCell.visited
return
end
currentCell.visited = true
if currentCell.data.nil?
return
end
if currentCell.row >0
markBlob(#cells[currentCell.row-1][currentCell.col])
end
if currentCell.row <#cells.size-1
markBlob(#cells[currentCell.row+1][currentCell.col])
end
if currentCell.col>0
markBlob(#cells[currentCell.row][currentCell.col-1])
end
if currentCell.col<#cells.size-1
markBlob(#cells[currentCell.row][currentCell.col+1])
end
end
def countblobs
count = 0
for i in 0..10
for j in 0..10
cell = #cells[i][j]
if !cell.visited && cell.data.nil?
count++
markBlob(cell)
end
end
end
return count
end
def to_s
for i in 0..10
for j in 0..10
if #cells[i][j].data.nil?
puts '- '
else
puts #cells[i][j].data + ' '
end
end
puts "\n"
end
end
blob = Blobs.new
number = blob
puts number
end
this is the error i am getting:
C:\Ruby22\bin\ruby.exe -e $stdout.sync=true;$stderr.sync=true;load($0=ARGV.shift) C:/Users/Nechama/RubymineProjects/blobs/blobs.rb
C:/Ruby22/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:38:in `require': wrong number of arguments (0 for 1) (ArgumentError)
from C:/Users/Nechama/RubymineProjects/blobs/blobs.rb:3:in `<class:Blobs>'
from C:/Users/Nechama/RubymineProjects/blobs/blobs.rb:2:in `<top (required)>'
from -e:1:in `load'
from -e:1:in `<main>'
Process finished with exit code 1
require should get string as an argument.
Use require_relative to cell file
require_relative 'cell'
and put it above class Blobs.
More about Including Other Files In Ruby

Why is rspec not seeing these class methods?

If I have ruby file maze.rb with
class Maze
def self.x
end
def self.y
end
end
and a rspec file with
require 'maze'
describe "A Maze" do
it "exists" do
expect(Maze).to be
end
it " has x-y dimension" do
expect(Maze.x).to be
expect(Maze.y).to be
end
end
Why does the test for Maze.x fail ?
Failures:
1) A Maze has x-y dimension
Failure/Error: expect(Maze.x).to be
expected nil to evaluate to true
# ./spec/maze_spec.rb:8:in `block (2 levels) in <top (required)>'
It is working.
What's happening is that the class level method isn't doing anything and thus returns nil - as opposed to method not found. Simply adding true as the return value resolves this, i.e.
def x
true
end

Ruby - How to populate a 2d array?

I have this rspec test:
it 'has a populated chessboard' do
expect(ChessBoard.new.populate_new_board).to eq [
['pawn','pawn','pawn','pawn','pawn','pawn','pawn','pawn'],
['pawn','pawn','pawn','pawn','pawn','pawn','pawn','pawn'],
['pawn','pawn','pawn','pawn','pawn','pawn','pawn','pawn'],
['pawn','pawn','pawn','pawn','pawn','pawn','pawn','pawn'],
['pawn','pawn','pawn','pawn','pawn','pawn','pawn','pawn'],
['pawn','pawn','pawn','pawn','pawn','pawn','pawn','pawn'],
['pawn','pawn','pawn','pawn','pawn','pawn','pawn','pawn'],
['pawn','pawn','pawn','pawn','pawn','pawn','pawn','pawn']]
end
For this code:
class ChessBoard
def initialize
#board=Array.new(7){Array.new(7)}
end
def populate_new_board
(0..7).each do |row|
(0..7).each do |cell|
#board[row][cell]='pawn'
end
end
#board
end
end
but I'm getting:
1) least number of moves from x to y has a populated chessboard
Failure/Error: expect(ChessBoard.new.populate_new_board).to eq [
NoMethodError:
undefined method `[]=' for nil:NilClass
# ./code.rb:10:in `block (2 levels) in populate_new_board'
# ./code.rb:9:in `each'
# ./code.rb:9:in `block in populate_new_board'
# ./code.rb:8:in `each'
# ./code.rb:8:in `populate_new_board'
# ./code_spec.rb:12:in `block (2 levels) in <top (required)>'
how can I fix this?
btw pawns in every space is not the final result but it's what I want for this test right now (then I can modify it further).
class ChessBoard
def populate_new_board
#board = [['pawn'] * 7] * 7
end
end
Change it to:
def populate_new_board
(0...7).each do |row|
(0...7).each do |cell|
#board[row][cell]='pawn'
end
end
#board
end
As it was denoted that you did a mistake in range, but a strongly advice you to reduce usage of index ranges. You can use :each, and :map methods instead:
class ChessBoard
def initialize
#board = Array.new( 7 ){ Array.new( 7 ) }
end
def populate_new_board
#board.each {| row | row.map! {| _ | 'pawn' } }
end
end
But I'd use more the simple code:
class ChessBoard
def populate_new_board
#board = Array.new( 7 ){Array.new( 7 ) { 'pawn' } }
end
end

Matching end tags in Ruby

To learn Ruby, I'm implementing different data structures starting with nodes and a simple stack. If I matching each def with a corresponding end, there are lots of error about expecting $end (EOF) but getting end. So I could fix it by stacking some ends at the end of the class, but obviously I don't know why that works.
require "Node"
class Stack
attr_accessor :top
def size
#size
end
def push(node)
if node && node.next
node.next = top
top = node
end
size++
def pop()
if top != nil
top = top.next
end
size--
def to_s
if top != nil
temp = top
while temp != nil
puts temp.value
temp = temp.next
end
else
puts "The stack is empty"
end
end
end
end
end
The node class is very simple and shouldn't cause any problems:
class Node
attr_accessor :next
def initialize(value)
#value = value
end
end
Everything works fine on that Frankenstein Stack, except pushing a node results in NoMethodError: undefined method +#' for nil:NilClass. Not sure if that is related, but I'm mostly concerned with the syntax of method/class declaration and using end
You get an error because ruby does not have ++ and -- operators.
Ruby understand the following constructs
size++
def pop()
# and
size--
def to_s()
like
size + +def pop()
# and
size - -def to_s()
Ruby syntax is expression-oriented and method definition is expression in Ruby. Method definition expressions (def pop() and def to_s()) are evaluated to nil (in your code you actually define method pop inside push method body and to_s inside pop method body). And this is why you get NoMethodError: undefined method +#' for nil:NilClass error - it evaluates expression size + +nil and nil does not define unary plus operator. In this expression first + is an Fixnum addition operator (size is Fixnum), and second + is unary plus operator of nil (result of def pop() expression).
Use += 1 and -= 1 instead of ++ and --. Your code should look like this:
class Stack
attr_accessor :top
def size
#size
end
def push(node)
if node && node.next
node.next = top
top = node
end
#size += 1 # #size, not `size` because you have `size` getter and you cannot modify size with getter method
end
def pop()
if top != nil
top = top.next
end
#size -= 1
end
def to_s
if top != nil
temp = top
while temp != nil
puts temp.value
temp = temp.next
end
else
puts "The stack is empty"
end
end
end
Your defs don’t have a matching end. Also, Ruby does not have a ++ operator; you’ll have to use += 1 instead.

Resources