Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: ef5241481cd7
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 8dd46629685f
Choose a head ref
  • 2 commits
  • 2 files changed
  • 2 contributors

Commits on Nov 5, 2014

  1. Copy the full SHA
    2adc2b1 View commit details

Commits on Nov 6, 2014

  1. Merge pull request #2113 from cheald/cleanup_min_max

    Improve Enumerable/Range min/max code by taking advantage of arity splitting.
    headius committed Nov 6, 2014
    Copy the full SHA
    8dd4662 View commit details
Showing with 84 additions and 91 deletions.
  1. +40 −41 core/src/main/java/org/jruby/RubyEnumerable.java
  2. +44 −50 core/src/main/java/org/jruby/RubyRange.java
81 changes: 40 additions & 41 deletions core/src/main/java/org/jruby/RubyEnumerable.java
Original file line number Diff line number Diff line change
@@ -1123,29 +1123,56 @@ public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
return runtime.getFalse();
}

@JRubyMethod(optional = 1)
public static IRubyObject max(ThreadContext context, IRubyObject self, IRubyObject[] args, final Block block) {
return getExtent(context, self, args, "max", SORT_MAX, block);
@JRubyMethod
public static IRubyObject max(ThreadContext context, IRubyObject self, final Block block) {
return singleExtent(context, self, "max", SORT_MAX, block);
}

@JRubyMethod
public static IRubyObject max(ThreadContext context, IRubyObject self, IRubyObject arg, final Block block) {
// TODO: Replace with an implementation (quickselect, etc) which requires O(k) memory rather than O(n) memory
RubyArray sorted = (RubyArray)sort(context, self, block);
return ((RubyArray) sorted.last(arg)).reverse();
}

@JRubyMethod(optional = 1)
public static IRubyObject min(ThreadContext context, IRubyObject self, IRubyObject[] args, final Block block) {
return getExtent(context, self, args, "min", SORT_MIN, block);
@JRubyMethod
public static IRubyObject min(ThreadContext context, IRubyObject self, final Block block) {
return singleExtent(context, self, "min", SORT_MIN, block);
}

@JRubyMethod(optional = 1)
public static IRubyObject max_by(ThreadContext context, IRubyObject self, IRubyObject[] args, final Block block) {
return getExtentBy(context, self, args, "max_by", SORT_MAX, block);
@JRubyMethod
public static IRubyObject min(ThreadContext context, IRubyObject self, IRubyObject arg, final Block block) {
// TODO: Replace with an implementation (quickselect, etc) which requires O(k) memory rather than O(n) memory
RubyArray sorted = (RubyArray)sort(context, self, block);
return sorted.first(arg);
}

@JRubyMethod(optional = 1)
public static IRubyObject min_by(ThreadContext context, IRubyObject self, IRubyObject[] args, final Block block) {
return getExtentBy(context, self, args, "min_by", SORT_MIN, block);
@JRubyMethod
public static IRubyObject max_by(ThreadContext context, IRubyObject self, final Block block) {
return singleExtentBy(context, self, "max", SORT_MAX, block);
}

@JRubyMethod
public static IRubyObject max_by(ThreadContext context, IRubyObject self, IRubyObject arg, final Block block) {
// TODO: Replace with an implementation (quickselect, etc) which requires O(k) memory rather than O(n) memory
RubyArray sorted = (RubyArray)sort_by(context, self, block);
return ((RubyArray) sorted.last(arg)).reverse();
}

@JRubyMethod
public static IRubyObject min_by(ThreadContext context, IRubyObject self, final Block block) {
return singleExtentBy(context, self, "min", SORT_MIN, block);
}

@JRubyMethod
public static IRubyObject min_by(ThreadContext context, IRubyObject self, IRubyObject arg, final Block block) {
// TODO: Replace with an implementation (quickselect, etc) which requires O(k) memory rather than O(n) memory
RubyArray sorted = (RubyArray)sort_by(context, self, block);
return sorted.first(arg);
}

private static final int SORT_MAX = 1;
private static final int SORT_MIN = -1;

private static IRubyObject singleExtent(ThreadContext context, IRubyObject self, final String op, final int sortDirection, final Block block) {
final Ruby runtime = context.runtime;
final IRubyObject result[] = new IRubyObject[] { null };
@@ -1170,20 +1197,6 @@ public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
return result[0] == null ? runtime.getNil() : result[0];
}

private static IRubyObject getExtent(ThreadContext context, IRubyObject self, IRubyObject[] args, String op, int sortDirection, final Block block) {
if (args.length == 0) {
return singleExtent(context, self, op, sortDirection, block);
} else {
// TODO: Replace with an implementation (quickselect, etc) which requires O(k) memory rather than O(n) memory
RubyArray sorted = (RubyArray)sort(context, self, block);
if(sortDirection == SORT_MAX) {
return ((RubyArray) sorted.last(args[0])).reverse();
} else {
return sorted.first(args[0]);
}
}
}

private static IRubyObject singleExtentBy(ThreadContext context, IRubyObject self, final String op, final int sortDirection, final Block block) {
final Ruby runtime = context.runtime;

@@ -1208,20 +1221,6 @@ public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
return result[0];
}

private static IRubyObject getExtentBy(ThreadContext context, IRubyObject self, IRubyObject[] args, String op, int dir, Block block) {
if(args.length == 0) {
return singleExtentBy(context, self, op, dir, block);
} else {
// TODO: Replace with an implementation (quickselect, etc) which requires O(k) memory rather than O(n) memory
RubyArray sorted = (RubyArray)sort_by(context, self, block);
if(dir == SORT_MAX) {
return ((RubyArray) sorted.last(args[0])).reverse();
} else {
return sorted.first(args[0]);
}
}
}

@JRubyMethod
public static IRubyObject minmax(ThreadContext context, IRubyObject self, final Block block) {
final Ruby runtime = context.runtime;
94 changes: 44 additions & 50 deletions core/src/main/java/org/jruby/RubyRange.java
Original file line number Diff line number Diff line change
@@ -629,68 +629,62 @@ public IRubyObject cover_p(ThreadContext context, IRubyObject obj) {
rangeLt(context, obj, end) != null : rangeLe(context, obj, end) != null);
}

@JRubyMethod(frame = true, optional = 1)
public IRubyObject min(ThreadContext context, IRubyObject[] args, Block block) {
RubyObject receiver;
@JRubyMethod(frame = true)
public IRubyObject min(ThreadContext context, Block block) {
IRubyObject receiver = getReceiverForMinMax(context, end, block);
if(receiver.isNil()) return receiver;
return Helpers.invokeSuper(context, receiver, block);
}

if (block.isGiven()) {
receiver = this;
} else {
int c = RubyComparable.cmpint(context, invokedynamic(context, begin, MethodNames.OP_CMP, end), begin, end);
boolean emptyRange = c > 0 || (c == 0 && isExclusive);
boolean wantsArray = args.length > 0;
if(emptyRange) {
if(wantsArray) {
return RubyArray.newArray(context.runtime, 0);
} else {
return context.runtime.getNil();
}
} else if (wantsArray) {
// Shortcut - just take the first N elements. No need to sort, as ranges are implicitly sorted.
return first(context, args[0]);
} else {
receiver = RubyArray.newArray(context.runtime, new IRubyObject[] {begin, end});
@JRubyMethod(frame = true)
public IRubyObject max(ThreadContext context, Block block) {
IRubyObject rangeEnd;
if (isExclusive) {
if (!(end instanceof RubyInteger)) {
throw context.runtime.newTypeError("cannot exclude non Integer end value");
}
if (!(begin instanceof RubyInteger)) {
throw context.runtime.newTypeError("cannot exclude end value with non Integer begin value");
}
rangeEnd = RubyFixnum.newFixnum(context.runtime, ((RubyFixnum) end).getLongValue() - 1);
} else {
rangeEnd = end;
}

return Helpers.invokeSuper(context, receiver, args, block);
IRubyObject receiver = getReceiverForMinMax(context, rangeEnd, block);
if(receiver.isNil()) return receiver;
return Helpers.invokeSuper(context, receiver, block);
}

@JRubyMethod(frame = true)
public IRubyObject min(ThreadContext context, IRubyObject arg, Block block) {
if (block.isGiven()) return Helpers.invokeSuper(context, this, block);

return first(context, arg);
}

@JRubyMethod(frame = true, optional = 1)
public IRubyObject max(ThreadContext context, IRubyObject[] args, Block block) {
@JRubyMethod(frame = true)
public IRubyObject max(ThreadContext context, IRubyObject arg, Block block) {
if (block.isGiven()) return Helpers.invokeSuper(context, this, block);

return ((RubyArray) last(context, arg)).reverse();
}

private boolean rangeEmpty_p(ThreadContext context) {
int cmp = RubyComparable.cmpint(context, invokedynamic(context, begin, MethodNames.OP_CMP, end), begin, end);
return cmp > 0 || (cmp == 0 && isExclusive);
}

private IRubyObject getReceiverForMinMax(ThreadContext context, IRubyObject rangeEnd, Block block) {
RubyObject receiver;

if (block.isGiven()) {
receiver = this;
} else {
IRubyObject rangeEnd = end;
if (isExclusive) {
if (!(end instanceof RubyInteger)) {
throw context.runtime.newTypeError("cannot exclude non Integer end value");
}
if (!(begin instanceof RubyInteger)) {
throw context.runtime.newTypeError("cannot exclude end value with non Integer begin value");
}
rangeEnd = RubyFixnum.newFixnum(context.runtime, ((RubyFixnum) end).getLongValue() - 1);
}

boolean emptyRange = RubyComparable.cmpint(context, invokedynamic(context, begin, MethodNames.OP_CMP, rangeEnd), begin, rangeEnd) > 0;
boolean wantsArray = args.length > 0;
if(emptyRange) {
if(wantsArray) {
return RubyArray.newArray(context.runtime, 0);
} else {
return context.runtime.getNil();
}
} else if (wantsArray) {
// Shortcut - just take the first N elements and reverse them. No need to sort, as ranges are implicitly sorted.
return ((RubyArray) last(context, args[0])).reverse();
} else {
receiver = RubyArray.newArray(context.runtime, new IRubyObject[]{begin, rangeEnd});
}
if(rangeEmpty_p(context)) return context.runtime.getNil();
receiver = RubyArray.newArray(context.runtime, new IRubyObject[]{begin, rangeEnd});
}

return Helpers.invokeSuper(context, receiver, args, block);
return receiver;
}

@JRubyMethod