Project

General

Profile

Actions

Feature #21135

closed

Feature request: Enumerable.compare_count

Added by Student (Nathan Zook) 9 days ago. Updated 5 days ago.

Status:
Closed
Assignee:
-
Target version:
-
[ruby-core:120992]

Description

Enumerables at times can be quite expensive to complete expand. If all is desired is to compare the count, we can terminate the enumeration once the result is known.

The functionality that I envision, if implemented in ruby, might look like this:

module Enumerable
  def compare_count(operation, standard, &blk)
    case operation
    when :<
      compare_count_lt(standard, &blk)
    when :>=
      !compare_count_lt(standard, &blk)
    when :>
      compare_count_spaceship(standard, &blk) == 1
    when :<=
      compare_count_spaceship(standard, &blk) != 1
    when :==
      compare_count_spaceship(standard, &blk) == 0
    when :!=
      compare_count_spaceship(standard, &blk) != 0
    end
  end

  def compare_count_lt(standard, &blk)
    truncate_count(standard - 1, &blk) < standard
  end

  def compare_count_spaceship(standard, &blk)
    truncate_count(standard, &blk) <=> standard
  end

  def truncate_count(limit, &blk)
    return 0 if limit < 0
    found = 0
    each do |ele|
      found += 1 unless blk && !yield(ele)
      break if found > limit
    end
    found
  end
end

class Demo
  include Enumerable

  attr_reader :capacity
  attr_reader :cnt

  def initialize(capacity)
    @capacity = capacity
  end

  def each
    @cnt = 0
    while cnt < capacity
      @cnt += 1
      yield cnt
    end
  end
end

%i{< <= >= > == !=}.each do |op|
  d = Demo.new(3)
  (-1..5).each do |x|
    puts [op, x, d.compare_count(op, x), d].inspect
  end
end


require 'prime'

%i{< <= >= > == !=}.each do |op|
  d = Demo.new(6)
  (-1..5).each do |x|
    puts [op, x, d.compare_count(op, x){|p| Prime.prime?(p)} , d].inspect
  end
end

