[前][次][番号順一覧][スレッド一覧]

ruby-changes:58438

From: Nobuyoshi <ko1@a...>
Date: Fri, 25 Oct 2019 23:31:08 +0900 (JST)
Subject: [ruby-changes:58438] 42c652d195 (master): Fixed range argument condition [Feature #14784]

https://git.ruby-lang.org/ruby.git/commit/?id=42c652d195

From 42c652d1959564bc5fb5147c8c343d8c0589583c Mon Sep 17 00:00:00 2001
From: Nobuyoshi Nakada <nobu@r...>
Date: Fri, 25 Oct 2019 22:09:38 +0900
Subject: Fixed range argument condition [Feature #14784]

Allows a beginless/endless range, and an end-exclusive range
unless the receiver is smaller than its end.

diff --git a/compar.c b/compar.c
index fe4cc42..058d912 100644
--- a/compar.c
+++ b/compar.c
@@ -179,53 +179,71 @@ cmp_between(VALUE x, VALUE min, VALUE max) https://github.com/ruby/ruby/blob/trunk/compar.c#L179
  *     obj.clamp(min, max) ->  obj
  *     obj.clamp(range)    ->  obj
  *
- * In the first form, returns _min_ if _obj_ <code><=></code> _min_ is
- * less than zero, _max_ if _obj_ <code><=></code> _max_ is greater
- * than zero and _obj_ otherwise.  In the second form, clamps by
- * _range.min_ and _range.max_.  If _range_ is an exclusive range,
- * raises an ArgumentError.
+ * In <code>(min, max)</code> form, returns _min_ if _obj_
+ * <code><=></code> _min_ is less than zero, _max_ if _obj_
+ * <code><=></code> _max_ is greater than zero, and _obj_
+ * otherwise.
  *
  *     12.clamp(0, 100)         #=> 12
  *     523.clamp(0, 100)        #=> 100
  *     -3.123.clamp(0, 100)     #=> 0
+ *
+ *     'd'.clamp('a', 'f')      #=> 'd'
+ *     'z'.clamp('a', 'f')      #=> 'f'
+ *
+ * In <code>(range)</code> form, returns _range.begin_ if _obj_
+ * <code><=></code> _range.begin_ is less than zero, _range.end_
+ * if _obj_ <code><=></code> _range.end_ is greater than zero, and
+ * _obj_ otherwise.
+ *
  *     12.clamp(0..100)         #=> 12
  *     523.clamp(0..100)        #=> 100
  *     -3.123.clamp(0..100)     #=> 0
  *
- *     'd'.clamp('a', 'f')      #=> 'd'
- *     'z'.clamp('a', 'f')      #=> 'f'
  *     'd'.clamp('a'..'f')      #=> 'd'
  *     'z'.clamp('a'..'f')      #=> 'f'
  *
- *     12.clamp(0...100)        #=> ArgumentError
+ * If _range.begin_ is +nil+, it is considered smaller than _obj_,
+ * and if _range.end_ is +nil+, it is considered greater than
+ * _obj_.
+ *
+ *     -20.clamp(0..)           #=> 0
+ *     523.clamp(..100)         #=> 100
+ *
+ * When _range.end_ is excluded, and _obj_ is greater than or
+ * equal to _range.end_, an exception is raised.
+ *
+ *     100.clamp(0...100)       # ArgumentError
  */
 
 static VALUE
 cmp_clamp(int argc, VALUE *argv, VALUE x)
 {
     VALUE min, max;
-    int c;
+    int c, excl = 0, allow_nil = 0;
 
     if (rb_scan_args(argc, argv, "11", &min, &max) == 1) {
         VALUE range = min;
-        int excl;
         if (!rb_range_values(range, &min, &max, &excl)) {
             rb_raise(rb_eTypeError, "wrong argument type %s (expected Range)",
                      rb_builtin_class_name(range));
         }
-        if (excl || NIL_P(min) || NIL_P(max)) {
-            rb_raise(rb_eArgError, "cannot clamp with an exclusive range");
-        }
+        allow_nil = 1;
     }
-    if (cmpint(min, max) > 0) {
+    if (!(allow_nil && (NIL_P(min) || NIL_P(max))) && cmpint(min, max) > 0) {
 	rb_raise(rb_eArgError, "min argument must be smaller than max argument");
     }
 
-    c = cmpint(x, min);
-    if (c == 0) return x;
-    if (c < 0) return min;
-    c = cmpint(x, max);
-    if (c > 0) return max;
+    if (!NIL_P(min)) {
+        c = cmpint(x, min);
+        if (c == 0) return x;
+        if (c < 0) return min;
+    }
+    if (!NIL_P(max)) {
+        c = cmpint(x, max);
+        if (excl && c >= 0) rb_raise(rb_eArgError, "cannot clamp with an exclusive range");
+        if (c > 0) return max;
+    }
     return x;
 }
 
diff --git a/test/ruby/test_comparable.rb b/test/ruby/test_comparable.rb
index c363a03..e8a2ff1 100644
--- a/test/ruby/test_comparable.rb
+++ b/test/ruby/test_comparable.rb
@@ -99,14 +99,21 @@ class TestComparable < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_comparable.rb#L99
     assert_equal(1, @o.clamp(1..1))
     assert_equal(@o, @o.clamp(0..0))
 
+    assert_equal(1, @o.clamp(1...2))
+    assert_equal(1, @o.clamp(1..))
+    assert_equal(1, @o.clamp(1...))
+    assert_equal(@o, @o.clamp(0...2))
+    assert_equal(@o, @o.clamp(0..))
+    assert_equal(@o, @o.clamp(0...))
+    assert_equal(@o, @o.clamp(..2))
+    assert_equal(@o, @o.clamp(...2))
+    assert_equal(-1, @o.clamp(-2..-1))
+    assert_equal(@o, @o.clamp(-2..0))
+    assert_equal(@o, @o.clamp(-2..))
+    assert_equal(@o, @o.clamp(-2...))
+
     assert_raise_with_message(ArgumentError, 'cannot clamp with an exclusive range') {
-      @o.clamp(1...2)
-    }
-    assert_raise_with_message(ArgumentError, 'cannot clamp with an exclusive range') {
-      @o.clamp(1...)
-    }
-    assert_raise_with_message(ArgumentError, 'cannot clamp with an exclusive range') {
-      @o.clamp(...2)
+      @o.clamp(-1...0)
     }
     assert_raise_with_message(ArgumentError, 'min argument must be smaller than max argument') {
       @o.clamp(2..1)
-- 
cgit v0.10.2


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]