0

I need to generate random values for a few variables. And then, if those values meet certain characteristics, I need to trash them and re-generate (re-define the value of the variable?).

I probably need to do this with a function but I'm just learning those and not sure how to escape the "locality" of variables created in a function (I'll need to call them later in the script).

So let's say I have:

x = rand(1...9)
y = rand(1...9)
z = rand(1...9)

Afterward I need something like

if x - y - z < 0
then **start over** until x - y - z > 0
end

It seems like this must have a "simple" solution that I just don't have the experience/knowledge to see. Any ideas appreciated!

1
  • If you find any of the answers helpful please consider selecting the one you liked best. Commented Aug 28, 2016 at 17:03

2 Answers 2

4

There's a number of patterns in programming, and Ruby in particular, that you'll see time and time again. This generate-and-verify-on-fail-redo type pattern is fairly common, and common enough Ruby has a begin ... end while (cond) structure for it.

One thing that's working against you here is having three variables. This violates the Zero, One or Infinity Rule which is essential to having organized code. When dealing with more than "one" thing, consider using a data structure like an array.

Taking all that into account here's a simple approach:

def random_values(count = 3)
  begin
    values = Array.new(count) { rand(1...9) }
  end while (values.inject(&:-) <= 0)

  values
end

This keeps generating lists until you find one that meets your criteria. The inject call is a quick way of performing the subtraction on the numbers in a particular set by iterating through the list and subtracting one element from the next, then using that result in the subsequent loop.

The Array constructor is given two arguments, one is the number of elements to pre-allocate to it, the second is a block that defines how those elements should be produced. This is a quick way of making a N length array of random values.

Now a slightly more Ruby way of doing this is to define a generator of random number sets:

random_values = Enumerator.new do |y|
  loop do
    y << Array.new(3) { rand(1...9) }
  end
end

This allows you to apply the Enumerable library to the problem and find the first entry matching your criteria. Here you can expand the variables in the array within this local test block to keep the code simple:

first = random_values.find do |x, y, z|
  x - y - z > 0
end

The advantage of this approach is you can chain things together and get things like this:

selection = random_values.lazy.select do |x, y, z|
  x - y - z > 0
end.first(10)

This will "lazy" enumerate arrays, that is it will produce them on-demand instead if running that loop until completion, and will pull out the first 10 that match and save those in an array called selection.

Sign up to request clarification or add additional context in comments.

10 Comments

Why not use while values.inject(&:-) <= 0?
Thanks very much tadman. So, later in the script, outside this function, I can call those values from the array? i.e. puts values[0] or puts values[0] + values[1]?
@pjs Actually that's a better way of doing it, I'll change it. Thanks!
Perhaps generalize to pass a proc or block that evaluates the condition that must be satisfied by the random numbers.
@CarySwoveland I put in an example using an Enumerator which makes the validation logic a lot easier to inline.
|
0

Try this:

    x = rand(1...9)
    y = rand(1...9)
    z = rand(1...9)

while x - y - z > 0
    x = rand(1...9)
    y = rand(1...9)
    z = rand(1...9)
end

puts "x: #{x}, y: #{y}, z: #{z}"

More generic way may be using method like this:

def generate 
    x = rand(1...9)
    y = rand(1...9)
    z = rand(1...9)
    [x,y,z]
end 
x,y,z = generate()

while x - y - z > 0
    x,y,z = generate()
end

puts "x: #{x}, y: #{y}, z: #{z}"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.