function doesn't return, very weird - ruby

I have a function which gets an array of element, then it iterates over the array, when expected element is found it breaks and return.
The function is this:
def get_expected_element(id:, name:)
# I am sure there are 3 elements got
elem_array = get_all_elements(id)
element = nil
elem_array.each { |elem|
# I see this log
puts "elem = #{elem}"
if elem == name
element = elem
# I see this log too
puts "Found element"
break
end
}
# I see this log too, and program is hanging
puts "=== return ==="
element
end
When I invoke the function, the program is hanging after puts "=== return ===":
service = MyService.new
element_got = service.get_expected_element(id:3, name:"apple")
# I don't see the below log
puts "#{element_got}, I don't see this, why?"
The log in console is this:
elem = orange
elem = apple
Found element
=== return ===
<it is hanging>
I cannot understand why the invoked function doesn't return?

Leaving out MyService I ran this:
def get_expected_element(id:, name:)
# I am sure there are 3 elements got
# elem_array = get_all_elements(id)
elem_array = ["elem1", "apple", "elem3"]
element = nil
elem_array.each { |elem|
# I see this log
puts "elem = #{elem}"
if elem == name
element = elem
# I see this log too
puts "Found element"
break
end
}
# I see this log too, and program is hanging
puts "=== return ==="
element
end
puts get_expected_element(id: 3, name: "apple")
and got this:
elem = elem1
elem = apple
Found element
=== return ===
apple
Your get_expected_element method seems fine.

Related

Get local variables of previous scope

