summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorRyan Lopopolo <[email protected]>2019-07-08 19:23:24 -0700
committerRyan Lopopolo <[email protected]>2019-07-08 22:36:24 -0700
commit4085709ae1736fb755955d3d52b6b73c15853506 (patch)
treeaaf0f788d879f009a2ba681319b45d75b76b9e2d
parent469c6261c69999aab9e0d19cea6ac8e03f7847e3 (diff)
downloadmruby-4085709ae1736fb755955d3d52b6b73c15853506.tar.gz
mruby-4085709ae1736fb755955d3d52b6b73c15853506.zip
Specialize Enumerable#max and Enumerable#min for Range
This patch prevents a hang for pathalogical (large) Ranges when computing max and min. Range inherits its implementation of max and min from Enumerable. Enumerable implements max and min by calling each. For Range objects, this is unnecessary since we know the max and the min by the end and begin attributes. It is also very slow. This code hangs unnecessarily: (0..2**32).max # ... hang (0..2**32).min # ... hang This patch overrides max and min after including enumerable to yield based on the begin and end methods.
-rw-r--r--mrblib/range.rb66
1 files changed, 65 insertions, 1 deletions
diff --git a/mrblib/range.rb b/mrblib/range.rb
index 392cc2274..746cb322c 100644
--- a/mrblib/range.rb
+++ b/mrblib/range.rb
@@ -15,7 +15,8 @@ class Range
val = self.first
last = self.last
- if val.kind_of?(Fixnum) && last.kind_of?(Fixnum) # fixnums are special
+ # numerics are special
+ if (val.kind_of?(Fixnum) || val.kind_of?(Float)) && (last.kind_of?(Fixnum) || last.kind_of?(Float))
lim = last
lim += 1 unless exclude_end?
i = val
@@ -64,4 +65,67 @@ end
# ISO 15.2.14.3
class Range
include Enumerable
+
+ def max(&block)
+ val = self.first
+ last = self.last
+ # numerics are special
+ if (val.kind_of?(Fixnum) || val.kind_of?(Float)) && (last.kind_of?(Fixnum) || last.kind_of?(Float))
+ return nil if val > last
+ return nil if val == last && exclude_end?
+
+ max = last
+ max -= 1 if exclude_end?
+ max = val if block && block.call(val, last) > 0
+ return max
+ end
+
+ max = nil
+ each do |item|
+ max =
+ if max.nil?
+ item
+ elsif block && block.call(max, item) > 0
+ item
+ elsif item > max
+ item
+ else
+ max
+ end
+ end
+ max
+ end
+
+ def min(&block)
+ val = self.first
+ last = self.last
+
+ # numerics are special
+ if (val.kind_of?(Fixnum) || val.kind_of?(Float)) && (last.kind_of?(Fixnum) || last.kind_of?(Float))
+ return nil if val > last
+ return nil if val == last && exclude_end?
+
+ min = val
+ if block && block.call(val, last) > 0
+ min = last
+ min -= 1 if exclude_end?
+ end
+ return min
+ end
+
+ min = nil
+ each do |item|
+ min =
+ if min.nil?
+ item
+ elsif block && block.call(min, item) < 0
+ item
+ elsif item < min
+ item
+ else
+ min
+ end
+ end
+ min
+ end
end