Possible use for a continuation? [Generating all factors of a given number]

Imagine I have an array of arrays, representing the prime factorisation of a number.

For example, the array for 12260 would be

[[2,3], [3,2], [5,2], [7,1]]

What I want to do is to use this to generate all the different factors of the number.

This means generating all values of x = 2^a * 3^b * 5^c * 7^d where a ranges from 0 to 3, b from 0 to 2, c from 0 to 2 and d from 0 to 1.

However, for space and efficiency reasons, I’d prefer not to have a method that simply returns an array containing all the factors, since in many cases, I’ll decide after having processed one of them that I don’t need any more.

I have a feeling that some kind of recursive method that returns a continuation might be the way to go, but I’m open to any suggestions, whatsoever.

The other possibility I can think of is to write an iterator that is recursive (or calls another method that is) and does a yield as the deepest nesting of the recursion. If I did that, would a “break” from inside the block passed to that iterator work the way I would normally expect it to? I feel like it probably would, but am wondering because of the recursion.

Thanks in advance,

Harry O.

Glad you said that :wink:

Continuations might be more-than-required since you
suggest that when a condition arises, the game is up.

catch / throw may be what you need.

N.B. I haven’t done the job but just to show that
throw can be issued from anywhere …

def gen_value_single(n, r)
ret_ =
0.upto(r) do |i|
ret_ << n ** i
throw :enough if ((false))
end
ret_
end

def gen_values(arr)
all_done = false
catch :enough do
arr.each do |n, r|
p gen_value_single(n, r)

···

“Harry Ohlsen” harryo@qiqsolutions.com wrote:

[…] since in many cases, I’ll decide after having
processed one of them that I don’t need any more.

I’m open to any suggestions, whatsoever.

  #
  ### accumulate stuff
  #
  throw :enough if ((false))
end
all_done = true

end # post-catch
return all_done
end

gen_values( [[2,3], [3,2], [5,2], [7,1]] )

And, because I don’t know your condition,
I’ve entered false.

The (useless) output from above is:

[1, 2, 4, 8]
[1, 3, 9]
[1, 5, 25]
[1, 7]

daz

Overkill - this should do it:

class MultiCounter
def initialize(a, b = nil)
@max = a
@min = b || a.map {0}
raise “max and min arrays should have the same length” unless @max.length ==
@min.length
@cur = @min
end

def next
inc (@cur.length - 1)
end

def inc(i)
return nil if i < 0
@cur[i] += 1
if @cur[i] > @max[i]
@cur[i] = 0
return inc(i - 1)
end
@cur
end
end

a = MultiCounter.new([3, 2, 2, 1])
while (b = a.next)
p b
end

martin

···

Harry Ohlsen harryo@qiqsolutions.com wrote:

I have a feeling that some kind of recursive method that returns a
continuation might be the way to go, but I’m open to any suggestions,
whatsoever.

daz wrote:

Continuations might be more-than-required since you
suggest that when a condition arises, the game is up.

Darn! I was hoping to finally get a chance to use one :-).

catch / throw may be what you need.

N.B. I haven’t done the job but just to show that
throw can be issued from anywhere …

def gen_value_single(n, r)
ret_ =
0.upto(r) do |i|
ret_ << n ** i
throw :enough if ((false))
end
ret_
end

def gen_values(arr)
all_done = false
catch :enough do
arr.each do |n, r|
p gen_value_single(n, r)
#
### accumulate stuff
#
throw :enough if ((false))
end
all_done = true
end # post-catch
return all_done
end

gen_values( [[2,3], [3,2], [5,2], [7,1]] )

And, because I don’t know your condition,
I’ve entered false.

What I was really trying to achieve, though, was to have a generic iterator that invoked a block that was passed to it, passing each factor as it was calculated, but for that block to be able to determine that it didn’t want any more values.

Alternatively, and I felt this would work more cleanly, I’d like a re-entrant generator that could simply be called multiple times, returning the next value each time. That way the caller doesn’t need to communicate the fact that no more values are required, it simply wouldn’t call the generator any more. This is where I thought a continuation might help.

What I ended up with was this …

def Primes.each_factor(n, &block)
prime_factors = factors(n)

  iterate_over_factors(1, prime_factors, &block)

end

def Primes.iterate_over_factors(start, factors, &block)
first = factors[0]
rest = factors[1 … -1]

  factor   = first[0]
  exponent = first[1]

  x = start

  (0 .. exponent).each do |i|
     if rest.length == 0
        if !block.call(x)
           return false
        end
     else
        if !iterate_over_factors(x, rest, &block)
           break
        end
     end

     x *= factor
  end

end

which can be called like …

isGenerator = true

Primes.each_factor(phi) do |factor|
exponent = phi / factor

  if (i ** exponent).to_i == 1
      isGenerator = false
  end

  isGenerator

end

The only problem with this is that you can’t really “return” a value from a block (or, at least, I don’t believe so), which makes the last line, which has to “return” true or false, so that the iterator knows whether to continue, isn’t quite as obvious as I’d like.

As I say, to me it would be much nicer if I could do something like …

factors = Primes.factors(phi)

while (f = factors.next())
exponent = phi / factor

  if (i ** exponent).to_i == 1
      return false
  end

end

return true

because there’s no need for the block to pass anything back to the generator (since the generator isn’t actually passed the block).

Anyway, I have something that works. But, I’d still be interested if someone feels like showing me how to achieve the equivalent generator that’s being used in the last code snippet.

Cheers,

Harry O.

IIRC, 1.8 has “next ” and “break ” which return a value.

In the above case, if ‘isGenerator = false’ terminates the loop, you could
just write

 exponent = phi / factor
 (i ** exponent).to_i != 1    # continue condition

Regards,

Brian.

···

On Wed, Jul 09, 2003 at 05:40:40PM +0900, Harry Ohlsen wrote:

which can be called like …

isGenerator = true

Primes.each_factor(phi) do |factor|
exponent = phi / factor

  if (i ** exponent).to_i == 1
      isGenerator = false
  end

  isGenerator

end

The only problem with this is that you can’t really “return” a value from
a block (or, at least, I don’t believe so), which makes the last line,
which has to “return” true or false, so that the iterator knows whether to
continue, isn’t quite as obvious as I’d like.

Scroll down to ‘Generators’.

···

On Wed, Jul 09, 2003 at 05:40:40PM +0900, Harry Ohlsen wrote:

Anyway, I have something that works. But, I’d still be interested if
someone feels like showing me how to achieve the equivalent generator
that’s being used in the last code snippet.


_ _

__ __ | | ___ _ __ ___ __ _ _ __
'_ \ / | __/ __| '_ _ \ / ` | ’ \
) | (| | |
__ \ | | | | | (| | | | |
.__/ _,
|_|/| || ||_,|| |_|
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

No, that’s wrong too. Now there’s a race condition between the rm and
the mv. Hmm, I need more coffee.
– Guy Maor on Debian Bug#25228

Brian Candler wrote:

IIRC, 1.8 has “next ” and “break ” which return a value.

That could be good! Thanks for the info.

In the above case, if ‘isGenerator = false’ terminates the loop, you could
just write

 exponent = phi / factor
 (i ** exponent).to_i != 1    # continue condition

Sorry, that was just a snippet of the overall code, to save people trying to understand all the details. The flag is there for other reasons. It just happens that it’s value also tells me whether I can terminate the loop over the generated values.

What I was getting at was that I didn’t think it yelled out clearly that the last line in the block being just “isGenerator” was passing information back to the iterator, since this isn’t something that I’ve often seen people do (although, that may just be a lack of data on my part).

Harry O.