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

ruby-changes:58259

From: Nobuyoshi <ko1@a...>
Date: Wed, 16 Oct 2019 01:42:50 +0900 (JST)
Subject: [ruby-changes:58259] 929d5fd3b9 (master): Comparable#clamp with a range [Feature #14784]

https://git.ruby-lang.org/ruby.git/commit/?id=929d5fd3b9

From 929d5fd3b99c1413f737ff16cf0680698036e60f Mon Sep 17 00:00:00 2001
From: Nobuyoshi Nakada <nobu@r...>
Date: Tue, 15 Oct 2019 22:32:10 +0900
Subject: Comparable#clamp with a range [Feature #14784]


diff --git a/NEWS b/NEWS
index a3283a6..36660de 100644
--- a/NEWS
+++ b/NEWS
@@ -142,6 +142,12 @@ Array:: https://github.com/ruby/ruby/blob/trunk/NEWS#L142
 
     * Added Array#intersection. [Feature #16155]
 
+Comparable::
+
+  Modified method::
+
+    * Comparable#clamp now accepts a Range argument. [Feature #14784]
+
 Complex::
 
   New method::
diff --git a/common.mk b/common.mk
index ff56a0c..19aa723 100644
--- a/common.mk
+++ b/common.mk
@@ -1636,6 +1636,7 @@ class.$(OBJEXT): {$(VPATH)}thread_native.h https://github.com/ruby/ruby/blob/trunk/common.mk#L1636
 class.$(OBJEXT): {$(VPATH)}vm_core.h
 class.$(OBJEXT): {$(VPATH)}vm_debug.h
 class.$(OBJEXT): {$(VPATH)}vm_opts.h
+compar.$(OBJEXT): $(hdrdir)/ruby.h
 compar.$(OBJEXT): $(hdrdir)/ruby/ruby.h
 compar.$(OBJEXT): {$(VPATH)}assert.h
 compar.$(OBJEXT): {$(VPATH)}compar.c
@@ -1643,6 +1644,7 @@ compar.$(OBJEXT): {$(VPATH)}config.h https://github.com/ruby/ruby/blob/trunk/common.mk#L1644
 compar.$(OBJEXT): {$(VPATH)}defines.h
 compar.$(OBJEXT): {$(VPATH)}id.h
 compar.$(OBJEXT): {$(VPATH)}intern.h
+compar.$(OBJEXT): {$(VPATH)}internal.h
 compar.$(OBJEXT): {$(VPATH)}missing.h
 compar.$(OBJEXT): {$(VPATH)}st.h
 compar.$(OBJEXT): {$(VPATH)}subst.h
diff --git a/compar.c b/compar.c
index 027328d..fe4cc42 100644
--- a/compar.c
+++ b/compar.c
@@ -11,6 +11,7 @@ https://github.com/ruby/ruby/blob/trunk/compar.c#L11
 
 #include "ruby/ruby.h"
 #include "id.h"
+#include "internal.h"
 
 VALUE rb_mComparable;
 
@@ -176,24 +177,46 @@ cmp_between(VALUE x, VALUE min, VALUE max) https://github.com/ruby/ruby/blob/trunk/compar.c#L177
 /*
  *  call-seq:
  *     obj.clamp(min, max) ->  obj
+ *     obj.clamp(range)    ->  obj
  *
- * 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 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.
  *
  *     12.clamp(0, 100)         #=> 12
  *     523.clamp(0, 100)        #=> 100
  *     -3.123.clamp(0, 100)     #=> 0
+ *     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
  */
 
 static VALUE
-cmp_clamp(VALUE x, VALUE min, VALUE max)
+cmp_clamp(int argc, VALUE *argv, VALUE x)
 {
+    VALUE min, max;
     int c;
 
+    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");
+        }
+    }
     if (cmpint(min, max) > 0) {
 	rb_raise(rb_eArgError, "min argument must be smaller than max argument");
     }
@@ -259,5 +282,5 @@ Init_Comparable(void) https://github.com/ruby/ruby/blob/trunk/compar.c#L282
     rb_define_method(rb_mComparable, "<", cmp_lt, 1);
     rb_define_method(rb_mComparable, "<=", cmp_le, 1);
     rb_define_method(rb_mComparable, "between?", cmp_between, 2);
-    rb_define_method(rb_mComparable, "clamp", cmp_clamp, 2);
+    rb_define_method(rb_mComparable, "clamp", cmp_clamp, -1);
 }
