mirror of
https://github.com/aNNiMON/Own-Programming-Language-Tutorial.git
synced 2024-09-20 00:34:20 +03:00
[functional] Add Stream.filterNot
This commit is contained in:
parent
fb6a8b8ea1
commit
110aa6264a
@ -0,0 +1,116 @@
|
|||||||
|
package com.annimon.ownlang.modules.functional;
|
||||||
|
|
||||||
|
import com.annimon.ownlang.exceptions.ArgumentsMismatchException;
|
||||||
|
import com.annimon.ownlang.lib.*;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
class StreamValue extends MapValue {
|
||||||
|
|
||||||
|
private final ArrayValue container;
|
||||||
|
|
||||||
|
public StreamValue(ArrayValue container) {
|
||||||
|
super(16);
|
||||||
|
this.container = container;
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
set("filter", wrapIntermediate(new functional_filter()));
|
||||||
|
set("filterNot", wrapIntermediate(new functional_filterNot()));
|
||||||
|
set("map", wrapIntermediate(new functional_map()));
|
||||||
|
set("flatMap", wrapIntermediate(new functional_flatmap()));
|
||||||
|
set("sorted", this::sorted);
|
||||||
|
set("sortBy", wrapIntermediate(new functional_sortby()));
|
||||||
|
set("takeWhile", wrapIntermediate(new functional_takeWhile()));
|
||||||
|
set("dropWhile", wrapIntermediate(new functional_dropwhile()));
|
||||||
|
set("peek", wrapIntermediate(new functional_foreach()));
|
||||||
|
set("skip", this::skip);
|
||||||
|
set("limit", this::limit);
|
||||||
|
set("custom", this::custom);
|
||||||
|
|
||||||
|
set("reduce", wrapTerminal(new functional_reduce()));
|
||||||
|
set("forEach", wrapTerminal(new functional_foreach()));
|
||||||
|
set("toArray", args -> container);
|
||||||
|
set("joining", container::joinToString);
|
||||||
|
set("count", args -> NumberValue.of(container.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Value skip(Value[] args) {
|
||||||
|
Arguments.check(1, args.length);
|
||||||
|
|
||||||
|
final int skipCount = args[0].asInt();
|
||||||
|
final int size = container.size();
|
||||||
|
|
||||||
|
if (skipCount <= 0) return this;
|
||||||
|
if (skipCount >= size) {
|
||||||
|
return new StreamValue(new ArrayValue(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
final Value[] result = new Value[size - skipCount];
|
||||||
|
System.arraycopy(container.getCopyElements(), skipCount, result, 0, result.length);
|
||||||
|
return new StreamValue(new ArrayValue(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Value limit(Value[] args) {
|
||||||
|
Arguments.check(1, args.length);
|
||||||
|
|
||||||
|
final int limitCount = args[0].asInt();
|
||||||
|
final int size = container.size();
|
||||||
|
|
||||||
|
if (limitCount >= size) return this;
|
||||||
|
if (limitCount <= 0) {
|
||||||
|
return new StreamValue(new ArrayValue(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
final Value[] result = new Value[limitCount];
|
||||||
|
System.arraycopy(container.getCopyElements(), 0, result, 0, limitCount);
|
||||||
|
return new StreamValue(new ArrayValue(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Value sorted(Value[] args) {
|
||||||
|
Arguments.checkOrOr(0, 1, args.length);
|
||||||
|
final Value[] elements = container.getCopyElements();
|
||||||
|
|
||||||
|
switch (args.length) {
|
||||||
|
case 0 -> Arrays.sort(elements);
|
||||||
|
case 1 -> {
|
||||||
|
final Function comparator = ValueUtils.consumeFunction(args[0], 0);
|
||||||
|
Arrays.sort(elements, (o1, o2) -> comparator.execute(o1, o2).asInt());
|
||||||
|
}
|
||||||
|
default -> throw new ArgumentsMismatchException("Wrong number of arguments");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new StreamValue(new ArrayValue(elements));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Value custom(Value[] args) {
|
||||||
|
Arguments.check(1, args.length);
|
||||||
|
final Function f = ValueUtils.consumeFunction(args[0], 0);
|
||||||
|
final Value result = f.execute(container);
|
||||||
|
if (result.type() == Types.ARRAY) {
|
||||||
|
return new StreamValue((ArrayValue) result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private FunctionValue wrapIntermediate(Function f) {
|
||||||
|
return wrap(f, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private FunctionValue wrapTerminal(Function f) {
|
||||||
|
return wrap(f, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private FunctionValue wrap(Function f, boolean intermediate) {
|
||||||
|
return new FunctionValue(args -> {
|
||||||
|
final Value[] newArgs = new Value[args.length + 1];
|
||||||
|
System.arraycopy(args, 0, newArgs, 1, args.length);
|
||||||
|
newArgs[0] = container;
|
||||||
|
final Value result = f.execute(newArgs);
|
||||||
|
if (intermediate && result.type() == Types.ARRAY) {
|
||||||
|
return new StreamValue((ArrayValue) result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,6 @@
|
|||||||
package com.annimon.ownlang.modules.functional;
|
package com.annimon.ownlang.modules.functional;
|
||||||
|
|
||||||
import com.annimon.ownlang.lib.Function;
|
import com.annimon.ownlang.lib.*;
|
||||||
import com.annimon.ownlang.lib.FunctionValue;
|
|
||||||
import com.annimon.ownlang.lib.Value;
|
|
||||||
import com.annimon.ownlang.modules.Module;
|
import com.annimon.ownlang.modules.Module;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -25,9 +23,9 @@ public final class functional implements Module {
|
|||||||
result.put("map", new functional_map());
|
result.put("map", new functional_map());
|
||||||
result.put("flatmap", new functional_flatmap());
|
result.put("flatmap", new functional_flatmap());
|
||||||
result.put("reduce", new functional_reduce());
|
result.put("reduce", new functional_reduce());
|
||||||
result.put("filter", new functional_filter(false));
|
result.put("filter", new functional_filter());
|
||||||
result.put("sortby", new functional_sortby());
|
result.put("sortby", new functional_sortby());
|
||||||
result.put("takewhile", new functional_filter(true));
|
result.put("takewhile", new functional_takeWhile());
|
||||||
result.put("dropwhile", new functional_dropwhile());
|
result.put("dropwhile", new functional_dropwhile());
|
||||||
|
|
||||||
result.put("chain", new functional_chain());
|
result.put("chain", new functional_chain());
|
||||||
|
@ -8,46 +8,41 @@ import java.util.Map;
|
|||||||
|
|
||||||
public final class functional_filter implements Function {
|
public final class functional_filter implements Function {
|
||||||
|
|
||||||
private final boolean takeWhile;
|
|
||||||
|
|
||||||
public functional_filter(boolean takeWhile) {
|
|
||||||
this.takeWhile = takeWhile;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Value execute(Value[] args) {
|
public Value execute(Value[] args) {
|
||||||
Arguments.check(2, args.length);
|
Arguments.check(2, args.length);
|
||||||
final Value container = args[0];
|
final Value container = args[0];
|
||||||
final Function predicate = ValueUtils.consumeFunction(args[1], 1);
|
final Function predicate = ValueUtils.consumeFunction(args[1], 1);
|
||||||
|
return filter(container, predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Value filter(Value container, Function predicate) {
|
||||||
if (container.type() == Types.ARRAY) {
|
if (container.type() == Types.ARRAY) {
|
||||||
return filterArray((ArrayValue) container, predicate, takeWhile);
|
return filterArray((ArrayValue) container, predicate);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (container.type() == Types.MAP) {
|
if (container.type() == Types.MAP) {
|
||||||
return filterMap((MapValue) container, predicate, takeWhile);
|
return filterMap((MapValue) container, predicate);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new TypeException("Invalid first argument. Array or map expected");
|
throw new TypeException("Invalid first argument. Array or map expected");
|
||||||
}
|
}
|
||||||
|
|
||||||
private Value filterArray(ArrayValue array, Function predicate, boolean takeWhile) {
|
static ArrayValue filterArray(ArrayValue array, Function predicate) {
|
||||||
final int size = array.size();
|
final int size = array.size();
|
||||||
final List<Value> values = new ArrayList<>(size);
|
final List<Value> values = new ArrayList<>(size);
|
||||||
for (Value value : array) {
|
for (Value value : array) {
|
||||||
if (predicate.execute(value) != NumberValue.ZERO) {
|
if (predicate.execute(value) != NumberValue.ZERO) {
|
||||||
values.add(value);
|
values.add(value);
|
||||||
} else if (takeWhile) break;
|
|
||||||
}
|
}
|
||||||
final int newSize = values.size();
|
}
|
||||||
return new ArrayValue(values.toArray(new Value[newSize]));
|
return new ArrayValue(values);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Value filterMap(MapValue map, Function predicate, boolean takeWhile) {
|
static MapValue filterMap(MapValue map, Function predicate) {
|
||||||
final MapValue result = new MapValue(map.size());
|
final MapValue result = new MapValue(map.size());
|
||||||
for (Map.Entry<Value, Value> element : map) {
|
for (Map.Entry<Value, Value> element : map) {
|
||||||
if (predicate.execute(element.getKey(), element.getValue()) != NumberValue.ZERO) {
|
if (predicate.execute(element.getKey(), element.getValue()) != NumberValue.ZERO) {
|
||||||
result.set(element.getKey(), element.getValue());
|
result.set(element.getKey(), element.getValue());
|
||||||
} else if (takeWhile) break;
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.annimon.ownlang.modules.functional;
|
||||||
|
|
||||||
|
import com.annimon.ownlang.lib.*;
|
||||||
|
|
||||||
|
public final class functional_filterNot implements Function {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Value execute(Value[] args) {
|
||||||
|
Arguments.check(2, args.length);
|
||||||
|
final Value container = args[0];
|
||||||
|
final Function predicate = ValueUtils.consumeFunction(args[1], 1);
|
||||||
|
return functional_filter.filter(container, negate(predicate));
|
||||||
|
}
|
||||||
|
|
||||||
|
static Function negate(Function f) {
|
||||||
|
return args -> NumberValue.fromBoolean(f.execute(args) == NumberValue.ZERO);
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,7 @@
|
|||||||
package com.annimon.ownlang.modules.functional;
|
package com.annimon.ownlang.modules.functional;
|
||||||
|
|
||||||
import com.annimon.ownlang.exceptions.ArgumentsMismatchException;
|
|
||||||
import com.annimon.ownlang.exceptions.TypeException;
|
import com.annimon.ownlang.exceptions.TypeException;
|
||||||
import com.annimon.ownlang.lib.*;
|
import com.annimon.ownlang.lib.*;
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
public final class functional_stream implements Function {
|
public final class functional_stream implements Function {
|
||||||
|
|
||||||
@ -25,117 +23,4 @@ public final class functional_stream implements Function {
|
|||||||
throw new TypeException("Invalid argument. Array or map expected");
|
throw new TypeException("Invalid argument. Array or map expected");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class StreamValue extends MapValue {
|
|
||||||
|
|
||||||
private final ArrayValue container;
|
|
||||||
|
|
||||||
public StreamValue(ArrayValue container) {
|
|
||||||
super(16);
|
|
||||||
this.container = container;
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init() {
|
|
||||||
set("filter", wrapIntermediate(new functional_filter(false)));
|
|
||||||
set("map", wrapIntermediate(new functional_map()));
|
|
||||||
set("flatMap", wrapIntermediate(new functional_flatmap()));
|
|
||||||
set("sorted", this::sorted);
|
|
||||||
set("sortBy", wrapIntermediate(new functional_sortby()));
|
|
||||||
set("takeWhile", wrapIntermediate(new functional_filter(true)));
|
|
||||||
set("dropWhile", wrapIntermediate(new functional_dropwhile()));
|
|
||||||
set("peek", wrapIntermediate(new functional_foreach()));
|
|
||||||
set("skip", this::skip);
|
|
||||||
set("limit", this::limit);
|
|
||||||
set("custom", this::custom);
|
|
||||||
|
|
||||||
set("reduce", wrapTerminal(new functional_reduce()));
|
|
||||||
set("forEach", wrapTerminal(new functional_foreach()));
|
|
||||||
set("toArray", args -> container);
|
|
||||||
set("joining", container::joinToString);
|
|
||||||
set("count", args -> NumberValue.of(container.size()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Value skip(Value[] args) {
|
|
||||||
Arguments.check(1, args.length);
|
|
||||||
|
|
||||||
final int skipCount = args[0].asInt();
|
|
||||||
final int size = container.size();
|
|
||||||
|
|
||||||
if (skipCount <= 0) return this;
|
|
||||||
if (skipCount >= size) {
|
|
||||||
return new StreamValue(new ArrayValue(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
final Value[] result = new Value[size - skipCount];
|
|
||||||
System.arraycopy(container.getCopyElements(), skipCount, result, 0, result.length);
|
|
||||||
return new StreamValue(new ArrayValue(result));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Value limit(Value[] args) {
|
|
||||||
Arguments.check(1, args.length);
|
|
||||||
|
|
||||||
final int limitCount = args[0].asInt();
|
|
||||||
final int size = container.size();
|
|
||||||
|
|
||||||
if (limitCount >= size) return this;
|
|
||||||
if (limitCount <= 0) {
|
|
||||||
return new StreamValue(new ArrayValue(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
final Value[] result = new Value[limitCount];
|
|
||||||
System.arraycopy(container.getCopyElements(), 0, result, 0, limitCount);
|
|
||||||
return new StreamValue(new ArrayValue(result));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Value sorted(Value[] args) {
|
|
||||||
Arguments.checkOrOr(0, 1, args.length);
|
|
||||||
final Value[] elements = container.getCopyElements();
|
|
||||||
|
|
||||||
switch (args.length) {
|
|
||||||
case 0:
|
|
||||||
Arrays.sort(elements);
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
final Function comparator = ValueUtils.consumeFunction(args[0], 0);
|
|
||||||
Arrays.sort(elements, (o1, o2) -> comparator.execute(o1, o2).asInt());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new ArgumentsMismatchException("Wrong number of arguments");
|
|
||||||
}
|
|
||||||
|
|
||||||
return new StreamValue(new ArrayValue(elements));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Value custom(Value[] args) {
|
|
||||||
Arguments.check(1, args.length);
|
|
||||||
final Function f = ValueUtils.consumeFunction(args[0], 0);
|
|
||||||
final Value result = f.execute(container);
|
|
||||||
if (result.type() == Types.ARRAY) {
|
|
||||||
return new StreamValue((ArrayValue) result);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private FunctionValue wrapIntermediate(Function f) {
|
|
||||||
return wrap(f, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private FunctionValue wrapTerminal(Function f) {
|
|
||||||
return wrap(f, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private FunctionValue wrap(Function f, boolean intermediate) {
|
|
||||||
return new FunctionValue(args -> {
|
|
||||||
final Value[] newArgs = new Value[args.length + 1];
|
|
||||||
System.arraycopy(args, 0, newArgs, 1, args.length);
|
|
||||||
newArgs[0] = container;
|
|
||||||
final Value result = f.execute(newArgs);
|
|
||||||
if (intermediate && result.type() == Types.ARRAY) {
|
|
||||||
return new StreamValue((ArrayValue) result);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
package com.annimon.ownlang.modules.functional;
|
||||||
|
|
||||||
|
import com.annimon.ownlang.exceptions.TypeException;
|
||||||
|
import com.annimon.ownlang.lib.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public final class functional_takeWhile implements Function {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Value execute(Value[] args) {
|
||||||
|
Arguments.check(2, args.length);
|
||||||
|
final Value container = args[0];
|
||||||
|
final Function predicate = ValueUtils.consumeFunction(args[1], 1);
|
||||||
|
return takeWhile(container, predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Value takeWhile(Value container, Function predicate) {
|
||||||
|
if (container.type() == Types.ARRAY) {
|
||||||
|
return takeWhileArray((ArrayValue) container, predicate);
|
||||||
|
}
|
||||||
|
if (container.type() == Types.MAP) {
|
||||||
|
return takeWhileMap((MapValue) container, predicate);
|
||||||
|
}
|
||||||
|
throw new TypeException("Invalid first argument. Array or map expected");
|
||||||
|
}
|
||||||
|
|
||||||
|
static ArrayValue takeWhileArray(ArrayValue array, Function predicate) {
|
||||||
|
final int size = array.size();
|
||||||
|
final List<Value> values = new ArrayList<>(size);
|
||||||
|
for (Value value : array) {
|
||||||
|
if (predicate.execute(value) != NumberValue.ZERO) {
|
||||||
|
values.add(value);
|
||||||
|
} else break;
|
||||||
|
}
|
||||||
|
return new ArrayValue(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
static MapValue takeWhileMap(MapValue map, Function predicate) {
|
||||||
|
final MapValue result = new MapValue(map.size());
|
||||||
|
for (Map.Entry<Value, Value> element : map) {
|
||||||
|
if (predicate.execute(element.getKey(), element.getValue()) != NumberValue.ZERO) {
|
||||||
|
result.set(element.getKey(), element.getValue());
|
||||||
|
} else break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
@ -10,6 +10,16 @@ def testStream() {
|
|||||||
assertEquals([8,6,4,2], result)
|
assertEquals([8,6,4,2], result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def testFilter() {
|
||||||
|
data = [1,2,3,4,5,6,7]
|
||||||
|
assertEquals([2, 4, 6], stream(data).filter(def(x) = x % 2 == 0).toArray())
|
||||||
|
}
|
||||||
|
|
||||||
|
def testFilterNot() {
|
||||||
|
data = [1,2,3,4,5,6,7]
|
||||||
|
assertEquals([1, 3, 5, 7], stream(data).filterNot(def(x) = x % 2 == 0).toArray())
|
||||||
|
}
|
||||||
|
|
||||||
def testSkip() {
|
def testSkip() {
|
||||||
data = [1,2,3,4,5,6,7]
|
data = [1,2,3,4,5,6,7]
|
||||||
assertEquals(7, stream(data).skip(0).count())
|
assertEquals(7, stream(data).skip(0).count())
|
||||||
|
Loading…
Reference in New Issue
Block a user