[:<, -1, false, #<Demo:0x034d567c @capacity=3>]
[:<, 0, false, #<Demo:0x034d567c @capacity=3>]
[:<, 1, false, #<Demo:0x034d567c @capacity=3, @cnt=1>]
[:<, 2, false, #<Demo:0x034d567c @capacity=3, @cnt=2>]
[:<, 3, false, #<Demo:0x034d567c @capacity=3, @cnt=3>]
[:<, 4, true, #<Demo:0x034d567c @capacity=3, @cnt=3>]
[:<, 5, true, #<Demo:0x034d567c @capacity=3, @cnt=3>]
[:<=, -1, false, #<Demo:0x034d512c @capacity=3>]
[:<=, 0, false, #<Demo:0x034d512c @capacity=3, @cnt=1>]
[:<=, 1, false, #<Demo:0x034d512c @capacity=3, @cnt=2>]
[:<=, 2, false, #<Demo:0x034d512c @capacity=3, @cnt=3>]
[:<=, 3, true, #<Demo:0x034d512c @capacity=3, @cnt=3>]
[:<=, 4, true, #<Demo:0x034d512c @capacity=3, @cnt=3>]
[:<=, 5, true, #<Demo:0x034d512c @capacity=3, @cnt=3>]
[:>=, -1, true, #<Demo:0x034d4d44 @capacity=3>]
[:>=, 0, true, #<Demo:0x034d4d44 @capacity=3>]
[:>=, 1, true, #<Demo:0x034d4d44 @capacity=3, @cnt=1>]
[:>=, 2, true, #<Demo:0x034d4d44 @capacity=3, @cnt=2>]
[:>=, 3, true, #<Demo:0x034d4d44 @capacity=3, @cnt=3>]
[:>=, 4, false, #<Demo:0x034d4d44 @capacity=3, @cnt=3>]
[:>=, 5, false, #<Demo:0x034d4d44 @capacity=3, @cnt=3>]
[:>, -1, true, #<Demo:0x034d4998 @capacity=3>]
[:>, 0, true, #<Demo:0x034d4998 @capacity=3, @cnt=1>]
[:>, 1, true, #<Demo:0x034d4998 @capacity=3, @cnt=2>]
[:>, 2, true, #<Demo:0x034d4998 @capacity=3, @cnt=3>]
[:>, 3, false, #<Demo:0x034d4998 @capacity=3, @cnt=3>]
[:>, 4, false, #<Demo:0x034d4998 @capacity=3, @cnt=3>]
[:>, 5, false, #<Demo:0x034d4998 @capacity=3, @cnt=3>]
[:==, -1, false, #<Demo:0x034d4600 @capacity=3>]
[:==, 0, false, #<Demo:0x034d4600 @capacity=3, @cnt=1>]
[:==, 1, false, #<Demo:0x034d4600 @capacity=3, @cnt=2>]
[:==, 2, false, #<Demo:0x034d4600 @capacity=3, @cnt=3>]
[:==, 3, true, #<Demo:0x034d4600 @capacity=3, @cnt=3>]
[:==, 4, false, #<Demo:0x034d4600 @capacity=3, @cnt=3>]
[:==, 5, false, #<Demo:0x034d4600 @capacity=3, @cnt=3>]
[:!=, -1, true, #<Demo:0x034d4268 @capacity=3>]
[:!=, 0, true, #<Demo:0x034d4268 @capacity=3, @cnt=1>]
[:!=, 1, true, #<Demo:0x034d4268 @capacity=3, @cnt=2>]
[:!=, 2, true, #<Demo:0x034d4268 @capacity=3, @cnt=3>]
[:!=, 3, false, #<Demo:0x034d4268 @capacity=3, @cnt=3>]
[:!=, 4, true, #<Demo:0x034d4268 @capacity=3, @cnt=3>]
[:!=, 5, true, #<Demo:0x034d4268 @capacity=3, @cnt=3>]
[:<, -1, false, #<Demo:0x00007f64147c37e0 @capacity=6>]
[:<, 0, false, #<Demo:0x00007f64147c37e0 @capacity=6>]
[:<, 1, false, #<Demo:0x00007f64147c37e0 @capacity=6, @cnt=2>]
[:<, 2, false, #<Demo:0x00007f64147c37e0 @capacity=6, @cnt=3>]
[:<, 3, false, #<Demo:0x00007f64147c37e0 @capacity=6, @cnt=5>]
[:<, 4, true, #<Demo:0x00007f64147c37e0 @capacity=6, @cnt=6>]
[:<, 5, true, #<Demo:0x00007f64147c37e0 @capacity=6, @cnt=6>]
[:<=, -1, false, #<Demo:0x00007f64147c21d8 @capacity=6>]
[:<=, 0, false, #<Demo:0x00007f64147c21d8 @capacity=6, @cnt=2>]
[:<=, 1, false, #<Demo:0x00007f64147c21d8 @capacity=6, @cnt=3>]
[:<=, 2, false, #<Demo:0x00007f64147c21d8 @capacity=6, @cnt=5>]
[:<=, 3, true, #<Demo:0x00007f64147c21d8 @capacity=6, @cnt=6>]
[:<=, 4, true, #<Demo:0x00007f64147c21d8 @capacity=6, @cnt=6>]
[:<=, 5, true, #<Demo:0x00007f64147c21d8 @capacity=6, @cnt=6>]
[:>=, -1, true, #<Demo:0x00007f64147c0860 @capacity=6>]
[:>=, 0, true, #<Demo:0x00007f64147c0860 @capacity=6>]
[:>=, 1, true, #<Demo:0x00007f64147c0860 @capacity=6, @cnt=2>]
[:>=, 2, true, #<Demo:0x00007f64147c0860 @capacity=6, @cnt=3>]
[:>=, 3, true, #<Demo:0x00007f64147c0860 @capacity=6, @cnt=5>]
[:>=, 4, false, #<Demo:0x00007f64147c0860 @capacity=6, @cnt=6>]
[:>=, 5, false, #<Demo:0x00007f64147c0860 @capacity=6, @cnt=6>]
[:>, -1, true, #<Demo:0x00007f64147df260 @capacity=6>]
[:>, 0, true, #<Demo:0x00007f64147df260 @capacity=6, @cnt=2>]
[:>, 1, true, #<Demo:0x00007f64147df260 @capacity=6, @cnt=3>]
[:>, 2, true, #<Demo:0x00007f64147df260 @capacity=6, @cnt=5>]
[:>, 3, false, #<Demo:0x00007f64147df260 @capacity=6, @cnt=6>]
[:>, 4, false, #<Demo:0x00007f64147df260 @capacity=6, @cnt=6>]
[:>, 5, false, #<Demo:0x00007f64147df260 @capacity=6, @cnt=6>]
[:==, -1, false, #<Demo:0x00007f64147dd898 @capacity=6>]
[:==, 0, false, #<Demo:0x00007f64147dd898 @capacity=6, @cnt=2>]
[:==, 1, false, #<Demo:0x00007f64147dd898 @capacity=6, @cnt=3>]
[:==, 2, false, #<Demo:0x00007f64147dd898 @capacity=6, @cnt=5>]
[:==, 3, true, #<Demo:0x00007f64147dd898 @capacity=6, @cnt=6>]
[:==, 4, false, #<Demo:0x00007f64147dd898 @capacity=6, @cnt=6>]
[:==, 5, false, #<Demo:0x00007f64147dd898 @capacity=6, @cnt=6>]
[:!=, -1, true, #<Demo:0x00007f64147dbf20 @capacity=6>]
[:!=, 0, true, #<Demo:0x00007f64147dbf20 @capacity=6, @cnt=2>]
[:!=, 1, true, #<Demo:0x00007f64147dbf20 @capacity=6, @cnt=3>]
[:!=, 2, true, #<Demo:0x00007f64147dbf20 @capacity=6, @cnt=5>]
[:!=, 3, false, #<Demo:0x00007f64147dbf20 @capacity=6, @cnt=6>]
[:!=, 4, true, #<Demo:0x00007f64147dbf20 @capacity=6, @cnt=6>]
[:!=, 5, true, #<Demo:0x00007f64147dbf20 @capacity=6, @cnt=6>]

Updated by kddnewton (Kevin Newton) 8 days ago

We already have lazy enumerators, so I think your request can be simplified a bit by taking advantage of that and defining Enumerator::Lazy#length to return an object that knows how to compare itself to other stuff. Something like:

class Enumerator
  class Lazy
    class Length
      def initialize(enum)
        @enum = enum
      end
    
      def force = @enum.to_a.length
      def <=>(value) = force <=> value
    
      def <(value) = value >= 0 && @enum.take(value).to_a.length < value
      def <=(value) = value >= 0 && @enum.take(value + 1).to_a.length <= value
      def >(value) = value <= 0 || @enum.take(value + 1).to_a.length > value
      def >=(value) = value <= 0 || @enum.take(value).to_a.length == value
      def ==(value) = value >= 0 && @enum.take(value + 1).to_a.length == value
      def !=(value) = value < 0 || @enum.take(value + 1).to_a.length != value
    end

    def length = Length.new(self)
  end
end

In your example, instead of d.compare_count(op, x) this would instead be d.lazy.length.public_send(op, x), and instead of d.compare_count(op, x){|p| Prime.prime?(p)} it would instead be d.lazy.select { |p| Prime.prime?(p) }.length.public_send(op, x).

I don't know if this is applicable enough to other projects that it would merit being in core, but I do think this would be simpler than the requested method.

Updated by Student (Nathan Zook) 5 days ago

I had indeed forgotten about Enumerable::Lazy. There is a more general solution, which I am working up. Please close

Actions #3

Updated by kddnewton (Kevin Newton) 5 days ago

  • Status changed from Open to Closed
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0