最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

ruby - Saving the calculation inside a block to the outside - Stack Overflow

programmeradmin3浏览0评论

Assume I have in Ruby an Enumerable coll, and the following code (which of course does not produce the desired result):

# WRONG approach
def f(coll)
  pivot = coll.find do
    |element|
    t = element.calculate
    t > 0
  end
  pivot && t # Return the calculated value 
end

This does not work, since each block has its own scope for locals, and the t of the inner block is different from the t of the outer block. What is the most elegant way to fix it? I can think of the following possibilities:

  • Recalculte the value for the found element
    def f(coll)
      pivot = coll.find do
        |element|
        element.calculate > 0
      end
      pivot && pivot.calculate 
    end

Drawback: The last calculation is unnecessarily done twice.

  • Use a global variable
    def f(coll)
      pivot = coll.find do
        |element|
        @t = element.calculate
        @t > 0
      end
      pivot && @t 
    end

Drawback: It's ugly design.

  • Perform the find on the calculated values:
    def f(coll)
      coll.map(&:calculate).find {|x| x>0}
    end

Drawback: Unnecessary calculations

Is there any better way to do it, perhaps using binding to make the local variable accessible inside the block? If so, how can it be done?

Assume I have in Ruby an Enumerable coll, and the following code (which of course does not produce the desired result):

# WRONG approach
def f(coll)
  pivot = coll.find do
    |element|
    t = element.calculate
    t > 0
  end
  pivot && t # Return the calculated value 
end

This does not work, since each block has its own scope for locals, and the t of the inner block is different from the t of the outer block. What is the most elegant way to fix it? I can think of the following possibilities:

  • Recalculte the value for the found element
    def f(coll)
      pivot = coll.find do
        |element|
        element.calculate > 0
      end
      pivot && pivot.calculate 
    end

Drawback: The last calculation is unnecessarily done twice.

  • Use a global variable
    def f(coll)
      pivot = coll.find do
        |element|
        @t = element.calculate
        @t > 0
      end
      pivot && @t 
    end

Drawback: It's ugly design.

  • Perform the find on the calculated values:
    def f(coll)
      coll.map(&:calculate).find {|x| x>0}
    end

Drawback: Unnecessary calculations

Is there any better way to do it, perhaps using binding to make the local variable accessible inside the block? If so, how can it be done?

Share Improve this question asked Feb 14 at 15:08 user1934428user1934428 22.3k9 gold badges51 silver badges104 bronze badges 1
  • 3 Add lazy to your last approach: coll.lazy.map(&:calculate).find { |x| x > 0 }. – Stefan Commented Feb 14 at 17:22
Add a comment  | 

3 Answers 3

Reset to default 7

I would recommend going about it as follows:

def f(coll)
  coll.lazy.filter_map do |element| 
    # could be element.calculate.then {|t| t if t.positive? }
    t = element.calculate 
    t if t.positive? 
  end.first
end

This uses an Enumerator::Lazy in combination with filter_map and first allowing us to return the first element where the block result is not nil, which due to the if t.positive? will be the first calculation where calculate returns a positive number.

The simplest way to have available a value outside a block is to use a local variable declared (by initialising) outside the block:

t = nil

call.find do |element|
  t = element.calculate
  t > 0
end

# here t is available

There is a more complex but may be more idiomatic/functional way suited for your case - to calculate t lazily and using lazy #zip find a pair of element and t:

ts = col.lazy.map { |e| e.calculate }
element, t = enum.lazy.zip(ts).find { |e, t| t < 0 }

break or return are also viable options:

def f(coll)
  result = coll.find do |element|
    t = element.calculate
    break t if t > 0
  end
  
  result
end

def f(coll)
  coll.find do |element|
    t = element.calculate
    return t if t > 0
  end
end
发布评论

评论列表(0)

  1. 暂无评论