From 2de94d94d5c194a245ed7bb526660d0e4adeeb46 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Thu, 5 Oct 2023 16:31:46 +0300 Subject: [PATCH] [functional] Unify functional operators --- .../modules/functional/StreamValue.java | 8 +-- .../modules/functional/functional.java | 6 +- ...opwhile.java => functional_dropWhile.java} | 16 +++-- .../functional/functional_flatmap.java | 15 +++-- .../functional/functional_forEach.java | 60 +++++++++++++++++++ .../functional/functional_foreach.java | 55 ----------------- .../modules/functional/functional_map.java | 4 +- .../modules/functional/functional_reduce.java | 34 +++++++---- ...nal_sortby.java => functional_sortBy.java} | 2 +- 9 files changed, 112 insertions(+), 88 deletions(-) rename modules/main/src/main/java/com/annimon/ownlang/modules/functional/{functional_dropwhile.java => functional_dropWhile.java} (79%) create mode 100644 modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEach.java delete mode 100644 modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java rename modules/main/src/main/java/com/annimon/ownlang/modules/functional/{functional_sortby.java => functional_sortBy.java} (93%) diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/StreamValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/StreamValue.java index 90d0abc..e4ce962 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/StreamValue.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/StreamValue.java @@ -20,16 +20,16 @@ class StreamValue extends MapValue { set("map", wrapIntermediate(new functional_map())); set("flatMap", wrapIntermediate(new functional_flatmap())); set("sorted", this::sorted); - set("sortBy", wrapIntermediate(new functional_sortby())); + 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("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("forEach", wrapTerminal(new functional_forEach())); set("toArray", args -> container); set("joining", container::joinToString); set("count", args -> NumberValue.of(container.size())); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional.java index 66c0dcc..22c7ffa 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional.java @@ -19,14 +19,14 @@ public final class functional implements Module { @Override public Map functions() { final var result = new HashMap(15); - result.put("foreach", new functional_foreach()); + result.put("foreach", new functional_forEach()); result.put("map", new functional_map()); result.put("flatmap", new functional_flatmap()); result.put("reduce", new functional_reduce()); result.put("filter", new functional_filter()); - result.put("sortby", new functional_sortby()); + result.put("sortby", new functional_sortBy()); 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("stream", new functional_stream()); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_dropwhile.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_dropWhile.java similarity index 79% rename from modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_dropwhile.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_dropWhile.java index 5817f71..11cbff3 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_dropwhile.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_dropWhile.java @@ -9,20 +9,24 @@ import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.lib.ValueUtils; -public final class functional_dropwhile implements Function { +public final class functional_dropWhile implements Function { @Override public Value execute(Value[] args) { Arguments.check(2, args.length); - if (args[0].type() != Types.ARRAY) { - throw new TypeException("Array expected in first argument"); - } final Value container = args[0]; final Function predicate = ValueUtils.consumeFunction(args[1], 1); - return dropWhileArray((ArrayValue) container, predicate); + return dropWhile(container, predicate); } - private Value dropWhileArray(ArrayValue array, Function predicate) { + static ArrayValue dropWhile(Value container, Function predicate) { + if (container.type() != Types.ARRAY) { + throw new TypeException("Array expected in first argument"); + } + return dropWhileArray((ArrayValue) container, predicate); + } + + static ArrayValue dropWhileArray(ArrayValue array, Function predicate) { int skipCount = 0; for (Value value : array) { if (predicate.execute(value) != NumberValue.ZERO) diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_flatmap.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_flatmap.java index 868b0c8..775be2b 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_flatmap.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_flatmap.java @@ -15,14 +15,19 @@ public final class functional_flatmap implements Function { @Override public Value execute(Value[] args) { Arguments.check(2, args.length); - if (args[0].type() != Types.ARRAY) { + final Value container = args[0]; + final Function mapper = ValueUtils.consumeFunction(args[1], 1); + return flatMap(container, mapper); + } + + static Value flatMap(Value container, Function mapper) { + if (container.type() != Types.ARRAY) { throw new TypeException("Array expected in first argument"); } - final Function mapper = ValueUtils.consumeFunction(args[1], 1); - return flatMapArray((ArrayValue) args[0], mapper); + return flatMapArray((ArrayValue) container, mapper); } - - private Value flatMapArray(ArrayValue array, Function mapper) { + + static Value flatMapArray(ArrayValue array, Function mapper) { final List values = new ArrayList<>(); final int size = array.size(); for (int i = 0; i < size; i++) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEach.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEach.java new file mode 100644 index 0000000..ff4175b --- /dev/null +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEach.java @@ -0,0 +1,60 @@ +package com.annimon.ownlang.modules.functional; + +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.*; +import java.util.Map; + +public final class functional_forEach implements Function { + + @Override + public Value execute(Value[] args) { + Arguments.check(2, args.length); + final Value container = args[0]; + final Function consumer = ValueUtils.consumeFunction(args[1], 1); + return forEach(container, consumer); + } + + static Value forEach(Value container, Function consumer) { + final int argsCount = consumer.getArgsCount(); + return switch (container.type()) { + case Types.STRING -> forEachString((StringValue) container, argsCount, consumer); + case Types.ARRAY -> forEachArray((ArrayValue) container, argsCount, consumer); + case Types.MAP -> forEachMap((MapValue) container, consumer); + default -> throw new TypeException("Cannot iterate " + Types.typeToString(container.type())); + }; + } + + static StringValue forEachString(StringValue string, int argsCount, Function consumer) { + if (argsCount == 2) { + for (char ch : string.asString().toCharArray()) { + consumer.execute(new StringValue(String.valueOf(ch)), NumberValue.of(ch)); + } + } else { + for (char ch : string.asString().toCharArray()) { + consumer.execute(new StringValue(String.valueOf(ch))); + } + } + return string; + } + + static ArrayValue forEachArray(ArrayValue array, int argsCount, Function consumer) { + if (argsCount == 2) { + int index = 0; + for (Value element : array) { + consumer.execute(element, NumberValue.of(index++)); + } + } else { + for (Value element : array) { + consumer.execute(element); + } + } + return array; + } + + static MapValue forEachMap(MapValue map, Function consumer) { + for (Map.Entry element : map) { + consumer.execute(element.getKey(), element.getValue()); + } + return map; + } +} diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java deleted file mode 100644 index f22aec4..0000000 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.annimon.ownlang.modules.functional; - -import com.annimon.ownlang.exceptions.TypeException; -import com.annimon.ownlang.lib.*; -import java.util.Map; - -public final class functional_foreach implements Function { - - @Override - public Value execute(Value[] args) { - Arguments.check(2, args.length); - final Value container = args[0]; - final Function consumer = ValueUtils.consumeFunction(args[1], 1); - final int argsCount = consumer.getArgsCount(); - - switch (container.type()) { - case Types.STRING: - final StringValue string = (StringValue) container; - if (argsCount == 2) { - for (char ch : string.asString().toCharArray()) { - consumer.execute(new StringValue(String.valueOf(ch)), NumberValue.of(ch)); - } - } else { - for (char ch : string.asString().toCharArray()) { - consumer.execute(new StringValue(String.valueOf(ch))); - } - } - return string; - - case Types.ARRAY: - final ArrayValue array = (ArrayValue) container; - if (argsCount == 2) { - int index = 0; - for (Value element : array) { - consumer.execute(element, NumberValue.of(index++)); - } - } else { - for (Value element : array) { - consumer.execute(element); - } - } - return array; - - case Types.MAP: - final MapValue map = (MapValue) container; - for (Map.Entry element : map) { - consumer.execute(element.getKey(), element.getValue()); - } - return map; - - default: - throw new TypeException("Cannot iterate " + Types.typeToString(container.type())); - } - } -} diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_map.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_map.java index 8219818..5ca801c 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_map.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_map.java @@ -31,7 +31,7 @@ public final class functional_map implements Function { throw new TypeException("Invalid first argument. Array or map expected"); } - private Value mapArray(ArrayValue array, Function mapper) { + static ArrayValue mapArray(ArrayValue array, Function mapper) { final int size = array.size(); final ArrayValue result = new ArrayValue(size); for (int i = 0; i < size; i++) { @@ -40,7 +40,7 @@ public final class functional_map implements Function { return result; } - private Value mapMap(MapValue map, Function keyMapper, Function valueMapper) { + static MapValue mapMap(MapValue map, Function keyMapper, Function valueMapper) { final MapValue result = new MapValue(map.size()); for (Map.Entry element : map) { final Value newKey = keyMapper.execute(element.getKey()); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_reduce.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_reduce.java index 005e686..cc03dfd 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_reduce.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_reduce.java @@ -19,22 +19,32 @@ public final class functional_reduce implements Function { final Value container = args[0]; final Value identity = args[1]; final Function accumulator = ValueUtils.consumeFunction(args[2], 2); + return reduce(container, identity, accumulator); + } + + static Value reduce(Value container, Value identity, Function accumulator) { if (container.type() == Types.ARRAY) { - Value result = identity; - final ArrayValue array = (ArrayValue) container; - for (Value element : array) { - result = accumulator.execute(result, element); - } - return result; + return reduceArray(identity, (ArrayValue) container, accumulator); } if (container.type() == Types.MAP) { - Value result = identity; - final MapValue map = (MapValue) container; - for (Map.Entry element : map) { - result = accumulator.execute(result, element.getKey(), element.getValue()); - } - return result; + return reduceMap(identity, (MapValue) container, accumulator); } throw new TypeException("Invalid first argument. Array or map expected"); } + + static Value reduceArray(Value identity, ArrayValue array, Function accumulator) { + Value result = identity; + for (Value element : array) { + result = accumulator.execute(result, element); + } + return result; + } + + static Value reduceMap(Value identity, MapValue map, Function accumulator) { + Value result = identity; + for (Map.Entry element : map) { + result = accumulator.execute(result, element.getKey(), element.getValue()); + } + return result; + } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_sortby.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_sortBy.java similarity index 93% rename from modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_sortby.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_sortBy.java index baa0532..a395ee9 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_sortby.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_sortBy.java @@ -10,7 +10,7 @@ import com.annimon.ownlang.lib.ValueUtils; import java.util.Arrays; import java.util.Comparator; -public final class functional_sortby implements Function { +public final class functional_sortBy implements Function { @Override public Value execute(Value[] args) {