diff --git a/spec/ruby/core/comparable/clamp_spec.rb b/spec/ruby/core/comparable/clamp_spec.rb
index 6d21622..393496f 100644
--- a/spec/ruby/core/comparable/clamp_spec.rb
+++ b/spec/ruby/core/comparable/clamp_spec.rb
@@ -2,10 +2,12 @@ require_relative '../../spec_helper' https://github.com/ruby/ruby/blob/trunk/spec/ruby/core/comparable/clamp_spec.rb#L2
 require_relative 'fixtures/classes'
 
 describe 'Comparable#clamp' do
-  it 'raises an Argument error unless given 2 parameters' do
-    c = ComparableSpecs::Weird.new(0)
-    -> { c.clamp(c) }.should raise_error(ArgumentError)
-    -> { c.clamp(c, c, c) }.should raise_error(ArgumentError)
+  ruby_version_is ""..."2.7" do
+    it 'raises an Argument error unless given 2 parameters' do
+      c = ComparableSpecs::Weird.new(0)
+      -> { c.clamp(c) }.should raise_error(ArgumentError)
+      -> { c.clamp(c, c, c) }.should raise_error(ArgumentError)
+    end
   end
 
   it 'raises an Argument error unless the 2 parameters are correctly ordered' do
@@ -45,4 +47,42 @@ describe 'Comparable#clamp' do https://github.com/ruby/ruby/blob/trunk/spec/ruby/core/comparable/clamp_spec.rb#L47
 
     c.clamp(one, two).should equal(two)
   end
+
+  ruby_version_is "2.7" do
+    it 'returns self if within the given range parameters' do
+      one = ComparableSpecs::WithOnlyCompareDefined.new(1)
+      two = ComparableSpecs::WithOnlyCompareDefined.new(2)
+      three = ComparableSpecs::WithOnlyCompareDefined.new(3)
+      c = ComparableSpecs::Weird.new(2)
+
+      c.clamp(one..two).should equal(c)
+      c.clamp(two..two).should equal(c)
+      c.clamp(one..three).should equal(c)
+      c.clamp(two..three).should equal(c)
+    end
+
+    it 'returns the minimum value of the range parameters if smaller than it' do
+      one = ComparableSpecs::WithOnlyCompareDefined.new(1)
+      two = ComparableSpecs::WithOnlyCompareDefined.new(2)
+      c = ComparableSpecs::Weird.new(0)
+
+      c.clamp(one..two).should equal(one)
+    end
+
+    it 'returns the maximum value of the range parameters if greater than it' do
+      one = ComparableSpecs::WithOnlyCompareDefined.new(1)
+      two = ComparableSpecs::WithOnlyCompareDefined.new(2)
+      c = ComparableSpecs::Weird.new(3)
+
+      c.clamp(one..two).should equal(two)
+    end
+
+    it 'raises an Argument error if the range parameter is exclusive' do
+      one = ComparableSpecs::WithOnlyCompareDefined.new(1)
+      two = ComparableSpecs::WithOnlyCompareDefined.new(2)
+      c = ComparableSpecs::Weird.new(3)
+
+      -> { c.clamp(one...two) }.should raise_error(ArgumentError)
+    end
+  end
 end
diff --git a/test/ruby/test_comparable.rb b/test/ruby/test_comparable.rb
index 4f1d38a..c363a03 100644
--- a/test/ruby/test_comparable.rb
+++ b/test/ruby/test_comparable.rb
@@ -90,6 +90,29 @@ class TestComparable < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_comparable.rb#L90
     }
   end
 
+  def test_clamp_with_range
+    cmp->(x) do 0 <=> x end
+    assert_equal(1, @o.clamp(1..2))
+    assert_equal(-1, @o.clamp(-2..-1))
+    assert_equal(@o, @o.clamp(-1..3))
+
+    assert_equal(1, @o.clamp(1..1))
+    assert_equal(@o, @o.clamp(0..0))
+
+    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)
+    }
+    assert_raise_with_message(ArgumentError, 'min argument must be smaller than max argument') {
+      @o.clamp(2..1)
+    }
+  end
+
   def test_err
     assert_raise(ArgumentError) { 1.0 < nil }
     assert_raise(ArgumentError) { 1.0 < Object.new }
-- 
cgit v0.10.2


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

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