I am making a Ruby REPL to be used inside an application. I made code:
a = 1
b = 2
currentScope = []
Kernel.local_variables.each do |var|
currentScope << [var,Kernel.eval(var.to_s)]
end
launchREPL(currentScope)
Inside the REPL, I can execute the following code:
#a #=>1
#a+#b #=>3
Ideally I wouldn't have to write the four lines of code before I launch the REPL, and instead I would like to run them inside the launchREPL function. However this would require access to the previous scope from inside the launchREPL function.
Test1
Most notably I tried:
launchREPL(Kernel)
When I do the following:
def launchREPL(scope)
F = 0
puts scope.local_variables # => [:F]
end
it is apparent that this method is not valid.
Test2
launchREPL(Kernel.binding)
def launchREPL(scope)
Kernel.binding.local_variables #= Error: private method 'local_variables' called for #<Binding>
end
Is there any way to do what I'm trying to do?
Edit: P.S. This is currently the code inside launchREPL:
def launchREPL(scope=nil,winName="Ruby REPL")
# ICM RB file Begin:
puts "\"Starting REPL...\""
__b = binding #Evaluating in a binding, keeps track of local variables
__s = ""
###############################################################################
# SEND INSTANCE VARIABLES TO REPL
###############################################################################
#
#How to prepare scope
# currentScope = []
# Kernel.local_variables.each do |var|
# currentScope << [var,Kernel.eval(var.to_s)]
# end
# launchREPL(currentScope)
if scope != nil
scope.each do |varDef|
__b.instance_variable_set "##{varDef[0].to_s}" , varDef[1]
__b.eval("##{varDef[0].to_s} = __b.instance_variable_get(:##{varDef[0].to_s})")
end
end
# to get instance variables: __b.instance_variable_get(__b.instance_variables[0])
# or better: __b.instance_variable_get(:#pipe1)
#
###############################################################################
bStartup = true
while bStartup || __s != ""
# If startup required skip evaluation step
if !bStartup
#Evaluate command
begin
__ret = __s + "\n>" + __b.eval(__s).to_s
rescue
__ret = __s + "\n> Error: " + $!.to_s
end
puts __ret
else
#REPL is already running
bStartup = false
end
#Read user input & print previous output
__s = WSApplication.input_box(__ret,winName,"")
__s == nil ? __s = "" : nil
end
end
Although what you are trying to achieve is unclear and there are definitely many ways to do it properly, every ruby method might be called with Object#send approach:
def launchREPL(scope)
scope.send :local_variables #⇒ here you go
end
a = 42
launchREPL(binding).include?(:a)
#⇒ true
Sidenote: this is how your “4 lines” are usually written in ruby:
local_variables.map { |var| [var, eval(var.to_s)] }
And this is how they should be written (note Binding#local_variable_get):
local_variables.map { |var| [var, binding.local_variable_get(var)] }
The summing up:
def launchREPL(scope)
vars = scope.send(:local_variables).map do |var|
[var, scope.local_variable_get(var)]
end
# some other code
end
a = 42
launchREPL(binding).to_h[:a]
#⇒ 42
This won’t fit the comment, so I would post it as an answer.
def launchREPL(scope = nil, winName = "Ruby REPL")
puts '"Starting REPL..."'
scope.eval('local_variables').each do |var|
instance_variable_set "##{var}", scope.eval(var.to_s)
end if scope
s = ""
loop do
ret = begin
"#{s}\n> #{eval(s)}"
rescue => e
"#{s}\n> Error: #{e.message}"
end
puts ret
# s = WSApplication.input_box(ret, winName, "")
# break if s.empty?
s = "100 * #a" # remove this line and uncomment 2 above
end
end
a = 42
launchREPL(binding)
This is how your function should be written (I have just make it looking as ruby code.) The above works (currently it has no break at all, but you can see as it’s calculating 4200 infinitely.)

No puts or gets inside instance method - Ruby

This is really driving me up a wall.
I have an instance method I'm trying to debug, but I'm running into an issue where my puts and gets aren't showing up inside the instance method.
Code:
#! /usr/bin/env ruby
class Calculator
def evaluate(string)
ops = string.split(' ')
ops.map! do |item|
if item.is_a? Numeric
return item.to_i
else
return item.to_sym
end
end
puts "Got #{string}" #Doesn't output
puts "Converted to #{ops}" #This too
opscopy = ops.clone
ops.each.with_index do |item, index|
if item == :* || item == :/
opscopy[index] = ops[index-1].send(item, ops[index+1])
opscopy[index-1] = opscopy[index+1] = nil
end
end
ops = opscopy.compact
puts "After multi/div #{ops}"
ops.each.with_index do |item, index|
if item == :+ || item == :-
opscopy[index] = ops[index-1].send(item, ops[index+1])
opscopy[index-1] = opscopy[index+1] = nil
end
end
puts "After +/- #{opscopy.compact}"
opscopy.compact.first
end
end
item = Calculator.new.evaluate "4 * 2"
puts "#{item} == 8" #Prints :(
Output:
action#X:~/workspace/ruby$ ./calculator.rb
4 == 8
That return in your map! block is where the problem is.
ops.map! do |item|
if item.is_a? Numeric
return item.to_i # returns from method
else
return item.to_sym # returns from method
end
end
You are returning the method in your map! block before the puts is called.
Change the map! block to:
ops.map! do |item|
item.send(item.is_a?(Numeric) ? :to_i : :to_sym)
end

Passing a reference to a method in Ruby to another method

I have a function which loops through/transvers something, and I want it to recieve a reference to a function which sets the stop creiteria/do something.
For example, in a class:
def a(func_stop,i)
ret = nil # default
while(i < 0 )
if (func_stop(#lines[i]))
ret = i
break
end
end
return ret
end
The idea is that I can pass a reference to the function, kinda like PERL'S
func1(\&func, $i);
I have looked, but failed to find something like this.
Thanks
Normally it is done with blocks.
def a(max, &func_stop)
puts "Processing #{max} elements"
max.times.each do |x|
if func_stop.call(x)
puts "Stopping"
break
else
puts "Current element: #{x}"
end
end
end
Then
a(10) do |x|
x > 5
end
# >> Processing 10 elements
# >> Current element: 0
# >> Current element: 1
# >> Current element: 2
# >> Current element: 3
# >> Current element: 4
# >> Current element: 5
# >> Stopping
You can also try this:
def a(func_stop,i)
ret = nil # default
while(i < 0 )
if (func_stop.call(#lines[i]))
ret = i
break
end
end
return ret
end
a(method(:your_function), i)

Return a single value from a nested each block, trying to use 'return'

def get_type
x = [{:type=>'A', :patterns=>['foo.*']}, {:type=>'B', :patterns=>['bar.*']}]
name = 'foo.txt'
result = x.each { |item|
item[:patterns].each { |regex|
puts "Checking #{regex} against #{name}"
if !name.match(regex).nil?
puts "Found match: #{item[:type]}"
return item[:type]
end
}
}
end
result = get_type
puts "result: #{result}"
Expected output:
Checking foo.* against foo.txt
Found match: A
result: A
However, all I see is:
Checking foo.* against foo.txt
Found match: A
My current work around is this:
def get_type
x = [{:type=>'A', :patterns=>['foo.*']}, {:type=>'B', :patterns=>['bar.*']}]
name = 'foo.txt'
result = []
x.each { |item|
item[:patterns].each { |regex|
puts "Checking #{regex} against #{name}"
if !name.match(regex).nil?
puts "Found match: #{item[:type]}"
result << item[:type]
end
}
}
result[0] unless result.empty?
end
Why doesn't the first approach work? or maybe it is 'working', I just don't understand why I'm not getting what I'd expect.
May I suggest a refactor? your code looks kind of clunky because you are using each loops (imperative) when you in fact need a map+first (functional). As Ruby enumerables are not lazy this would be inefficient, so people usually build the abstraction Enumerable#map_detect (or find_yield, or find_first, or map_first):
def get_type_using_map_detect(name)
xs = [{:type => 'A', :patterns => ['foo.*']}, {:type => 'B', :patterns => ['bar.*']}]
xs.map_detect do |item|
item[:patterns].map_detect do |regex|
item[:type] if name.match(regex)
end
end
end
This is a possible implementation of the method:
module Enumerable
# Like Enumerable#map but return only the first non-nil value
def map_detect
self.each do |item|
if result = (yield item)
return result
end
end
nil
end
end
Works fine for me. Are you actually invoking it with
result = get_type puts "result: #{result}"
? Because that shouldn't work at all, though I'm assuming there's a linefeed that got eaten when you posted this.

How to do this in Ruby?

I have an array(list?) in ruby:
allRows = ["start","one","two","start","three","four","start","five","six","seven","eight","start","nine","ten"]
I need to run a loop on this to get a list that clubs elements from "start" till another "start" is encountered, like the following:
listOfRows = [["start","one","two"],["start","three","four"],["start","five","six","seven","eight"],["start","nine","ten"]]
Based on Array#split from Rails:
def group(arr, bookend)
arr.inject([[]]) do |results, element|
if (bookend == element)
results << []
end
results.last << element
results
end.select { |subarray| subarray.first == bookend } # if the first element wasn't "start", skip everything until the first occurence
end
allRows = ["start","one","two","start","three","four","start","five","six","seven","eight","start","nine","ten"]
listOfRows = group(allRows, "start")
# [["start","one","two"],["start","three","four"],["start","five","six","seven","eight"],["start","nine","ten"]]
If you can use ActiveSupport (for example, inside Rails):
def groups(arr, delim)
dels = arr.select {|e| == delim }
objs = arr.split(delim)
dels.zip(objs).map {|e,objs| [e] + objs }
end

Resources